Skip to content

Aggregate lockfile regeneration failures in bump (#84) - #127

Merged
leynos merged 10 commits into
mainfrom
issue-84-aggregate-lockfile-refresh-failures
Jul 8, 2026
Merged

Aggregate lockfile regeneration failures in bump (#84)#127
leynos merged 10 commits into
mainfrom
issue-84-aggregate-lockfile-refresh-failures

Conversation

@leynos

@leynos leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #84

  • regenerate_lockfiles previously aborted on the first cargo failure, leaving the workspace inconsistent (manifests carry the new version; later lockfiles never attempted) with no inventory of what needs repair.
  • Every manifest is now attempted; failures are collected and one aggregated LockfileRegenerationError is raised after the loop. The message states the workspace is inconsistent and lists each failed manifest with its repair command (cargo update --workspace --manifest-path …).
  • The exception chains from the first underlying failure so diagnostics (e.g. a missing cargo executable) survive aggregation. Manifest-path validation errors still raise immediately — those are configuration mistakes, not partial-update hazards.
  • The regenerate_lockfiles docstring documents the partial-update semantics.

Testing

  • Mid-list failure: remaining manifests still attempted; successes excluded from the repair list.
  • Multi-failure aggregation: exactly one repair command per failed manifest; syrupy snapshot of the aggregated message (workspace root normalised).
  • Existing cause-preservation test still passes.
  • make check-fmt, make lint, make typecheck, and make test (563 passed) all green after rebasing onto current main.
  • coderabbit review --agent: 0 findings.

🤖 Generated with Claude Code

Summary by Sourcery

Aggregate Cargo lockfile regeneration failures so all manifests are attempted and a single error reports required repairs.

Enhancements:

  • Regenerate lockfiles for every manifest, aggregating per-manifest failures into a single LockfileRegenerationError with operator-facing repair commands.
  • Preserve the original underlying error as the cause when reporting aggregated lockfile regeneration failures.
  • Clarify regenerate_lockfiles docstring to document non-atomic, aggregated failure semantics and operator guidance.

Tests:

  • Add tests to verify all manifests are attempted after a mid-list failure and that multiple failures are aggregated with one repair command per manifest, using snapshot testing for the aggregated message.

References

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

regenerate_lockfiles now attempts every manifest, collects all lockfile regeneration failures, and raises either the original failure or an aggregated LockfileRegenerationError. Tests, docs, a snapshot, and .gitignore are updated alongside the control-flow change.

Changes

Lockfile regeneration aggregation

Layer / File(s) Summary
Aggregated failure handling in regenerate_lockfiles
lading/commands/bump_lockfiles.py
regenerate_lockfiles now tries each manifest, records successes and failures, and formats multi-manifest failures with quoted repair commands through new helpers.
Tests and snapshot for aggregated failures
tests/unit/test_bump_lockfiles.py, tests/unit/__snapshots__/test_bump_lockfiles.ambr
Add selective failure scaffolding, workspace fixtures, continued-attempt coverage, aggregated failure assertions, and the updated snapshot output.
Documentation updates
docs/developers-guide.md, docs/users-guide.md
Document the new lockfile regeneration flow in both guides and update the developer note formatting.
Local state ignore rule
.gitignore
Ignore the .memdb/ local state directory.

Sequence Diagram(s)

sequenceDiagram
  participant regenerate_lockfiles
  participant _attempt_single_lockfile_update
  participant _run_workspace_lockfile_update
  participant _build_aggregate_failure_message

  loop each manifest
    regenerate_lockfiles->>_attempt_single_lockfile_update: attempt update
    _attempt_single_lockfile_update->>_run_workspace_lockfile_update: run cargo update
    _run_workspace_lockfile_update-->>_attempt_single_lockfile_update: success or error
    _attempt_single_lockfile_update-->>regenerate_lockfiles: path or failure
  end
  alt multiple failures
    regenerate_lockfiles->>_build_aggregate_failure_message: format aggregated message
    _build_aggregate_failure_message-->>regenerate_lockfiles: operator-facing details
  end
Loading

Possibly related PRs

  • leynos/lading#85: Track the same regenerate_lockfiles failure-handling path in bump_lockfiles.py.
  • leynos/lading#115: Touch the same lockfile regeneration message formatting in bump_lockfiles.py.

Suggested labels: Issue

Suggested reviewers: codescene-delta-analysis, codescene-access

Poem

One manifest, then two, then all the lot,
Failures gathered where before they shot.
Repair commands now line up neat and clear,
While lockfiles march on, manifest by manifest, here.
A tidy tale for Cargo’s bumpy track 🛠️


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Testing (Overall) ❌ Error The new tests cover order and aggregate messaging, but they never assert the aggregated error’s cause or the lone-root branch, so plausible regressions could still pass. Add explicit assertions for the aggregated error’s cause and a lone-root regression test proving failures are re-raised unchanged rather than wrapped.
Out of Scope Changes check ⚠️ Warning The PR includes an unrelated .gitignore change adding .memdb/, which is outside the lockfile fix scope. Move the .gitignore update into a separate PR or justify it in the linked issue if it is required here.
✅ Passed checks (18 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change and includes the linked issue reference (#84).
Description check ✅ Passed The description describes the lockfile-regeneration fix and test coverage, so it is on topic.
Linked Issues check ✅ Passed The PR attempts all manifests, aggregates failures, and surfaces repair commands, satisfying issue #84.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
User-Facing Documentation ✅ Passed docs/users-guide.md now explains non-atomic bump lockfile regeneration, aggregated failures, and recovery, and README signposts the user guide.
Developer Documentation ✅ Passed PASS: Document the new lockfile-regeneration flow in docs/developers-guide.md; the roadmap item remains checked off and no execplan file exists.
Module-Level Documentation ✅ Passed Both touched Python modules include top-level docstrings; the command module states its lading bump role, and the test module states it exercises lockfile regeneration after bump operations.
Testing (Unit And Behavioural) ✅ Passed Tests hit the public regenerate_lockfiles boundary and cover ordering, aggregation, and failure invariants without relying only on private helpers.
Testing (Property / Proof) ✅ Passed Hypothesis test exercises varied workspace/failure configs and asserts every manifest is attempted and each failure is reported once.
Testing (Compile-Time / Ui) ✅ Passed Use the focused syrupy snapshot plus semantic assertions; no compile-time surface exists here, so no trybuild analogue is required.
Unit Architecture ✅ Passed Keep the command/query seam explicit: queries stay read-only, lockfile regeneration takes an injectable runner, and tests exercise the side-effect boundary.
Domain Architecture ✅ Passed Keep the change in the command layer; it documents and tests infrastructure behaviour, and no domain model or invariant is leaked.
Observability ✅ Passed PASS: regenerate_lockfiles logs start/end, per-manifest attempts and failures, and the aggregated error names each failed manifest with repair commands.
Security And Privacy ✅ Passed PASS: Validate manifest paths before use, quote them for repair commands, and expose no secrets or sensitive data in code, docs, or snapshots.
Performance And Resource Use ✅ Passed Keep the new lockfile loop linear and bounded by manifest count; no unbounded collections, repeated scans, or quadratic behaviour appears.
Concurrency And State ✅ Passed PASS: State is locally owned and sequential; regenerate_lockfiles documents partial-failure semantics and tests assert ordered attempts plus aggregated reporting.
Architectural Complexity And Maintainability ✅ Passed Keep the refactor local: it removes branching from regenerate_lockfiles and adds no public surface or extension mechanism.
Rust Compiler Lint Integrity ✅ Passed No Rust source files changed in the PR range; only docs, Python, snapshots, and .gitignore were touched, so no lint suppressions or clone issues were introduced.
✨ 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-84-aggregate-lockfile-refresh-failures

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

@sourcery-ai

sourcery-ai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements aggregated failure handling in regenerate_lockfiles so all manifests are attempted, failures are collected into a single LockfileRegenerationError with an operator-focused message, and adds tests (including snapshot tests) to verify the new behavior and messaging.

Flow diagram for aggregated failure handling in regenerate_lockfiles

flowchart TD
    A[Start regenerate_lockfiles] --> B[Resolve manifests]
    B --> C[Init lockfiles and failures]
    C --> D{For each manifest}
    D -->|Next manifest| E[Log regeneration start]
    E --> F[Call _run_workspace_lockfile_update]
    F --> G{LockfileRegenerationError?}
    G -->|No| H[Log success and add Cargo.lock to lockfiles]
    H --> D
    G -->|Yes| I[Log exception and append manifest error to failures]
    I --> D
    D -->|No more manifests| J{Any failures?}
    J -->|No| K[Log overall success and return lockfiles]
    J -->|Yes| L[Select first failure and its cause]
    L --> M[Call _build_aggregate_failure_message]
    M --> N[Raise LockfileRegenerationError with aggregated message chained from cause]
    N --> O[End]
    K --> O
Loading

File-Level Changes

Change Details Files
Change regenerate_lockfiles to attempt all manifests, collect per-manifest failures, and raise a single aggregated LockfileRegenerationError with a detailed repair message while preserving the original cause.
  • Extend regenerate_lockfiles docstring and Notes to describe partial-update semantics and new aggregated failure behavior.
  • Introduce a failures collection in regenerate_lockfiles, wrapping _run_workspace_lockfile_update in a try/except to log and record LockfileRegenerationError instances while continuing with remaining manifests.
  • After processing all manifests, if any failures occurred, build an aggregated message and raise a new LockfileRegenerationError chained from the first underlying failure or its cause.
  • Add _build_aggregate_failure_message helper that formats a header, lists each individual error, and appends a cargo update --workspace --manifest-path command using a shell-quoted manifest path.
lading/commands/bump_lockfiles.py
Add tests to verify that regenerate_lockfiles attempts all manifests after failures, aggregates multiple failures into a single message with one repair command per manifest, and that the human-facing error message is snapshot-tested with a normalized workspace root.
  • Refactor Path import in test_bump_lockfiles.py to use a direct from pathlib import Path and import SnapshotAssertion for type checking.
  • Add _selective_failure_runner helper to simulate cargo failures for selected manifest paths and record which manifests were attempted.
  • Add test_regenerate_lockfiles_attempts_all_manifests_after_failure to assert that a mid-list failure does not skip later manifests and that only failed manifests appear in the repair message.
  • Add test_regenerate_lockfiles_aggregates_multiple_failures to assert each failed manifest appears exactly once with its repair command and to snapshot the aggregated error message with the workspace root normalized.
  • Introduce a new snapshot file tests/unit/snapshots/test_bump_lockfiles.ambr to store the expected aggregated error message output.
tests/unit/test_bump_lockfiles.py
tests/unit/__snapshots__/test_bump_lockfiles.ambr

Assessment against linked issues

Issue Objective Addressed Explanation
#84 Ensure lockfile regeneration in bump attempts all configured manifests, aggregates any cargo failures into a single LockfileRegenerationError, and reports each failed manifest with a concrete repair command while indicating the workspace may be inconsistent.
#84 Document the non-atomic / partial-update semantics of lockfile regeneration, including that manifests may have the new version while some lockfiles remain stale and how the operator can repair them.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codescene-delta-analysis codescene-delta-analysis 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 Failed
Enforce critical code health rules (1 file with Bumpy Road Ahead)
Enforce advisory code health rules (1 file with Large Method)

Our agent can fix these. Install it.

Gates Passed
4 Quality Gates Passed

Reason for failure
Enforce critical code health rules Violations Code Health Impact
bump_lockfiles.py 1 critical rule 10.00 → 9.45 Suppress
Enforce advisory code health rules Violations Code Health Impact
bump_lockfiles.py 1 advisory rule 10.00 → 9.45 Suppress

See analysis details in CodeScene

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.

Comment thread lading/commands/bump_lockfiles.py Outdated
Comment thread lading/commands/bump_lockfiles.py Outdated
@leynos

leynos commented Jun 16, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response.

lading/commands/bump_lockfiles.py

Comment on lines +64 to +67

        not named ``Cargo.toml``), orafter every manifest has been
        attemptedif ``cargo update --workspace`` failed for one or more
        manifests. The aggregated message lists each failed manifest with a
        repair command.

❌ New issue: Large Method
regenerate_lockfiles has 71 lines, threshold = 70

@leynos

leynos commented Jun 16, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response.

lading/commands/bump_lockfiles.py

Comment on lines +64 to +67

        not named ``Cargo.toml``), orafter every manifest has been
        attemptedif ``cargo update --workspace`` failed for one or more
        manifests. The aggregated message lists each failed manifest with a
        repair command.

❌ New issue: Bumpy Road Ahead
regenerate_lockfiles has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is 2 blocks per function

@coderabbitai

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-84-aggregate-lockfile-refresh-failures branch from eae8151 to 9356db7 Compare July 7, 2026 18:55
codescene-access[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-84-aggregate-lockfile-refresh-failures branch from 9356db7 to 9aa5c72 Compare July 7, 2026 19:05
codescene-access[bot]

This comment was marked as outdated.

@pandalump
pandalump marked this pull request as ready for review July 7, 2026 19:06
@leynos

leynos commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph.

If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

lading/commands/bump_lockfiles.py

Comment on lines +65 to +70

        not named ``Cargo.toml``), orafter every manifest has been
        attemptedif ``cargo update --workspace`` failed. When only the
        workspace-root lockfile is regenerated, the original cargo error is
        re-raised unchanged. When several lockfiles are regenerated, one
        aggregated error is raised whose message lists each failed manifest
        with a repair command.

❌ New issue: Bumpy Road Ahead
regenerate_lockfiles has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is 2 blocks per function

@coderabbitai

This comment was marked as resolved.

codescene-access[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot added the Issue label Jul 7, 2026
codescene-access[bot]

This comment was marked as outdated.

@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: 4

🤖 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_lockfiles.py`:
- Around line 166-193: The return value of _attempt_single_lockfile_update is a
position-based union tuple that hides intent at call sites. Replace the tuple in
this function with a small self-documenting result type, such as a dataclass
result object with lockfile and error fields, and update the success/failure
returns to populate that type consistently. Then adjust any callers that unpack
the current tuple so they read the named fields instead.
- Around line 166-193: The private helper _attempt_single_lockfile_update has an
overlong multi-line docstring; trim it to a single-line summary only. Keep the
function’s purpose concise at the top of the docstring and remove the extended
returns/details text, leaving the fuller explanation for public APIs instead.

In `@tests/unit/test_bump_lockfiles.py`:
- Around line 299-354: Both new lockfile regeneration tests duplicate the same
`a`/`b` Cargo manifest directory setup, so extract that shared workspace
preparation into a reusable `pytest` fixture. Move the repeated creation of the
`a`, `b`, and root `Cargo.toml` files into a fixture (in `conftest.py` or a
shared fixtures module), then have
`test_regenerate_lockfiles_attempts_all_manifests_after_failure` and
`test_regenerate_lockfiles_aggregates_multiple_failures` depend on it instead of
building the layout inline.
- Around line 317-326: The new bare asserts in the unit tests should include
explicit failure messages so failures are self-explanatory. Update the key
checks in the affected test cases in test_bump_lockfiles.py to use assert ... ,
message instead of bare assert, especially around the attempted-manifest list
and the error-message content checks. Keep the assertions themselves the same,
but add short messages that identify what condition failed for easier debugging.
🪄 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: 7b184364-9252-456f-ad1f-567fba849adb

📥 Commits

Reviewing files that changed from the base of the PR and between 6bd4bd5 and bcbcf0c.

📒 Files selected for processing (5)
  • .gitignore
  • docs/developers-guide.md
  • lading/commands/bump_lockfiles.py
  • tests/unit/__snapshots__/test_bump_lockfiles.ambr
  • tests/unit/test_bump_lockfiles.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)

Comment thread lading/commands/bump_lockfiles.py
Comment thread tests/unit/test_bump_lockfiles.py Outdated
Comment thread tests/unit/test_bump_lockfiles.py Outdated
codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph.

If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

tests/unit/test_bump_lockfiles.py

Comment on lines +395 to +442

def test_regenerate_lockfiles_attempts_all_and_reports_each_failure_once(
    spec: tuple[bool, list[tuple[str, bool]]],
) -> None:
    """Every manifest is attempted and each failure is reported exactly once."""
    fail_root, crates = spec
    with tempfile.TemporaryDirectory() as tmp:
        root = Path(tmp)
        (root / "Cargo.toml").write_text("", encoding="utf-8")
        expected_order = [(root / "Cargo.toml").resolve()]
        failing: set[Path] = set()
        if fail_root:
            failing.add((root / "Cargo.toml").resolve())
        for name, should_fail in crates:
            (root / name).mkdir()
            (root / name / "Cargo.toml").write_text("", encoding="utf-8")
            manifest = (root / name / "Cargo.toml").resolve()
            expected_order.append(manifest)
            if should_fail:
                failing.add(manifest)

        runner, attempted = _selective_failure_runner(failing)
        configured = tuple(f"{name}/Cargo.toml" for name, _ in crates)

        if failing:
            with pytest.raises(bump_lockfiles.LockfileRegenerationError) as excinfo:
                bump_lockfiles.regenerate_lockfiles(root, configured, runner=runner)
            message = str(excinfo.value)
            repair_lines = message.count("cargo update --workspace --manifest-path")
            assert repair_lines == len(failing), (
                f"expected one repair line per failure; got {repair_lines} for "
                f"{len(failing)} failure(s): {message}"
            )
            for manifest in failing:
                assert shlex.quote(str(manifest)) in message, (
                    f"failed manifest {manifest} should appear in: {message}"
                )
        else:
            regenerated = bump_lockfiles.regenerate_lockfiles(
                root, configured, runner=runner
            )
            assert len(regenerated) == len(expected_order), (
                "all lockfiles should be regenerated when nothing fails"
            )

        assert attempted == expected_order, (
            f"every manifest should be attempted once, in order; "
            f"expected {expected_order}, got {attempted}"
        )

❌ New issue: Bumpy Road Ahead
test_regenerate_lockfiles_attempts_all_and_reports_each_failure_once has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is 2 blocks per function

leynos and others added 5 commits July 7, 2026 23:02
regenerate_lockfiles aborted on the first cargo failure, leaving the
workspace inconsistent with no inventory of which lockfiles were
refreshed: manifests carry the new version while later lockfiles were
never attempted.

Attempt every manifest, collect the failures, and raise one aggregated
LockfileRegenerationError after the loop. The message states that the
workspace is inconsistent and lists each failed manifest with its
repair command (cargo update --workspace --manifest-path ...). The
exception chains from the first underlying failure so diagnostics such
as a missing cargo executable survive aggregation. Manifest-path
validation errors still raise immediately, as those are configuration
mistakes rather than partial-update hazards.

Tests cover the mid-list failure (remaining manifests still attempted;
successes excluded from the repair list), multi-failure aggregation
with exactly one repair command per failed manifest, and a syrupy
snapshot of the aggregated message.

Closes #84
Reduce regenerate_lockfiles below the 70-line limit by moving the
per-manifest iteration into a new private _attempt_lockfile_updates
helper, placed immediately before _build_aggregate_failure_message.

The helper owns its lockfiles and failures lists and returns them as a
pair; regenerate_lockfiles now calls it in one line and keeps the
aggregation and raise logic unchanged. Behaviour is identical: every
manifest is still attempted and failures are aggregated into one
LockfileRegenerationError (issue #84).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The memtrace agent tooling writes .memdb/daemon.pid and
.memdb/daemon-state.json into the worktree. These are per-machine
runtime artifacts and must not be tracked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the whole-loop _attempt_lockfile_updates helper with a
per-manifest _attempt_single_lockfile_update, keeping the iteration loop
in regenerate_lockfiles. The helper logs, runs the cargo update, and
returns (lockfile, None) on success or (None, error) on failure, so the
loop stays flat: one call per manifest, then branch on the result.

The success branch narrows lockfile to Path via `elif lockfile is not
None`, so no type suppression is needed (ty does not honour mypy's
`# type: ignore[arg-type]`). Simplify the cause selection to
`failures[0][1].__cause__ or failures[0][1]`. Observable behaviour of
regenerate_lockfiles is unchanged: every manifest is attempted and
failures aggregate into one LockfileRegenerationError (issue #84).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The aggregate failure message exists to disambiguate which of several
regenerated lockfiles is now inconsistent and how to repair each. When
only the workspace-root lockfile is regenerated there is nothing to
disambiguate, so wrapping the lone cargo error in the aggregate header
plus repair list adds noise without information.

Re-raise the original LockfileRegenerationError unchanged when exactly
one manifest was processed; keep the aggregate path for multiple
manifests (issue #84 still attempts every manifest and lists each
failure with its repair command). This restores the canonical
single-line detail message that test_lockfile_regeneration_failure_message
pins, and needs no snapshot change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
leynos and others added 3 commits July 7, 2026 23:02
Add a Mermaid flowchart of regenerate_lockfiles to the Bump command
internals section, with a screen-reader description before it and an
accessible Figure 1 caption after, per the documentation style guide. A
following note records that a lone workspace-root failure re-raises the
plain cargo error rather than the aggregate message.

Normalize the one asterisk emphasis (_already-derived_) in the section to
underscore so the document is MD049-consistent with the new caption.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CodeScene flagged two nested-conditional blocks in regenerate_lockfiles.
Extract each into a private helper:

- _collect_lockfile_results attempts every manifest and partitions the
  outcomes into (lockfiles, failures).
- _raise_aggregated_failure raises the appropriate error — the plain
  cargo error for a lone workspace-root failure, or one aggregated error
  for several regenerated lockfiles. It is annotated typ.NoReturn since
  it is only reached when failures is non-empty.

regenerate_lockfiles now reads as a flat sequence: collect, branch on
failures, log, return. Behaviour is unchanged.

Use the repo's aliased typing import (typ.NoReturn); `from typing import
NoReturn` is banned by ruff ICN003.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Replace the position-based union tuple returned by
  _attempt_single_lockfile_update with a self-documenting
  _LockfileUpdateResult dataclass (lockfile/error fields); the caller now
  reads named fields. Trim the helper's docstring to a one-line summary.
- Extract the duplicated a/b workspace setup in the two aggregate tests
  into an `ab_workspace` fixture, and give the key assertions explicit
  failure messages.
- Add a Hypothesis property test over arbitrary manifest lists and
  failing subsets, asserting every manifest is attempted in order and each
  failure is reported exactly once.
- Document the attempt-all-and-aggregate bump behaviour and the
  operator-facing repair list in docs/users-guide.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@leynos
leynos force-pushed the issue-84-aggregate-lockfile-refresh-failures branch from 03bf3d9 to 3c4d65a Compare July 7, 2026 22:02
@coderabbitai

This comment was marked as resolved.

codescene-access[bot]

This comment was marked as outdated.

Extract the two nested blocks of the Hypothesis property test into
module-level helpers:

- _prepare_manifest_fixture creates the manifest files and returns the
  (expected_order, failing) pair.
- _assert_failure_message_reports_each_once checks each failing manifest
  has exactly one repair line.

The test body now reads as a flat arrange/act/assert. `fail_root` is
keyword-only on the helper to satisfy ruff FBT001 (no bool positional
args). Decorators, docstring, and the other tests are unchanged.

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

Copy link
Copy Markdown
Collaborator

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

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.

@pandalump

Copy link
Copy Markdown
Collaborator

@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: 4

🤖 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 `@docs/developers-guide.md`:
- Around line 167-205: Update the `regenerate_lockfiles` Mermaid flowchart to
include the single-manifest branch handled by `_raise_aggregated_failure`, since
it does not always go through `_build_aggregate_failure_message`. Add a separate
outcome from the failures path that shows the workspace-root-only case re-raises
the original cargo error unchanged, while keeping the existing
aggregated-failure branch for multi-manifest runs. Make sure the diagram in
`docs/developers-guide.md` matches the behavior described by
`_raise_aggregated_failure` and `_build_aggregate_failure_message`.

In `@docs/users-guide.md`:
- Around line 282-303: The new fenced example in the users guide uses the wrong
code-block language identifier; update the aggregated error-output example to
use plaintext instead of text. Locate the fenced block in the section describing
lading bump’s aggregated lockfile failures, and make it consistent with the
existing plaintext example elsewhere in the file so the markdown style guide is
followed.

In `@lading/commands/bump_lockfiles.py`:
- Around line 101-140: Trim the docstrings for _collect_lockfile_results and
_raise_aggregated_failure to single-line summaries only, matching the style
already used for _attempt_single_lockfile_update. Keep the brief purpose
statement in each helper, but remove the extended explanatory paragraphs so
these private functions follow the one-line docstring rule for internal helpers.

In `@tests/unit/test_bump_lockfiles.py`:
- Around line 276-327: The private helper `_prepare_manifest_fixture` has a
multi-line docstring that should be reduced to a single-line summary to match
the other private helpers in this test block. Update only the docstring in
`_prepare_manifest_fixture` so it stays brief and omits the separate Returns
section, keeping the style consistent with `_selective_failure_runner` and
`_assert_failure_message_reports_each_once`.
🪄 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: 8b1f4bfb-193d-466c-bd24-d4098bbc2c63

📥 Commits

Reviewing files that changed from the base of the PR and between 106e740 and b978c1c.

📒 Files selected for processing (6)
  • .gitignore
  • docs/developers-guide.md
  • docs/users-guide.md
  • lading/commands/bump_lockfiles.py
  • tests/unit/__snapshots__/test_bump_lockfiles.ambr
  • tests/unit/test_bump_lockfiles.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)

Comment thread docs/developers-guide.md
Comment thread docs/users-guide.md
Comment thread lading/commands/bump_lockfiles.py
Comment thread tests/unit/test_bump_lockfiles.py
- developers-guide.md: extend the regenerate_lockfiles flowchart with the
  single-manifest branch (workspace-root-only failure re-raises the plain
  cargo error) alongside the multi-manifest aggregate branch, matching
  _raise_aggregated_failure; update the screen-reader description to match.
- bump_lockfiles.py: trim _collect_lockfile_results and
  _raise_aggregated_failure docstrings to single-line summaries, matching
  the other private helpers.
- test_bump_lockfiles.py: trim _prepare_manifest_fixture's docstring;
  add test_regenerate_lockfiles_aggregate_chains_first_underlying_cause
  (asserts the aggregated error chains from the first failure's real
  __cause__) and test_regenerate_lockfiles_single_manifest_reraises_unchanged
  (proves a lone-root failure surfaces the plain error, not the aggregate).

The users-guide fenced-block finding was skipped: `text` is the file's
dominant convention (3 blocks vs a single `plaintext` outlier), so the
block already matches; switching to `plaintext` would reduce consistency.

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

leynos commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following now been resolved?

Check name Status Explanation Resolution
Testing (Overall) ❌ Error The new tests cover order and aggregate messaging, but they never assert the aggregated error’s cause or the lone-root branch, so plausible regressions could still pass. Add explicit assertions for the aggregated error’s cause and a lone-root regression test proving failures are re-raised unchanged rather than wrapped.

@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
fd test_bump_lockfiles.py
echo "---"
rg -n "cause|__cause__|lone|single.*root|root.*single|re-?raise" tests/unit/test_bump_lockfiles.py -C2
echo "---helper/aggregate function---"
rg -n "def _raise_aggregated_failure|def regenerate_lockfiles|def _collect_lockfile_results|def _attempt_single_lockfile_update|raise .* from" lading/commands/bump_lockfiles.py -A15

Length of output: 2312


🏁 Script executed:

#!/bin/bash
fd test_bump_lockfiles.py
echo "---"
rg -n "cause|__cause__|lone|single.*root|root.*single|re-?raise" tests/unit/test_bump_lockfiles.py -C2
echo "---helper/aggregate function---"
rg -n "def _raise_aggregated_failure|def regenerate_lockfiles|def _collect_lockfile_results|def _attempt_single_lockfile_update|raise .* from" lading/commands/bump_lockfiles.py -A15

Length of output: 5606


@leynos, confirm both gaps are closed.

  • Aggregated error cause: test_regenerate_lockfiles_aggregate_chains_first_underlying_cause (Line 415) asserts excinfo.value.__cause__ is boom, proving the aggregate chains to the first failure's underlying cause rather than the wrapper.
  • Lone-root branch: test_regenerate_lockfiles_single_manifest_reraises_unchanged (Line 445) proves a single workspace-root failure re-raises the original LockfileRegenerationError unchanged, matching _raise_aggregated_failure's if len(manifests) == 1: raise failures[0][1] branch.

Both match the implementation in _raise_aggregated_failure (Lines 120–130). Mark this check resolved.

(=^・ω・^=) ノ⌒●~*

@leynos

leynos commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following now been resolved?

Check name Status Explanation Resolution
Testing (Overall) ❌ Error The new tests cover order and aggregate messaging, but they never assert the aggregated error’s cause or the lone-root branch, so plausible regressions could still pass. Add explicit assertions for the aggregated error’s cause and a lone-root regression test proving failures are re-raised unchanged rather than wrapped.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Tip

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

@leynos: Confirmed, yes — both gaps remain closed as verified previously.

  • Aggregated error cause: test_regenerate_lockfiles_aggregate_chains_first_underlying_cause asserts excinfo.value.__cause__ is boom, proving the aggregate chains to the first failure's underlying cause.
  • Lone-root branch: test_regenerate_lockfiles_single_manifest_reraises_unchanged proves a single workspace-root failure re-raises the original LockfileRegenerationError unchanged.

Mark this check resolved; nothing further outstanding on this item.

(=^・ω・^=)ノ

@leynos
leynos merged commit a70a881 into main Jul 8, 2026
6 checks passed
@leynos
leynos deleted the issue-84-aggregate-lockfile-refresh-failures 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.

Handle workspace inconsistency when lockfile refresh fails after manifest rewrites in bump

2 participants