Skip to content

Derive bump crate sets once in the bump context (#97) - #121

Merged
leynos merged 21 commits into
mainfrom
issue-97-bump-context-single-derivation
Jul 8, 2026
Merged

Derive bump crate sets once in the bump context (#97)#121
leynos merged 21 commits into
mainfrom
issue-97-bump-context-single-derivation

Conversation

@leynos

@leynos leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #97

  • _update_crate_manifest now consumes the excluded and updated_crate_names sets from _BumpContext instead of re-deriving both from options for every crate, removing the O(n²) workspace-member iteration.
  • The previously dead _BumpContext.excluded field is now used.
  • _validate_bump_options is removed — its only caller performed the redundant re-extraction.
  • The single-derivation rule is documented in docs/developers-guide.md, and the bump.py module docstring now orients contributors on run as the orchestrator, the BumpOptions/BumpChanges records, the single-source-of-truth crate-set rule, and delegation to bump_docs, bump_lockfiles, bump_readme, and bump_toml.

Testing

  • Hypothesis property test asserts the context crate sets equal a naive reference computation across generated workspaces and exclude sets (behaviour parity through the refactor).
  • A second Hypothesis property test, test_manifest_selection_matches_naive_reference, drives _apply_crate_manifest_update against real temporary per-crate manifests: it asserts was_skipped/was_updated and the on-disk package and dependency versions against a naive reference. Every drawn example forces at least one excluded crate and at least one dependency on an updated crate, so the test fails if the manifest pass stops consuming context.excluded or context.updated_crate_names (verified against both mutations).
  • Existing bump result-message snapshots unchanged.
  • make check-fmt, make lint, make typecheck, and make test (675 passed) all green.

References

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

This PR centralises bump-time crate-set derivation in _initialize_bump_context, so downstream manifest updates consume the precomputed excluded and updated_crate_names sets rather than re-deriving them per crate. This removes redundant workspace-member iteration, wires in the previously unused _BumpContext.excluded field, and eliminates the now-redundant _validate_bump_options flow.

It refactors manifest bumping around a closed per-crate outcome enum (_CrateManifestOutcome) and reworks the per-crate processing to use _apply_crate_manifest_update, which selects package selectors, computes dependency-section targets from context.updated_crate_names, skips/updates appropriately, and emits structured debug logs (context initialisation counts, per-crate decision totals, and README transposition). Dependency-section rewriting is tightened to accept Set[str] selectors directly and includes small efficiency/behavioural improvements (e.g., early-return when the updated set is empty).

Documentation was updated: docs/developers-guide.md clarifies that Hypothesis-backed tests are used across bump/lockfile/publish/workspace-utility suites (and fixes a spelling), and the bump.py module docstring was expanded to describe run() as the orchestrator, BumpOptions/BumpChanges, and the “derive crate sets exactly once” rule.

Test coverage was expanded and reorganised: Hypothesis property tests validate crate-set derivation and manifest-selection parity against a naive reference model, with existing bump output snapshots remaining unchanged. Additional focused unit tests cover bump context properties, manifest writing, selector logic, TOML/version helper utilities, and bump output formatting; the prior monolithic bump internals test module was removed in favour of these smaller responsibility-focused suites.

Walkthrough

Centralise crate-set derivation in _initialize_bump_context, thread it through manifest updates, adjust dependency-section selection and logging, and extend docs plus helper, property, and output-format tests around the new flow.

Changes

Centralised crate-set threading

Layer / File(s) Summary
Docs and bump orchestration
docs/developers-guide.md, lading/commands/bump.py
Updates bump guidance, expands module-level documentation, adds bump-context and completion logging, introduces _CrateManifestOutcome, refactors crate-manifest processing, and records README transposition counts.
Manifest update logic
lading/commands/bump.py
Replaces per-crate validation and update flow with _apply_crate_manifest_update, and changes dependency-section helpers to use precomputed updated-crate sets directly.
Helper and property tests
tests/unit/test_bump_manifest_writing.py, tests/unit/test_bump_toml_helpers.py, tests/unit/test_bump_selectors.py, tests/unit/test_bump_context_properties.py
Covers manifest rewriting, TOML table helpers, selector/skip logic, and Hypothesis-based parity checks for bump context and manifest selection.
Result and output formatting tests
tests/unit/test_bump_result_formatting.py, tests/unit/test_bump_output_formatting.py
Updates crate-manifest update coverage to use bump context, and adds message-formatting coverage for change counting, path rendering, grammar, and snapshot cases.

Sequence Diagram(s)

sequenceDiagram
  participant Init as _initialize_bump_context
  participant Process as _process_crate_manifests
  participant Apply as _apply_crate_manifest_update
  participant Write as _update_manifest
  Init->>Process: Provide _BumpContext
  loop For each workspace crate
    Process->>Apply: Pass context and crate name
    Apply->>Apply: Derive selectors and dependency targets from context
    Apply->>Write: Rewrite manifest when needed
    Apply-->>Process: Return _CrateManifestOutcome
  end
Loading

Possibly related PRs

  • leynos/crate-tools#14: Connects to the same bump helper flow in lading/commands/bump.py, including _initialize_bump_context and per-crate update logic.
  • leynos/lading#55: Also changes dependency-section rewriting for workspace.* manifest tables.
  • leynos/lading#114: Also touches _workspace_dependency_sections and _dependency_sections_for_crate.

Suggested reviewers: codescene-delta-analysis

Poem

A crate set wakes and walks the line,
Then threads through bumps in tidy time.
TOML hums soft, and tests hold fast,
While logs record each change that passed.
✨📦

🚥 Pre-merge checks | ✅ 20
✅ Passed checks (20 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the bump-context refactor and includes the linked issue reference (#97).
Description check ✅ Passed The description clearly describes the bump-context refactor and the new tests.
Linked Issues check ✅ Passed The refactor, docs update, and Hypothesis parity tests satisfy issue #97's requirements.
Out of Scope Changes check ✅ Passed The added test splits and doc tweaks stay within the bump refactor's scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Testing (Overall) ✅ Passed New Hypothesis tests and direct manifest/formatting assertions exercise the refactor paths and fail on wrong crate-set, rewrite, or output behaviour.
User-Facing Documentation ✅ Passed PASS: the diff is internal refactoring plus developer-guide/test updates; no new user-facing bump behaviour is introduced, and the user guide already covers lading bump.
Developer Documentation ✅ Passed docs/developers-guide.md documents the new bump-context crate-set rule and Hypothesis coverage; no execplan exists and no roadmap item is left to check off.
Module-Level Documentation ✅ Passed PASS: every touched module has a top-level docstring that states its purpose and, where needed, its relationship to sibling components.
Testing (Unit And Behavioural) ✅ Passed PASS: The PR adds focused unit/property tests for manifest, selector, TOML and invariants, while BDD CLI scenarios exercise the bump command boundary end-to-end.
Testing (Property / Proof) ✅ Passed PASS: The refactor introduces a selection invariant and adds Hypothesis property tests that verify context derivation and manifest-selection parity against a naive reference.
Testing (Compile-Time / Ui) ✅ Passed No compile-time analogue is present in this Python PR; structured bump output is covered by focused snapshot tests plus semantic assertions in tests/unit/test_bump_output_formatting.py.
Unit Architecture ✅ Passed PASS: bump now consumes precomputed crate sets via _BumpContext, keeps I/O at command boundaries, and the new tests inject workspace/config dependencies and verify write paths explicitly.
Domain Architecture ✅ Passed Keep this refactor in the command layer: bump.py and cli.py delegate adapter concerns, and the developer guide documents the single-source-of-truth crate-set rule.
Observability ✅ Passed PASS: bump.py adds debug/error logs at context init, per-crate decisions, README transposition, and completion counts; no metrics or tracing gaps are introduced.
Security And Privacy ✅ Passed Keep this as PASS: no secrets, credentials, or auth-sensitive sinks appear; changes are confined to synthetic tests, docs, and internal debug logs.
Performance And Resource Use ✅ Passed PASS: _initialize_bump_context computes excluded/updated sets once, _apply_crate_manifest_update uses O(1) membership checks, and the new Hypothesis tests are bounded (max_examples=20).
Concurrency And State ✅ Passed No shared mutable state, locks, async tasks, or parallelism were introduced; BumpContext stays frozen and all new state is local and single-threaded.
Architectural Complexity And Maintainability ✅ Passed PASS: The refactor removes redundant per-crate recomputation, uses the context fields directly, and adds only a closed local enum plus focused tests/docs; no speculative layer or registry was intro...
Rust Compiler Lint Integrity ✅ Passed PASS: The PR touches only docs, Python tests, and a snapshot rename; no .rs files or Rust lint suppressions/clones were changed.
📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #97

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-97-bump-context-single-derivation

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 2500000 diff characters.

Please try again later or upgrade to continue using Sourcery

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review June 10, 2026 16:49
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@leynos

leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added the Issue label Jun 10, 2026
coderabbitai[bot]

This comment was marked as resolved.

@leynos

leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leynos

leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-97-bump-context-single-derivation branch from 5cb7c2d to cd8932a Compare June 10, 2026 22:09
codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-97-bump-context-single-derivation branch from ddb1f24 to 508c189 Compare June 11, 2026 14:51
codescene-delta-analysis[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@leynos

leynos commented Jun 11, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

codescene-delta-analysis[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

codescene-delta-analysis[bot]

This comment was marked as outdated.

Fold `params.test_id` into the three assertion failure messages of the
parametrised `test_update_crate_manifest` so a failure identifies the offending
case without cross-referencing the parametrize table.

Assertion conditions are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lading/commands/bump.py`:
- Around line 128-133: _CrateManifestUpdate currently allows an invalid state by
combining was_skipped and was_updated booleans; replace it with a single enum
(or Literal) representing skipped, updated, and unchanged so the outcome is
closed and type-safe. Update _update_crate_manifest to return the enum value and
refactor _process_crate_manifests to use match/case on that enum instead of
checking .was_skipped/.was_updated, and adjust any other call sites that rely on
the old boolean fields.

In `@tests/unit/test_bump_context_properties.py`:
- Around line 126-129: Add failure messages to the bare asserts in the affected
tests so Hypothesis failures identify which crate/example and expected values
broke. Update the checks in test_bump_context_properties and the
_assert_crate_manifest_update assertions to use assert with a descriptive
message including the crate name, example name, and expected vs actual
outcome/version values, referencing context.excluded,
context.updated_crate_names, and _assert_crate_manifest_update to locate the
assertions.
- Around line 286-353: The Hypothesis test for manifest selection is unbounded,
which can make this file-I/O-heavy suite slow and unpredictable in CI. Update
the `@settings` on test_manifest_selection_matches_naive_reference to include
max_examples=20 while keeping the existing suppress_health_check for
HealthCheck.function_scoped_fixture. Locate the change in
test_manifest_selection_matches_naive_reference and keep the rest of the test
logic unchanged.

In `@tests/unit/test_bump_manifest_writing.py`:
- Around line 21-22: The unit tests use bare asserts in multiple cases, which
makes failures hard to identify across parametrized branches. Update the
assertions in the test module to include a clear failure message for each check,
especially in the test cases around the version load and manifest write
scenarios, and apply the same pattern consistently in the related test functions
so the failing condition is obvious when an assert trips. Use the existing test
function names and assertion sites as the anchors for the edits.

In `@tests/unit/test_bump_result_formatting.py`:
- Around line 1-7: The test module is over the logical-line limit and currently
mixes result-message formatting checks with _update_crate_manifest coverage.
Move the change-description, grammar, and snapshot rendering tests from
test_bump_result_formatting.py into a new dedicated
test_bump_output_formatting.py, keeping the existing file focused on
lading.commands.bump._update_crate_manifest and its workspace-manifest fixtures.
Preserve the existing test names and assertions while relocating the
Result-message grammar and rendering block so the module stays under the
400-line cap.
- Around line 410-424: Add failure messages to the newly introduced asserts in
the bump result formatting tests so they follow the existing pattern and are
easier to debug. Update the bare assertions in the message formatting checks
around _build_expected_body, _build_expected_categories, and the header
validation in test_bump_result_formatting to use assert ... , "message" instead
of plain asserts, matching the earlier test_update_crate_manifest block.

In `@tests/unit/test_bump_selectors.py`:
- Around line 8-24: The unit tests for _determine_package_selectors and
_should_skip_crate_update use bare asserts, so add explicit failure messages to
each assertion. Update the asserts in
test_determine_package_selectors_respects_exclusions,
test_determine_package_selectors_includes_package_for_active_crates, and
test_should_skip_crate_update_requires_selectors_or_dependencies to use the
assert-with-message form, keeping the existing expectations but attaching a
short message that explains the expected selector or skip behavior.

In `@tests/unit/test_bump_toml_helpers.py`:
- Line 14: The unit tests in the bump TOML helpers file use bare asserts, so add
failure messages to each assertion for clearer debugging. Update the existing
checks in the nested-table selection and related test cases referenced by the
assertions in the test module, using the relevant test function names (such as
the ones covering absent-table handling, version assignment, value matching, and
out-of-order proxies) to locate them and convert each bare assert to an assert
with a descriptive message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 62c87c23-5a99-4595-9f11-90d3eefc8f58

📥 Commits

Reviewing files that changed from the base of the PR and between f712adb and de774e6.

📒 Files selected for processing (8)
  • docs/developers-guide.md
  • lading/commands/bump.py
  • tests/unit/__snapshots__/test_bump_result_formatting.ambr
  • tests/unit/test_bump_context_properties.py
  • tests/unit/test_bump_manifest_writing.py
  • tests/unit/test_bump_result_formatting.py
  • tests/unit/test_bump_selectors.py
  • tests/unit/test_bump_toml_helpers.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/cmd-mox (auto-detected)
  • leynos/shared-actions (auto-detected)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lading/commands/bump.py`:
- Around line 128-133: _CrateManifestUpdate currently allows an invalid state by
combining was_skipped and was_updated booleans; replace it with a single enum
(or Literal) representing skipped, updated, and unchanged so the outcome is
closed and type-safe. Update _update_crate_manifest to return the enum value and
refactor _process_crate_manifests to use match/case on that enum instead of
checking .was_skipped/.was_updated, and adjust any other call sites that rely on
the old boolean fields.

In `@tests/unit/test_bump_context_properties.py`:
- Around line 126-129: Add failure messages to the bare asserts in the affected
tests so Hypothesis failures identify which crate/example and expected values
broke. Update the checks in test_bump_context_properties and the
_assert_crate_manifest_update assertions to use assert with a descriptive
message including the crate name, example name, and expected vs actual
outcome/version values, referencing context.excluded,
context.updated_crate_names, and _assert_crate_manifest_update to locate the
assertions.
- Around line 286-353: The Hypothesis test for manifest selection is unbounded,
which can make this file-I/O-heavy suite slow and unpredictable in CI. Update
the `@settings` on test_manifest_selection_matches_naive_reference to include
max_examples=20 while keeping the existing suppress_health_check for
HealthCheck.function_scoped_fixture. Locate the change in
test_manifest_selection_matches_naive_reference and keep the rest of the test
logic unchanged.

In `@tests/unit/test_bump_manifest_writing.py`:
- Around line 21-22: The unit tests use bare asserts in multiple cases, which
makes failures hard to identify across parametrized branches. Update the
assertions in the test module to include a clear failure message for each check,
especially in the test cases around the version load and manifest write
scenarios, and apply the same pattern consistently in the related test functions
so the failing condition is obvious when an assert trips. Use the existing test
function names and assertion sites as the anchors for the edits.

In `@tests/unit/test_bump_result_formatting.py`:
- Around line 1-7: The test module is over the logical-line limit and currently
mixes result-message formatting checks with _update_crate_manifest coverage.
Move the change-description, grammar, and snapshot rendering tests from
test_bump_result_formatting.py into a new dedicated
test_bump_output_formatting.py, keeping the existing file focused on
lading.commands.bump._update_crate_manifest and its workspace-manifest fixtures.
Preserve the existing test names and assertions while relocating the
Result-message grammar and rendering block so the module stays under the
400-line cap.
- Around line 410-424: Add failure messages to the newly introduced asserts in
the bump result formatting tests so they follow the existing pattern and are
easier to debug. Update the bare assertions in the message formatting checks
around _build_expected_body, _build_expected_categories, and the header
validation in test_bump_result_formatting to use assert ... , "message" instead
of plain asserts, matching the earlier test_update_crate_manifest block.

In `@tests/unit/test_bump_selectors.py`:
- Around line 8-24: The unit tests for _determine_package_selectors and
_should_skip_crate_update use bare asserts, so add explicit failure messages to
each assertion. Update the asserts in
test_determine_package_selectors_respects_exclusions,
test_determine_package_selectors_includes_package_for_active_crates, and
test_should_skip_crate_update_requires_selectors_or_dependencies to use the
assert-with-message form, keeping the existing expectations but attaching a
short message that explains the expected selector or skip behavior.

In `@tests/unit/test_bump_toml_helpers.py`:
- Line 14: The unit tests in the bump TOML helpers file use bare asserts, so add
failure messages to each assertion for clearer debugging. Update the existing
checks in the nested-table selection and related test cases referenced by the
assertions in the test module, using the relevant test function names (such as
the ones covering absent-table handling, version assignment, value matching, and
out-of-order proxies) to locate them and convert each bare assert to an assert
with a descriptive message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 62c87c23-5a99-4595-9f11-90d3eefc8f58

📥 Commits

Reviewing files that changed from the base of the PR and between f712adb and de774e6.

📒 Files selected for processing (8)
  • docs/developers-guide.md
  • lading/commands/bump.py
  • tests/unit/__snapshots__/test_bump_result_formatting.ambr
  • tests/unit/test_bump_context_properties.py
  • tests/unit/test_bump_manifest_writing.py
  • tests/unit/test_bump_result_formatting.py
  • tests/unit/test_bump_selectors.py
  • tests/unit/test_bump_toml_helpers.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/cmd-mox (auto-detected)
  • leynos/shared-actions (auto-detected)
🛑 Comments failed to post (8)
lading/commands/bump.py (1)

128-133: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the two-boolean outcome with an enum to close off the invalid state.

_CrateManifestUpdate models three mutually exclusive outcomes (skipped, updated, unchanged) with two independent booleans, which permits a fourth, meaningless combination (was_skipped=True, was_updated=True) that nothing prevents at the type level. Collapse this into a single enum.Enum (or Literal) so illegal states are unrepresentable and call sites can pattern-match instead of chaining ifs.

♻️ Proposed refactor
-@dc.dataclass(frozen=True, slots=True)
-class _CrateManifestUpdate:
-    """Outcome from processing a single crate manifest."""
-
-    was_skipped: bool
-    was_updated: bool
+class _ManifestUpdateOutcome(enum.Enum):
+    """Result of processing a single crate manifest."""
+
+    SKIPPED = enum.auto()
+    UPDATED = enum.auto()
+    UNCHANGED = enum.auto()
-        return _CrateManifestUpdate(was_skipped=True, was_updated=False)
+        return _ManifestUpdateOutcome.SKIPPED
-    return _CrateManifestUpdate(was_skipped=False, was_updated=was_updated)
+    return (
+        _ManifestUpdateOutcome.UPDATED
+        if was_updated
+        else _ManifestUpdateOutcome.UNCHANGED
+    )

Update _process_crate_manifests and _update_crate_manifest to match on the enum instead of reading .was_skipped/.was_updated.

As per coding guidelines: "Use Enum for fixed sets of related constants" and "Use structural pattern matching (match/case) for branching over structured data, especially for enums."

Also applies to: 399-399, 429-429

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lading/commands/bump.py` around lines 128 - 133, _CrateManifestUpdate
currently allows an invalid state by combining was_skipped and was_updated
booleans; replace it with a single enum (or Literal) representing skipped,
updated, and unchanged so the outcome is closed and type-safe. Update
_update_crate_manifest to return the enum value and refactor
_process_crate_manifests to use match/case on that enum instead of checking
.was_skipped/.was_updated, and adjust any other call sites that rely on the old
boolean fields.
tests/unit/test_bump_context_properties.py (2)

126-129: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add failure messages to the bare asserts.

context.excluded/context.updated_crate_names checks (126-129) and the _assert_crate_manifest_update outcome/version checks (271-283) fail silently on which crate/example broke, since Hypothesis shrinks across many generated names. Attach the crate name and expected values to each assert.

🐛 Proposed fix (representative examples)
-    assert context.excluded == frozenset(exclude)
-    assert context.updated_crate_names == frozenset(
-        crate.name for crate in workspace.crates if crate.name not in set(exclude)
-    )
+    assert context.excluded == frozenset(exclude), f"excluded mismatch for {exclude=}"
+    assert context.updated_crate_names == frozenset(
+        crate.name for crate in workspace.crates if crate.name not in set(exclude)
+    ), f"updated_crate_names mismatch for {exclude=}"
-    assert outcome.was_skipped is expect_skipped
-    assert outcome.was_updated is (not expect_skipped)
+    assert outcome.was_skipped is expect_skipped, f"skip mismatch for {crate.name!r}"
+    assert outcome.was_updated is (not expect_skipped), f"update mismatch for {crate.name!r}"

Apply the same treatment to the remaining bare asserts at lines 275-277 and 283.
As per path instructions, "Use assert …, "message" over bare asserts".

Also applies to: 271-283

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_bump_context_properties.py` around lines 126 - 129, Add
failure messages to the bare asserts in the affected tests so Hypothesis
failures identify which crate/example and expected values broke. Update the
checks in test_bump_context_properties and the _assert_crate_manifest_update
assertions to use assert with a descriptive message including the crate name,
example name, and expected vs actual outcome/version values, referencing
context.excluded, context.updated_crate_names, and _assert_crate_manifest_update
to locate the assertions.

Source: Path instructions


286-353: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Bound Hypothesis examples for the file-I/O-heavy manifest test.

This test writes real manifests to disk for every generated example with no max_examples cap, unlike the documented pattern for I/O-heavy property suites. Bound it to keep CI time predictable.

 `@given`(data=st.data())
-@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
+@settings(
+    max_examples=20,
+    suppress_health_check=[HealthCheck.function_scoped_fixture],
+)
 def test_manifest_selection_matches_naive_reference(

As per coding guidelines, "Property-based tests in the publish suite use @settings(max_examples=20, suppress_health_check=[HealthCheck.function_scoped_fixture]) to keep continuous integration (CI) fast".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`@given`(data=st.data())
`@settings`(
    max_examples=20,
    suppress_health_check=[HealthCheck.function_scoped_fixture],
)
def test_manifest_selection_matches_naive_reference(
    tmp_path: Path,
    data: st.DataObject,
) -> None:
    """``_apply_crate_manifest_update`` honours the context crate sets.

    Reference semantics (issue `#97`): a non-excluded crate's package version is
    rewritten to the target; an excluded crate's package version is left
    untouched; a dependency edge is rewritten only when the crate it targets is
    itself being updated, and only in the manifest section its ``kind`` selects.
    The manifest pass must consume ``context.excluded`` and
    ``context.updated_crate_names`` — derived once in
    ``_initialize_bump_context`` — rather than re-deriving them per crate.

    ``tmp_path`` is a function-scoped fixture reused across Hypothesis
    examples; each example rewrites every manifest to its initial state before
    processing, so the shared directory cannot leak state between examples.
    """
    names = sorted(
        data.draw(st.sets(_crate_name, min_size=2, max_size=6), label="names")
    )

    # Force at least one updated (non-excluded) crate and at least one excluded
    # crate so every example exercises both branches of the skip rule.
    updated_pivot = data.draw(st.sampled_from(names), label="updated_pivot")
    other_names = [name for name in names if name != updated_pivot]
    excluded_pivot = data.draw(st.sampled_from(other_names), label="excluded_pivot")
    extra_excluded = data.draw(
        st.sets(st.sampled_from(other_names), max_size=len(other_names)),
        label="extra_excluded",
    )
    exclude_set = {excluded_pivot} | extra_excluded
    exclude = tuple(sorted(exclude_set))

    edges = _draw_dependency_edges(data, names, updated_pivot)
    dependencies: _DependencyEdges = {
        name: tuple(sorted(targets.items())) for name, targets in edges.items()
    }

    updated_names = {name for name in names if name not in exclude_set}
    assert exclude_set, "example must exclude at least one crate"
    assert any(
        target in updated_names for targets in edges.values() for target in targets
    ), "example must contain a dependency on an updated crate"

    _write_synthetic_manifests(tmp_path, names, dependencies)
    workspace = _synthetic_workspace(names, root=tmp_path, dependencies=dependencies)
    context = bump._initialize_bump_context(
        tmp_path,
        bump.BumpOptions(
            configuration=_make_config(exclude=exclude),
            workspace=workspace,
        ),
    )

    crates_by_name = {crate.name: crate for crate in workspace.crates}
    for name in names:
        _assert_crate_manifest_update(
            crates_by_name[name],
            edges[name],
            context,
            _ExpectedManifestOutcome(
                updated_names=updated_names,
                excluded=name in exclude_set,
            ),
        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_bump_context_properties.py` around lines 286 - 353, The
Hypothesis test for manifest selection is unbounded, which can make this
file-I/O-heavy suite slow and unpredictable in CI. Update the `@settings` on
test_manifest_selection_matches_naive_reference to include max_examples=20 while
keeping the existing suppress_health_check for
HealthCheck.function_scoped_fixture. Locate the change in
test_manifest_selection_matches_naive_reference and keep the rest of the test
logic unchanged.

Source: Coding guidelines

tests/unit/test_bump_manifest_writing.py (1)

21-22: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add failure messages to the bare asserts.

None of the asserts in this file carry a message, making a Hypothesis-free but multi-branch failure harder to diagnose (e.g. which parametrised case failed at lines 74-78).

🐛 Proposed fix (representative example)
-    assert changed is True
-    assert document["dependencies"]["alpha"].value == "1.0.0"
-    assert (
-        document["workspace"]["dependencies"]["alpha"].value == expected_workspace_alpha
-    )
+    assert changed is True, "dependency-section update should report a change"
+    assert document["dependencies"]["alpha"].value == "1.0.0", "top-level alpha not updated"
+    assert (
+        document["workspace"]["dependencies"]["alpha"].value == expected_workspace_alpha
+    ), f"workspace alpha mismatch for include_workspace_sections={include_workspace_sections}"

Apply the same treatment to the remaining bare asserts (lines 21-22, 34-38, 49-50, 90-91, 106-109).
As per path instructions, "Use assert …, "message" over bare asserts".

Also applies to: 34-38, 49-50, 74-78, 90-91, 106-109

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_bump_manifest_writing.py` around lines 21 - 22, The unit
tests use bare asserts in multiple cases, which makes failures hard to identify
across parametrized branches. Update the assertions in the test module to
include a clear failure message for each check, especially in the test cases
around the version load and manifest write scenarios, and apply the same pattern
consistently in the related test functions so the failing condition is obvious
when an assert trips. Use the existing test function names and assertion sites
as the anchors for the edits.

Source: Path instructions

tests/unit/test_bump_result_formatting.py (2)

1-7: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

File exceeds the 400-logical-line cap.

The module reaches 461 lines. Split the change-description/grammar/snapshot formatting tests (roughly the # Result-message grammar and rendering block) into a dedicated test_bump_output_formatting.py, keeping this file focused on _update_crate_manifest coverage.

As per path instructions, "Files must not exceed 400 logical lines: Decompose large modules into subpackages".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_bump_result_formatting.py` around lines 1 - 7, The test
module is over the logical-line limit and currently mixes result-message
formatting checks with _update_crate_manifest coverage. Move the
change-description, grammar, and snapshot rendering tests from
test_bump_result_formatting.py into a new dedicated
test_bump_output_formatting.py, keeping the existing file focused on
lading.commands.bump._update_crate_manifest and its workspace-manifest fixtures.
Preserve the existing test names and assertions while relocating the
Result-message grammar and rendering block so the module stays under the
400-line cap.

Source: Path instructions


410-424: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add failure messages to the newly-added asserts.

The earlier test_update_crate_manifest block (328-335) already carries assert messages per the prior fix, but this later block reintroduces bare asserts.

🐛 Proposed fix (representative example)
     if not any((manifests, documents, readmes, lockfiles)):
-        assert message == "No manifest changes required; all versions already 1.2.3."
+        assert message == "No manifest changes required; all versions already 1.2.3.", (
+            "empty change set should report no-changes message"
+        )
         return

     lines = message.splitlines()
-    assert lines[1:] == _build_expected_body(root, changes)
+    assert lines[1:] == _build_expected_body(root, changes), "body lines mismatch"

Apply the same treatment to lines 421-424, 446-447, and 458-461.
As per path instructions, "Use assert …, "message" over bare asserts".

Also applies to: 446-447, 458-461

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_bump_result_formatting.py` around lines 410 - 424, Add
failure messages to the newly introduced asserts in the bump result formatting
tests so they follow the existing pattern and are easier to debug. Update the
bare assertions in the message formatting checks around _build_expected_body,
_build_expected_categories, and the header validation in
test_bump_result_formatting to use assert ... , "message" instead of plain
asserts, matching the earlier test_update_crate_manifest block.

Source: Path instructions

tests/unit/test_bump_selectors.py (1)

8-24: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add failure messages to the bare asserts.

Logic coverage is correct, but none of the four asserts carry a message.

🐛 Proposed fix
-    assert bump._determine_package_selectors("beta", {"beta"}) == ()
+    assert bump._determine_package_selectors("beta", {"beta"}) == (), (
+        "excluded crate should yield no selectors"
+    )
 ...
-    assert bump._determine_package_selectors("beta", set()) == (("package",),)
+    assert bump._determine_package_selectors("beta", set()) == (("package",),), (
+        "active crate should yield the package selector"
+    )
 ...
-    assert bump._should_skip_crate_update((), {}) is True
+    assert bump._should_skip_crate_update((), {}) is True, (
+        "empty selectors and dependencies should skip"
+    )
     assert (
         bump._should_skip_crate_update((("package",),), {"dependencies": ("alpha",)})
         is False
-    )
+    ), "non-empty selectors should not skip"

As per path instructions, "Use assert …, "message" over bare asserts".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

def test_determine_package_selectors_respects_exclusions() -> None:
    """Excluded crates produce no package selectors."""
    assert bump._determine_package_selectors("beta", {"beta"}) == (), (
        "excluded crate should yield no selectors"
    )


def test_determine_package_selectors_includes_package_for_active_crates() -> None:
    """Active crates receive the package selector tuple."""
    assert bump._determine_package_selectors("beta", set()) == (("package",),), (
        "active crate should yield the package selector"
    )


def test_should_skip_crate_update_requires_selectors_or_dependencies() -> None:
    """Skipping occurs only when both selectors and dependency sections are empty."""
    assert bump._should_skip_crate_update((), {}) is True, (
        "empty selectors and dependencies should skip"
    )
    assert (
        bump._should_skip_crate_update((("package",),), {"dependencies": ("alpha",)})
        is False
    ), "non-empty selectors should not skip"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_bump_selectors.py` around lines 8 - 24, The unit tests for
_determine_package_selectors and _should_skip_crate_update use bare asserts, so
add explicit failure messages to each assertion. Update the asserts in
test_determine_package_selectors_respects_exclusions,
test_determine_package_selectors_includes_package_for_active_crates, and
test_should_skip_crate_update_requires_selectors_or_dependencies to use the
assert-with-message form, keeping the existing expectations but attaching a
short message that explains the expected selector or skip behavior.

Source: Path instructions

tests/unit/test_bump_toml_helpers.py (1)

14-14: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add failure messages to the bare asserts.

All assertions in this file (nested-table selection, absent-table handling, version assignment, value matching, out-of-order proxies) are bare.

🐛 Proposed fix (representative example)
-    assert table is document["workspace"]["package"]
+    assert table is document["workspace"]["package"], "nested table selector failed"
 ...
-    assert table is None
+    assert table is None, "non-table selector path should return None"
 ...
-    assert bump._assign_version(None, "1.0.0") is False
+    assert bump._assign_version(None, "1.0.0") is False, "absent table should be a no-op"

Apply the same treatment to the remaining bare asserts at lines 32-33, 39, 44-45, 52-53, 68-69, and 82-83.
As per path instructions, "Use assert …, "message" over bare asserts".

Also applies to: 21-21, 26-26, 32-33, 39-39, 44-45, 52-53, 68-69, 82-83

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_bump_toml_helpers.py` at line 14, The unit tests in the bump
TOML helpers file use bare asserts, so add failure messages to each assertion
for clearer debugging. Update the existing checks in the nested-table selection
and related test cases referenced by the assertions in the test module, using
the relevant test function names (such as the ones covering absent-table
handling, version assignment, value matching, and out-of-order proxies) to
locate them and convert each bare assert to an assert with a descriptive
message.

Source: Path instructions

@leynos

leynos commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leynos

leynos commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

…ssages (#97)

Address a batch of review findings on the bump command and its tests.

- bump.py: replace the two-boolean `_CrateManifestUpdate` dataclass with a
  closed `_CrateManifestOutcome` enum (SKIPPED / UPDATED / UNCHANGED) so the
  both-true state is unrepresentable; `_process_crate_manifests` selects on it
  with match/case. Remove the redundant `_update_crate_manifest` wrapper (a
  pass-through once the outcome is an enum) and point its sole caller at
  `_apply_crate_manifest_update`.
- Split test_bump_result_formatting.py: the change-description, grammar, and
  snapshot result-message tests move to a new test_bump_output_formatting.py
  (with the snapshot file renamed accordingly), leaving the original focused on
  `_apply_crate_manifest_update` and its fixtures. Both modules are now well
  under the 400-line cap.
- Cap the I/O-heavy manifest-selection Hypothesis test at max_examples=20,
  matching the repo's established pattern.
- Add descriptive failure messages to the bare asserts across
  test_bump_selectors, test_bump_toml_helpers, test_bump_manifest_writing,
  test_bump_context_properties, and test_bump_output_formatting; the manifest
  property helper now names the crate and reports expected vs actual.

No behavioural change to the bump command; the enum carries the same
skipped/updated/unchanged information the boolean pair did.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following failed checks now been resolved?

If further work is required, please provide an AI agent prompt for the remaining work to be done to address these failures.

Do not treat warnings as optional or aspirational. Where a change is out of scope for this PR, propose a GitHub issue unless one exists already. (Treat documentation and validation coverage as in scope).

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Performance And Resource Use ⚠️ Warning The new Hypothesis manifest test writes and rereads real files per example but lacks a max_examples cap, so CI cost is needlessly high versus the repo's 20-example pattern. Add @settings(max_examples=20, suppress_health_check=[HealthCheck.function_scoped_fixture]) to the manifest-selection property test, matching the docs and bounding file I/O.
Architectural Complexity And Maintainability ⚠️ Warning The PR leaves a 461-line mixed-purpose test module and adds a wrapper/dataclass layer around manifest updates, increasing indirection without a lean maintenance story. Split the formatting/snapshot tests into a dedicated module, collapse the manifest-update outcome layer, and cap the I/O-heavy Hypothesis suite.

@leynos

leynos commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following now been resolved?

Annul any requirements that violate the en-GB-oxendict spelling (-ize / -yse / -our) conventions (for example a request to replace "normalize" with "normalise" or "artefact" with "artifact"), or where the requirement unnecessarily increases cyclomatic complexity.

Inline comments:
In @lading/commands/bump.py:

  • Around line 128-133: _CrateManifestUpdate currently allows an invalid state by
    combining was_skipped and was_updated booleans; replace it with a single enum
    (or Literal) representing skipped, updated, and unchanged so the outcome is
    closed and type-safe. Update _update_crate_manifest to return the enum value and
    refactor _process_crate_manifests to use match/case on that enum instead of
    checking .was_skipped/.was_updated, and adjust any other call sites that rely on
    the old boolean fields.

In @tests/unit/test_bump_context_properties.py:

  • Around line 126-129: Add failure messages to the bare asserts in the affected
    tests so Hypothesis failures identify which crate/example and expected values
    broke. Update the checks in test_bump_context_properties and the
    _assert_crate_manifest_update assertions to use assert with a descriptive
    message including the crate name, example name, and expected vs actual
    outcome/version values, referencing context.excluded,
    context.updated_crate_names, and _assert_crate_manifest_update to locate the
    assertions.
  • Around line 286-353: The Hypothesis test for manifest selection is unbounded,
    which can make this file-I/O-heavy suite slow and unpredictable in CI. Update
    the @settings on test_manifest_selection_matches_naive_reference to include
    max_examples=20 while keeping the existing suppress_health_check for
    HealthCheck.function_scoped_fixture. Locate the change in
    test_manifest_selection_matches_naive_reference and keep the rest of the test
    logic unchanged.

In @tests/unit/test_bump_manifest_writing.py:

  • Around line 21-22: The unit tests use bare asserts in multiple cases, which
    makes failures hard to identify across parametrized branches. Update the
    assertions in the test module to include a clear failure message for each check,
    especially in the test cases around the version load and manifest write
    scenarios, and apply the same pattern consistently in the related test functions
    so the failing condition is obvious when an assert trips. Use the existing test
    function names and assertion sites as the anchors for the edits.

In @tests/unit/test_bump_result_formatting.py:

  • Around line 1-7: The test module is over the logical-line limit and currently
    mixes result-message formatting checks with _update_crate_manifest coverage.
    Move the change-description, grammar, and snapshot rendering tests from
    test_bump_result_formatting.py into a new dedicated
    test_bump_output_formatting.py, keeping the existing file focused on
    lading.commands.bump._update_crate_manifest and its workspace-manifest fixtures.
    Preserve the existing test names and assertions while relocating the
    Result-message grammar and rendering block so the module stays under the
    400-line cap.
  • Around line 410-424: Add failure messages to the newly introduced asserts in
    the bump result formatting tests so they follow the existing pattern and are
    easier to debug. Update the bare assertions in the message formatting checks
    around _build_expected_body, _build_expected_categories, and the header
    validation in test_bump_result_formatting to use assert ... , "message" instead
    of plain asserts, matching the earlier test_update_crate_manifest block.

In @tests/unit/test_bump_selectors.py:

  • Around line 8-24: The unit tests for _determine_package_selectors and
    _should_skip_crate_update use bare asserts, so add explicit failure messages to
    each assertion. Update the asserts in
    test_determine_package_selectors_respects_exclusions,
    test_determine_package_selectors_includes_package_for_active_crates, and
    test_should_skip_crate_update_requires_selectors_or_dependencies to use the
    assert-with-message form, keeping the existing expectations but attaching a
    short message that explains the expected selector or skip behavior.

In @tests/unit/test_bump_toml_helpers.py:

  • Line 14: The unit tests in the bump TOML helpers file use bare asserts, so add
    failure messages to each assertion for clearer debugging. Update the existing
    checks in the nested-table selection and related test cases referenced by the
    assertions in the test module, using the relevant test function names (such as
    the ones covering absent-table handling, version assignment, value matching, and
    out-of-order proxies) to locate them and convert each bare assert to an assert
    with a descriptive message.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== settings/max_examples on property tests ==="
rg -n "max_examples|`@settings`|HealthCheck" tests/unit/test_bump_context_properties.py

echo ""
echo "=== file line counts ==="
wc -l tests/unit/test_bump_context_properties.py tests/unit/test_bump_result_formatting.py tests/unit/test_bump_manifest_writing.py tests/unit/test_bump_selectors.py tests/unit/test_bump_toml_helpers.py 2>/dev/null

echo ""
echo "=== does test_bump_command_internals.py still exist? ==="
fd test_bump_command_internals.py

echo ""
echo "=== _CrateManifestUpdate / outcome dataclass in bump.py ==="
rg -n "_CrateManifestUpdate|was_skipped|was_updated" lading/commands/bump.py

echo ""
echo "=== docs mention of max_examples pattern ==="
rg -n "max_examples" docs/developers-guide.md

Length of output: 1767


🏁 Script executed:

#!/bin/bash
echo "=== _apply_crate_manifest_update and outcome type ==="
rg -n "_apply_crate_manifest_update|_process_crate_manifests|_should_skip_crate_update" lading/commands/bump.py

echo ""
sed -n '380,435p' lading/commands/bump.py

Length of output: 2637


@leynos Both warnings are resolved. Verified against the current branch.

Performance And Resource Use — ✅ resolved. tests/unit/test_bump_context_properties.py Lines 304–306 now carry:

`@settings`(
    max_examples=20,
    suppress_health_check=[HealthCheck.function_scoped_fixture],
)

directly above the manifest-selection property test, matching the 20-example pattern documented in docs/developers-guide.md Line 111. File I/O per example is now bounded.

Architectural Complexity And Maintainability — ✅ resolved. Both remediation items landed:

  • The 461-line tests/unit/test_bump_command_internals.py no longer exists. Content is split by responsibility: test_bump_context_properties.py (373 lines, Hypothesis/context tests), test_bump_result_formatting.py (189 lines), test_bump_manifest_writing.py (123 lines), test_bump_toml_helpers.py (105 lines), test_bump_selectors.py (30 lines). None approach the 400-line threshold.
  • The manifest-update outcome layer is collapsed. _apply_crate_manifest_update (lading/commands/bump.py, Line 379) now returns a plain _CrateManifestOutcome enum (SKIPPED/UPDATED/UNCHANGED) rather than a wrapper dataclass with was_skipped/was_updated booleans, and _process_crate_manifests (Line 260) consumes it with a match statement — removing the extra indirection flagged in the review.

No further work is required for either check.

(⁠ ⁠•̀ᴗ⁠•́⁠)⁠ nine for nine

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== _CrateManifestUpdate ==="
rg -n "_CrateManifestUpdate|was_skipped|was_updated" lading/commands/bump.py
echo ""
sed -n '100,145p' lading/commands/bump.py

echo "=== test_bump_context_properties.py asserts ==="
sed -n '100,140p' tests/unit/test_bump_context_properties.py
echo "---settings---"
rg -n "`@settings`|max_examples|suppress_health_check" tests/unit/test_bump_context_properties.py
sed -n '280,360p' tests/unit/test_bump_context_properties.py

echo "=== test_bump_manifest_writing.py asserts ==="
grep -n "assert " tests/unit/test_bump_manifest_writing.py | head -40

echo "=== test_bump_result_formatting.py length & asserts ==="
wc -l tests/unit/test_bump_result_formatting.py
grep -n "^def \|^class " tests/unit/test_bump_result_formatting.py
grep -n "assert " tests/unit/test_bump_result_formatting.py | head -40

echo "=== test_bump_selectors.py asserts ==="
cat -n tests/unit/test_bump_selectors.py

echo "=== test_bump_toml_helpers.py asserts ==="
grep -n "assert " tests/unit/test_bump_toml_helpers.py | head -40

Length of output: 11848


@leynos All items resolved as of the current branch state.

lading/commands/bump.py — enum replacing the boolean pair — ✅ resolved. Lines 128–137 now define:

class _CrateManifestOutcome(enum.Enum):
    """Closed outcome of processing a single crate manifest.

    Replaces a two-boolean record so the skipped/updated/unchanged states are
    mutually exclusive and the ``both true`` state is unrepresentable.
    """

    SKIPPED = enum.auto()
    UPDATED = enum.auto()
    UNCHANGED = enum.auto()

_update_crate_manifest/_apply_crate_manifest_update now return this enum, and call sites (e.g. test_bump_result_formatting.py Line 180, test_bump_context_properties.py) compare against bump._CrateManifestOutcome.UPDATED/.SKIPPED directly rather than checking two booleans. The invalid "both true" state is gone.

tests/unit/test_bump_context_properties.py — bare asserts — ✅ resolved. Every assertion, including the context-derivation checks and the _assert_crate_manifest_update outcome/version checks, now carries an f-string message naming the crate, the field (context.excluded, context.updated_crate_names, section name), and expected-vs-actual values.

tests/unit/test_bump_context_properties.py — unbounded Hypothesis manifest test — ✅ resolved. Lines 304–306:

`@settings`(
    max_examples=20,
    suppress_health_check=[HealthCheck.function_scoped_fixture],
)

is applied directly above test_manifest_selection_matches_naive_reference, matching the repo's existing 20-example pattern and bounding the file-I/O cost.

tests/unit/test_bump_manifest_writing.py — bare asserts — ✅ resolved. All asserts (Lines 21, 22, 36, 38, 40, 53, 54, 80, 81, 84, 98, 99, 116, 117, 121) now carry failure messages describing the version/change expectation being checked.

tests/unit/test_bump_result_formatting.py — module split / length — ✅ resolved. The file is now 189 lines and scoped to _update_crate_manifest and its workspace-manifest fixtures (UpdateCrateTestParams, _make_test_crate_with_dependency, _make_workspace_with_alpha_dependency, _parse_manifest_versions, test_update_crate_manifest); the result-message grammar/rendering/snapshot tests have already been relocated out of this module, so it sits well under the 400-line cap. Its remaining asserts (Lines 180, 184, 187) carry messages too.

tests/unit/test_bump_selectors.py — bare asserts — ✅ resolved. All three tests (test_determine_package_selectors_respects_exclusions, test_determine_package_selectors_includes_package_for_active_crates, test_should_skip_crate_update_requires_selectors_or_dependencies) now assert with descriptive messages (Lines 10–11, 17–18, 24–25, 27–29).

tests/unit/test_bump_toml_helpers.py — bare asserts — ✅ resolved. Every assertion across the nested-table selection, version-assignment, value-matching, and out-of-order table tests now includes a message (Lines 14, 23, 28, 36, 39, 45, 52, 55, 64, 67, 84, 85, 100, 103).

Nice work clearing the full batch. (⁠ゝ⁠∀⁠・⁠)⁠b

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lading/commands/bump.py (1)

434-449: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tighten excluded to cabc.Set[str] for the same reason updated_crates was tightened.

_determine_package_selectors still accepts excluded: cabc.Collection[str], so a caller could hand it a list and silently degrade the crate_name in excluded check to O(n). The sibling helpers (_workspace_dependency_sections, _dependency_sections_for_crate) were already tightened to cabc.Set[str] for exactly this reason in this PR — extend the same contract here for consistency, since the only caller passes context.excluded, which is already a frozenset[str].

♻️ Proposed fix
 def _determine_package_selectors(
     crate_name: str,
-    excluded: cabc.Collection[str],
+    excluded: cabc.Set[str],
 ) -> tuple[tuple[str, ...], ...]:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lading/commands/bump.py` around lines 434 - 449, Tighten
`_determine_package_selectors` to accept `excluded: cabc.Set[str]` instead of
`cabc.Collection[str]`, matching the existing set-based contract used by
`_workspace_dependency_sections` and `_dependency_sections_for_crate`. Update
the helper signature and any related type hints in `bump.py` so the membership
check stays O(1) and remains consistent with the `context.excluded` caller,
which already provides a `frozenset[str]`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit/test_bump_context_properties.py`:
- Around line 230-262: The forced “depends on updated crate” edge is currently
attached to an arbitrary crate in `_draw_dependency_edges`, so the
excluded-crate rewrite path is not guaranteed to be exercised. Update the helper
and its call site in `test_bump_context_properties` so the forced edge is pinned
to `excluded_pivot` instead of a random non-updated crate, and pass that
excluded pivot through to `_draw_dependency_edges` alongside `updated_pivot`.

In `@tests/unit/test_bump_output_formatting.py`:
- Around line 46-89: Add assertion messages to every bare equality check in the
affected bump output formatting tests so failures explain what went wrong.
Update test_format_manifest_path_relative,
test_format_manifest_path_outside_workspace, and the no-op asserts around the
later sections in test_bump_output_formatting.py to use assert ... , "message"
consistently, following the existing style already used in the sibling
formatting tests.
- Line 25: The test module is using two different references for the same class,
so update all `BumpChanges` usages in this file to consistently use
`bump_output.BumpChanges` instead of `bump.BumpChanges`. Keep the existing
`bump_output` import as the single source of truth and replace the remaining
`bump` references at the relevant `BumpChanges` construction sites and
assertions.

In `@tests/unit/test_bump_result_formatting.py`:
- Around line 32-141: The private helper docstrings in
_make_test_crate_with_dependency, _make_workspace_with_alpha_dependency, and
_parse_manifest_versions use mixed Google/numpy style and are too verbose for
underscore-prefixed functions. Simplify each private helper to a single-line
summary only, or if structured docs must remain, convert the section headers to
numpy style with Parameters/Returns and remove the Google-style Args:. Apply the
same docstring cleanup consistently across all three helpers so the test module
follows the numpy docstring convention.

In `@tests/unit/test_bump_selectors.py`:
- Around line 22-30: The test for bump._should_skip_crate_update only covers the
both-empty and both-non-empty extremes, so add explicit branch coverage for the
empty-selectors/non-empty-dependencies case and the
non-empty-selectors/empty-dependencies case. Update
test_should_skip_crate_update_requires_selectors_or_dependencies to use a
parametrized pytest test (and add the pytest import) so each OR-branch is
asserted directly, while still keeping the existing crate-update skip behavior
checks.

---

Outside diff comments:
In `@lading/commands/bump.py`:
- Around line 434-449: Tighten `_determine_package_selectors` to accept
`excluded: cabc.Set[str]` instead of `cabc.Collection[str]`, matching the
existing set-based contract used by `_workspace_dependency_sections` and
`_dependency_sections_for_crate`. Update the helper signature and any related
type hints in `bump.py` so the membership check stays O(1) and remains
consistent with the `context.excluded` caller, which already provides a
`frozenset[str]`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c67e240b-24f7-4cd8-9641-8d34af81f4c5

📥 Commits

Reviewing files that changed from the base of the PR and between f712adb and a98ce57.

📒 Files selected for processing (10)
  • docs/developers-guide.md
  • lading/commands/bump.py
  • tests/unit/__snapshots__/test_bump_output_formatting.ambr
  • tests/unit/test_bump_command_internals.py
  • tests/unit/test_bump_context_properties.py
  • tests/unit/test_bump_manifest_writing.py
  • tests/unit/test_bump_output_formatting.py
  • tests/unit/test_bump_result_formatting.py
  • tests/unit/test_bump_selectors.py
  • tests/unit/test_bump_toml_helpers.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/cmd-mox (auto-detected)
  • leynos/shared-actions (auto-detected)
💤 Files with no reviewable changes (1)
  • tests/unit/test_bump_command_internals.py

Comment thread tests/unit/test_bump_context_properties.py
Comment thread tests/unit/test_bump_output_formatting.py Outdated
Comment thread tests/unit/test_bump_output_formatting.py Outdated
Comment thread tests/unit/test_bump_result_formatting.py
Comment thread tests/unit/test_bump_selectors.py Outdated
Address a batch of review findings across the bump tests and one typing tweak.

- bump.py: tighten `_determine_package_selectors` from `cabc.Collection[str]`
  to `cabc.Set[str]`, matching the sibling section helpers and the frozenset
  `context.excluded` caller so membership stays O(1).
- test_bump_context_properties.py: pin the forced dependency edge to
  `excluded_pivot` (not a random crate) so the excluded-crate dependency
  rewrite path is exercised on every example.
- test_bump_output_formatting.py: reference `bump_output.BumpChanges` /
  `bump_output._format_result_message` consistently (drop the `bump` alias) and
  add failure messages to the remaining bare equality asserts.
- test_bump_result_formatting.py: reduce three private helper docstrings to
  single-line summaries.
- test_bump_selectors.py: parametrise the skip-behaviour test across all four
  selector/dependency OR-branches.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Passed
6 Quality Gates Passed

See analysis details in CodeScene

Absence of Expected Change Pattern

  • lading/lading/commands/bump.py is usually changed with: lading/tests/unit/test_cli.py

Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@leynos

leynos commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lading/commands/bump.py (1)

434-449: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fix the mixed docstring style on _determine_package_selectors.

The docstring pairs a Google-style Args: block with a numpy-style Returns\n------- section. _determine_package_selectors is a private helper, so per path instructions it should carry only a single-line summary — full structured docs are reserved for public interfaces.

🩹 Proposed fix
 def _determine_package_selectors(
     crate_name: str,
     excluded: cabc.Set[str],
 ) -> tuple[tuple[str, ...], ...]:
-    """Return package selectors for the crate, respecting exclusion rules.
-
-    Args:
-        crate_name: Name of the crate to check.
-        excluded: Set of excluded crate names.
-
-    Returns
-    -------
-        Package selectors tuple, or empty tuple if crate is excluded.
-
-    """
+    """Return package selectors for the crate, respecting exclusion rules."""
     return () if crate_name in excluded else (("package",),)

As per path instructions, "Docstrings must follow the numpy style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lading/commands/bump.py` around lines 434 - 449, The docstring on
_determine_package_selectors mixes Google and numpy styles and is too verbose
for a private helper; replace it with a single-line summary only. Keep the
function name and existing behavior unchanged, and remove the Args/Returns
sections so the docstring matches the path instruction for private functions and
methods.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@lading/commands/bump.py`:
- Around line 434-449: The docstring on _determine_package_selectors mixes
Google and numpy styles and is too verbose for a private helper; replace it with
a single-line summary only. Keep the function name and existing behavior
unchanged, and remove the Args/Returns sections so the docstring matches the
path instruction for private functions and methods.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b0638458-c4c0-4e8b-b11d-08379ed55029

📥 Commits

Reviewing files that changed from the base of the PR and between a98ce57 and 5e18f53.

📒 Files selected for processing (5)
  • lading/commands/bump.py
  • tests/unit/test_bump_context_properties.py
  • tests/unit/test_bump_output_formatting.py
  • tests/unit/test_bump_result_formatting.py
  • tests/unit/test_bump_selectors.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/cmd-mox (auto-detected)
  • leynos/shared-actions (auto-detected)

@leynos

leynos commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai please create a single new GitHub issue for the following concern and all other instances of this issue across the codebase:

lading/commands/bump.py (1)

434-449: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Fix the mixed docstring style on _determine_package_selectors.
The docstring pairs a Google-style Args: block with a numpy-style Returns\n------- section. _determine_package_selectors is a private helper, so per path instructions it should carry only a single-line summary — full structured docs are reserved for public interfaces.

🩹 Proposed fix
def _determine_package_selectors(
     crate_name: str,
     excluded: cabc.Set[str],
 ) -> tuple[tuple[str, ...], ...]:
-    """Return package selectors for the crate, respecting exclusion rules.
-
-    Args:
-        crate_name: Name of the crate to check.
-        excluded: Set of excluded crate names.
-
-    Returns
-    -------
-        Package selectors tuple, or empty tuple if crate is excluded.
-
-    """
+    """Return package selectors for the crate, respecting exclusion rules."""
     return () if crate_name in excluded else (("package",),)

As per path instructions, "Docstrings must follow the numpy style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces."

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @lading/commands/bump.py around lines 434 - 449, The docstring on
_determine_package_selectors mixes Google and numpy styles and is too verbose
for a private helper; replace it with a single-line summary only. Keep the
function name and existing behavior unchanged, and remove the Args/Returns
sections so the docstring matches the path instruction for private functions and
methods.

Source: Path instructions

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== search for mixed docstring patterns (Args: followed by Returns\n----) ==="
rg -n -B2 "Args:" lading -A15 --type py | grep -B15 "Returns$" | head -100

echo ""
echo "=== more targeted: find functions with 'Args:' block in private helpers ==="
rg -n "Args:" --type py lading

echo ""
echo "=== find 'Returns' followed by dashes (numpy style) near Args: (google style) in same docstring ==="
rg -nP "Args:" --type py -A 20 lading | rg -B20 "^\d+-\s*-------"

Length of output: 5173


🏁 Script executed:

#!/bin/bash
echo "=== bump_toml.py function signatures ==="
sed -n '110,175p' lading/commands/bump_toml.py

echo ""
echo "=== bump.py functions at 480-500 ==="
sed -n '475,500p' lading/commands/bump.py

echo ""
echo "=== config.py function at 225-250 ==="
sed -n '220,250p' lading/config.py

Length of output: 4340


🏁 Script executed:

#!/bin/bash
set -euo pipefail

body=$(cat <<'EOF'
## Summary

Several docstrings in the codebase mix Google-style `Args:` blocks with numpy-style `Returns`/`Raises` sections. Per the repository's path instructions, docstrings must follow the numpy style guide throughout: private functions/methods should carry only a single-line summary, and public interfaces should use full structured numpy-style docs (`Parameters`, `Returns`, `Raises` — not `Args:`).

Originating from review discussion on `#121` (comment: https://github.com/leynos/lading/pull/121#issuecomment-placeholder), raised by `@leynos`.

## Affected locations

**Private helpers (should be truncated to a single-line summary):**
- `lading/commands/bump.py` — `_determine_package_selectors` (Lines 434-449)
- `lading/commands/bump.py` — `_update_manifest` (Lines 481-500)
- `lading/config.py` — `_validate_mapping_keys` (Lines 225-244)

**Public functions (should be converted to full numpy-style docs, i.e. `Args:` → `Parameters\n----------`):**
- `lading/commands/bump_toml.py` — `update_section` (Lines 128-144)
- `lading/commands/bump_toml.py` — `update_dependency_sections` (Lines 156-171)

## Required changes

- For each private helper listed above, replace the docstring with a single-line summary only, removing the `Args:`/`Returns`/`Raises` sections.
- For each public function listed above, rewrite the docstring to full numpy style: replace `Args:` with `Parameters\n----------` (with parameter types), and keep `Returns`/`Raises` sections in numpy format (already numpy-style in these cases).
- Audit the rest of the codebase for the same `Args:` + numpy-section mixed pattern to catch any remaining instances beyond those listed here.

## Acceptance criteria

- No docstring mixes Google-style `Args:` with numpy-style `Returns`/`Raises` sections.
- Private functions/methods carry single-line-summary docstrings only.
- Public functions/methods carry full numpy-style structured docstrings.

## References

- PR: https://github.com/leynos/lading/pull/121
- Requested by: `@leynos`
EOF
)

gh issue create \
  --repo leynos/lading \
  --title "Fix mixed Google/numpy docstring style across bump.py, bump_toml.py, config.py" \
  --body "$body" \
  --assignee leynos

Length of output: 2445


@leynos I've created a single issue covering the mixed docstring style concern and every other instance I found in the codebase: #162.

Instances included:

Private helpers (fix: truncate to single-line summary, per path instructions):

  • lading/commands/bump.py_determine_package_selectors (Lines 434–449) — the one you originally flagged.
  • lading/commands/bump.py_update_manifest (Lines 481–500) — same Args: + numpy Returns mix.
  • lading/config.py_validate_mapping_keys (Lines 225–244) — Args: mixed with numpy Raises.

Public functions (fix: convert fully to numpy style, i.e. Args:Parameters\n----------, not truncate — full structured docs are required for public interfaces):

  • lading/commands/bump_toml.pyupdate_section (Lines 128–144).
  • lading/commands/bump_toml.pyupdate_dependency_sections (Lines 156–171).

I assigned it to you. Let me know if you'd like the fix for these applied in this PR instead of deferred to the new issue.

@leynos
leynos merged commit 447ef12 into main Jul 8, 2026
6 checks passed
@leynos
leynos deleted the issue-97-bump-context-single-derivation branch July 8, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bump._update_crate_manifest recomputes per-crate state (O(n²)) + dead context field

2 participants