Skip to content

Abstract bump lockfile operations behind a repository port (#82) - #135

Merged
leynos merged 9 commits into
mainfrom
issue-82-bump-repository-port
Jul 13, 2026
Merged

Abstract bump lockfile operations behind a repository port (#82)#135
leynos merged 9 commits into
mainfrom
issue-82-bump-repository-port

Conversation

@leynos

@leynos leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #82

  • BumpOptions, a public domain dataclass, no longer exposes a CommandRunner field. It now carries lockfile_repository, a bump_lockfiles.LockfileRepository protocol.
  • LockfileRepository is the port through which the bump domain projects (resolve_lockfile_paths) and regenerates (regenerate_lockfiles) Cargo lockfiles; CargoLockfileRepository is the cargo-backed adapter bound to a command runner.
  • The CLI binds the adapter to its selected runner at the composition root; bump falls back to the default adapter when no repository is injected.
  • Publish-side lockfile discovery and freshness validation in lockfile.py now go through a sibling LockfileInspectionRepository port, with CargoLockfileInspectionRepository as the git/cargo-backed adapter. The publish pre-flight domain (_validate_lockfile_freshness, _collect_stale_lockfiles) depends only on the port; publish_preflight._run_preflight_checks is the composition root that binds the adapter to the selected runner and pre-flight environment, and tests inject a port double at the _validate_lockfile_freshness seam. This fully closes Remove infrastructure _CommandRunner from public BumpOptions and abstract VCS/filesystem behind a repository port #82: neither the bump nor the publish lockfile domain holds a raw CommandRunner.

Testing

  • New bump tests inject a recording repository through bump.run and verify live runs regenerate while dry runs only project, without touching Cargo.
  • CLI test updated to assert the bump adapter is bound to the selected runner.
  • Publish-side pre-flight validation tests rewritten to inject a recording LockfileInspectionRepository double; new adapter tests cover env binding, delegation, and the manifest_exists predicate.
  • make check-fmt, make lint, make typecheck, make test (701 passed), make markdownlint, and make nixie all green.

🤖 Generated with Claude Code

Summary by Sourcery

Introduce a lockfile repository port for bump operations to decouple lockfile handling from direct command runner usage and wire it through the CLI and tests.

New Features:

  • Add a LockfileRepository protocol and a CargoLockfileRepository adapter to abstract lockfile projection and regeneration behind a repository port.

Enhancements:

  • Update BumpOptions to depend on a lockfile_repository instead of a raw command runner and adjust bump processing to use the repository for both dry runs and live lockfile regeneration.
  • Wire the CLI bump command to construct and inject a CargoLockfileRepository bound to the selected command runner.
  • Export bump_lockfiles from the commands package and document the new lockfile repository abstraction and its scope in the developer guide.

Tests:

  • Add recording lockfile repository tests to verify that bump.run uses the injected repository and that dry runs only project lockfile paths without regenerating them.
  • Update CLI tests to assert that the CargoLockfileRepository adapter is bound to the CLI's subprocess runner.

References

@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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2cba09f0-5c2d-4a5c-9f54-7f92ce7c1e69

📥 Commits

Reviewing files that changed from the base of the PR and between 7c3e81b and 2ac681f.

📒 Files selected for processing (1)
  • tests/unit/test_lockfile.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)

Summary

  • Replaced public CommandRunner injection in BumpOptions with the LockfileRepository port and Cargo adapter, bound by the CLI.
  • Added the publish-side LockfileInspectionRepository port and Cargo adapter for lockfile discovery and freshness validation.
  • Updated bump and publish workflows, tests, and documentation to use repository injection and keep infrastructure concerns outside domain logic.
  • Added adapter and repository-routing tests, including runner environment and echo_stdout forwarding coverage.
  • All formatting, lint, type-check, test, markdownlint, and nixie checks pass.

References: issue #82; updated docs/lading-design.md and docs/developers-guide.md.

Walkthrough

Replace direct CommandRunner plumbing with lockfile repository ports for bump and publish pre-flight. Wire Cargo-backed adapters through the CLI and command modules, then update documentation and unit tests for repository-based flows.

Changes

Lockfile repository ports and bump wiring

Layer / File(s) Summary
Bump port and wiring
lading/commands/bump_lockfiles.py, lading/commands/bump.py, lading/commands/__init__.py, lading/cli.py, docs/lading-design.md, docs/developers-guide.md
Add LockfileRepository and CargoLockfileRepository, replace BumpOptions.command_runner with lockfile_repository, route lockfile handling through repository methods, export the submodule, and document the wiring.
Bump repository validation
tests/unit/test_bump_lockfile_repository.py, tests/unit/test_cli.py
Verify dry-run and regeneration routing through a recording repository and validate CLI adapter construction.

Publish lockfile inspection

Layer / File(s) Summary
Publish inspection port and wiring
lading/commands/lockfile.py, lading/commands/publish_preflight.py, docs/lading-design.md, docs/developers-guide.md
Add LockfileInspectionRepository and CargoLockfileInspectionRepository, then route publish pre-flight discovery and freshness checks through the repository.
Publish inspection validation
tests/unit/publish/test_preflight_lockfile_validation.py, tests/unit/test_lockfile.py
Test repository-based pre-flight validation and adapter environment, manifest, command, and freshness behaviour.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant bump as bump.run
  participant repository as CargoLockfileRepository
  participant runner as CommandRunner
  CLI->>bump: BumpOptions(lockfile_repository)
  bump->>repository: resolve_lockfile_paths() or regenerate_lockfiles()
  repository->>runner: Cargo command
  runner-->>repository: command result
  repository-->>bump: lockfile paths
Loading
sequenceDiagram
  participant preflight as publish_preflight
  participant repository as CargoLockfileInspectionRepository
  participant runner as CommandRunner
  preflight->>repository: discover_tracked_lockfiles()
  repository->>runner: git command
  runner-->>repository: tracked paths
  preflight->>repository: validate_lockfile_freshness()
  repository->>runner: cargo metadata
  runner-->>repository: freshness result
  repository-->>preflight: LockfileFreshness
Loading

Possibly related PRs

  • leynos/lading#85: Shares the bump lockfile regeneration path wrapped by the new repository.
  • leynos/lading#127: Changes bump lockfile regeneration behaviour dispatched through the repository.
  • leynos/lading#130: Exercises the bump lockfile regeneration path refactored behind the repository port.

Suggested labels: Issue

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

Poem

A runner steps aside,
Ports guide locks with pride.
Cargo hums, tests record,
Freshness checks stay on board.

🚥 Pre-merge checks | ✅ 18 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
User-Facing Documentation ⚠️ Warning FAIL: docs/users-guide covers the CLI workflow, but the PR breaks the public BumpOptions API and adds no migration note or user-guide update. Add an n+1 migration note for the BumpOptions API break, and update docs/users-guide.md only if any CLI behaviour actually changed.
Testing (Unit And Behavioural) ⚠️ Warning Publish coverage stays at private seams: the new tests hit _validate_lockfile_freshness via recording doubles, but no behavioural test drives _run_preflight_checks/publish.run through the new... Add one publish workflow test at the _run_preflight_checks or publish.run boundary that asserts lockfile discovery/freshness uses the adapter, and keep the unit seam tests.
✅ Passed checks (18 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the lockfile repository abstraction and includes the linked issue number.
Description check ✅ Passed The description matches the PR changes and stays on topic throughout.
Linked Issues check ✅ Passed The PR removes raw runner exposure from BumpOptions and introduces repository ports for bump and publish lockfile handling, satisfying #82.
Out of Scope Changes check ✅ Passed The changes stay within the lockfile abstraction work; the docs, exports, and tests support the same objective.
Docstring Coverage ✅ Passed Docstring coverage is 88.37% which is sufficient. The required threshold is 80.00%.
Testing (Overall) ✅ Passed Accept these tests: they exercise real behaviour with recorded calls, filesystem fixtures, and a stale-lockfile snapshot, and they would fail on plausible regressions.
Developer Documentation ✅ Passed PASS: Document the new lockfile repositories in docs/developers-guide.md and docs/lading-design.md; roadmap items are already checked off, and no execplan or sibling locales exist.
Module-Level Documentation ✅ Passed PASS: Every touched Python module carries a module docstring, and the updated docs describe each module’s role and links to CLI, bump, and publish components.
Testing (Property / Proof) ✅ Passed PASS: This PR adds port/adaptor wiring only; the substantive lockfile invariants already have Hypothesis coverage, so no new property/proof test is warranted.
Testing (Compile-Time / Ui) ✅ Passed No compile-time path exists; the stale-lockfile message is covered by a focused snapshot with stable paths and extra semantic assertions.
Unit Architecture ✅ Passed PASS: Bump and publish lockfile work now use narrow injectable repository ports; command/query paths are explicit, and tests cover injected doubles and CLI binding.
Domain Architecture ✅ Passed BumpOptions now depends on a lockfile repository port, the CLI binds the adapter at the composition root, and publish pre-flight uses a separate inspection port.
Observability ✅ Passed PASS: the new ports only wrap existing helpers; lockfile.py and bump_lockfiles.py still emit info/error logs and bounded lockfile.* metrics at discovery, validation, and regeneration boundaries.
Security And Privacy ✅ Passed No secrets or auth gaps were introduced; the new ports keep shell calls parameterised, and subprocess logging redacts env overrides.
Performance And Resource Use ✅ Passed PASS: The new loops stay linear in lockfile/manifest count, I/O remains one git scan plus one cargo probe per item, and no unbounded collections or material extra cloning appear.
Concurrency And State ✅ Passed PASS: The PR adds only frozen, per-call repository adapters and local state; it introduces no threads, async tasks, locks, or shared mutable globals, and tests stay single-threaded.
Architectural Complexity And Maintainability ✅ Passed The new ports isolate real runner/git/cargo seams, are wired explicitly at the CLI and preflight composition roots, and the docs/tests show immediate use with no cyclic deps.
Rust Compiler Lint Integrity ✅ Passed No Rust sources, Cargo files, or lint suppressions changed in the branch diff; the check is not applicable.
✨ 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-82-bump-repository-port

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

Refactors bump lockfile handling to depend on a LockfileRepository port instead of a raw CommandRunner, introduces a Cargo-backed repository adapter, wires the CLI and bump options to use it, and adds tests and docs covering the new abstraction and dry-run behavior.

Sequence diagram for bump lockfile processing via LockfileRepository

sequenceDiagram
    actor User
    participant CLI as lading.cli.bump
    participant Bump as bump.run
    participant Process as _process_lockfiles
    participant Repo as CargoLockfileRepository

    User->>CLI: invoke bump(...)
    CLI->>Repo: CargoLockfileRepository(runner=command_runner)
    CLI->>Bump: run(options.with(lockfile_repository=Repo))
    Bump->>Process: _process_lockfiles(context)
    alt dry_run
        Process->>Repo: resolve_lockfile_paths(root_path, lockfile_manifests)
        Repo-->>Process: lockfile_paths
    else live_run
        Process->>Repo: regenerate_lockfiles(root_path, lockfile_manifests)
        Repo-->>Process: rewritten_lockfile_paths
    end
    Process-->>Bump: lockfile_paths
    Bump-->>CLI: BumpChanges
Loading

File-Level Changes

Change Details Files
Introduce a LockfileRepository abstraction and Cargo-backed adapter for lockfile operations, decoupling bump from CommandRunner.
  • Define LockfileRepository protocol with methods to resolve lockfile paths and regenerate lockfiles.
  • Add CargoLockfileRepository dataclass that delegates to existing resolve_lockfile_paths/regenerate_lockfiles functions and optionally binds a CommandRunner.
  • Export bump_lockfiles from lading.commands to make the new types available to callers.
lading/commands/bump_lockfiles.py
lading/commands/__init__.py
Change BumpOptions and bump workflow to use LockfileRepository instead of CommandRunner and route dry-run vs live lockfile behavior through the repository.
  • Replace BumpOptions.command_runner with lockfile_repository field and update its docstring to describe the new port.
  • Propagate lockfile_repository through _initialize_bump_context into the bump context.
  • Refactor _process_lockfiles to select an injected repository or default CargoLockfileRepository, and to call resolve_lockfile_paths on dry runs and regenerate_lockfiles on live runs.
lading/commands/bump.py
Wire the CLI to construct and inject a CargoLockfileRepository bound to the selected command runner.
  • Update CLI bump command to pass a CargoLockfileRepository(runner=command_runner) into BumpOptions instead of passing command_runner directly.
  • Adjust CLI tests to assert that the injected repository is a CargoLockfileRepository wired to cli.subprocess_runner.
lading/cli.py
tests/unit/test_cli.py
Add tests around the new lockfile repository port to validate injected behavior for live and dry runs without invoking Cargo.
  • Introduce _RecordingLockfileRepository test double that records resolve/regenerate calls and returns a dummy Cargo.lock path.
  • Add integration tests to verify bump.run uses the injected repository and that dry runs only resolve lockfile paths without regenerating.
tests/unit/test_bump_command_integration.py
Update developer documentation to describe the new lockfile repository port, its scope, and interaction with CLI and tests.
  • Revise developers-guide BumpOptions section to document lockfile_repository, CargoLockfileRepository, and the separation from publish-side CommandRunner usage.
  • Clarify that the port covers bump-side projection/regeneration only and that publish still works directly with CommandRunner in lockfile.py.
docs/developers-guide.md

Assessment against linked issues

Issue Objective Addressed Explanation
#82 Remove the CommandRunner/runner infrastructure field from the public BumpOptions API and instead expose a lockfile-focused port for bump-side lockfile operations.
#82 Introduce a LockfileRepository-style abstraction that encapsulates lockfile projection and regeneration, with a Cargo-backed adapter bound to a CommandRunner at the composition root (e.g., CLI), and update bump domain code, callers, and tests to use this port.
#82 Refactor lockfile discovery/validation logic (including the git VCS + filesystem mixing) in lockfile.py to go through a repository/VCS port instead of using CommandRunner directly, fully separating VCS/filesystem concerns from domain logic there as well. The diff does not modify lockfile.py or introduce a VCS/repository port for publish-side discovery/validation. The new LockfileRepository and CargoLockfileRepository are scoped to bump-side lockfile projection and regeneration only, and the updated documentation explicitly states that publish-side discovery/validation continues to take a CommandRunner directly in lockfile.py.

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 Low Cohesion)

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
test_bump_command_integration.py 1 critical rule 10.00 → 8.03 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 tests/unit/test_bump_command_integration.py Outdated
@lodyai
lodyai Bot force-pushed the issue-82-bump-repository-port branch from cc47439 to bbb546b Compare June 16, 2026 19:09
@leynos
leynos force-pushed the issue-82-bump-repository-port branch from bbb546b to 30933bf Compare July 8, 2026 17:56
codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos force-pushed the issue-82-bump-repository-port branch from 30933bf to a078db3 Compare July 8, 2026 20:27
@leynos

leynos commented Jul 8, 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_command_integration.py

Comment on file

from __future__ import annotations

import collections.abc as cabc

❌ New issue: Low Cohesion
This module has at least 15 different responsibilities amongst its 24 functions, threshold = 4

codescene-access[bot]

This comment was marked as outdated.

@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.

@lodyai
lodyai Bot force-pushed the issue-82-bump-repository-port branch from d7a7786 to b2a7d9a Compare July 8, 2026 21:25
@pandalump
pandalump marked this pull request as ready for review July 8, 2026 21:26
codescene-access[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-82-bump-repository-port branch from b2a7d9a to 9774d03 Compare July 9, 2026 08:53
codescene-access[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot added the Issue label Jul 9, 2026

@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

🤖 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 213-224: The new issue callout in the developers guide uses bare
“issue 82” text instead of the document’s existing GitHub-linked convention.
Update the prose around lading.commands.bump.run and the lockfile_repository /
bump_lockfiles.LockfileRepository discussion to use the same issue-reference
style as the rest of the file (for example, `#82` formatting) wherever that
reference appears, including the parenthetical at the end.

In `@lading/commands/bump_lockfiles.py`:
- Around line 240-287: The public contract on LockfileRepository and
CargoLockfileRepository is under-documented: their method docstrings only give
brief summaries and do not describe inputs, outputs, or the fact that
regenerate_lockfiles can raise LockfileRegenerationError via the delegated
regenerate_lockfiles function. Expand the docstrings on resolve_lockfile_paths
and regenerate_lockfiles in both the Protocol and CargoLockfileRepository to
include clear Parameters/Returns/Raises-style details, especially the error
behavior, so callers can rely on the port without inspecting the implementation.

In `@lading/commands/lockfile.py`:
- Around line 266-286: The environment-bound wrapper in `_bound_runner` is
ignoring the caller’s `echo_stdout` argument whenever it returns
`runner_with_env`, so preserve and forward that parameter instead of discarding
it. Update the `runner_with_env` closure to accept `echo_stdout` and pass it
through to `base_runner`, while still applying `base_env` only when `env` is not
provided.

In `@tests/unit/test_bump_lockfile_repository.py`:
- Line 1: The module docstring in test_bump_lockfile_repository is triggering an
E501 suppression without justification; either shorten the docstring so the line
no longer exceeds the limit or replace the bare # noqa: E501 on that docstring
with an inline reason. Update the top-level docstring in this test module so the
suppression, if kept, includes a clear justification rather than only the code.

In `@tests/unit/test_lockfile.py`:
- Around line 440-459: Consolidate the new _recording_runner test double with
the existing CallTrackingRunner instead of keeping two near-identical helpers.
Update CallTrackingRunner in tests/unit/publish/conftest.py to also capture env
if needed, then refactor the lockfile tests to use that shared runner for
command, cwd, and env recording. Keep the behavior of _recording_runner callers
intact while removing the duplicated ad-hoc implementation.
🪄 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: 21c9d48e-6912-4cb8-9dbd-2a1f2fee9006

📥 Commits

Reviewing files that changed from the base of the PR and between 2221d2a and 9774d03.

📒 Files selected for processing (11)
  • docs/developers-guide.md
  • lading/cli.py
  • lading/commands/__init__.py
  • lading/commands/bump.py
  • lading/commands/bump_lockfiles.py
  • lading/commands/lockfile.py
  • lading/commands/publish_preflight.py
  • tests/unit/publish/test_preflight_lockfile_validation.py
  • tests/unit/test_bump_lockfile_repository.py
  • tests/unit/test_cli.py
  • tests/unit/test_lockfile.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 Outdated
Comment thread lading/commands/bump_lockfiles.py Outdated
Comment thread lading/commands/lockfile.py
Comment thread tests/unit/test_bump_lockfile_repository.py Outdated
Comment thread tests/unit/test_lockfile.py
codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Jul 9, 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 (1 error, 1 warning)

Check name Status Explanation Resolution
Testing (Overall) ❌ Error The new tests miss a plausible regression: CargoLockfileInspectionRepository._bound_runner() drops echo_stdout, and no test would fail if that flag were ignored. Add a test that drives an env-bound inspection repository with echo_stdout=False, capture the kwarg in the runner double, and assert it is forwarded unchanged.
Developer Documentation ⚠️ Warning Update the guide, but also record the new lockfile ports in the design doc; the current design docs omit them and the guide still says 'issue 82'. Add the lockfile repository abstraction to docs/lading-design.md or an ADR, and change bare 'issue 82' mentions in docs/developers-guide.md to '#82'.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
git fetch origin pull/135/head:pr-135 2>&1 | tail -5
git checkout pr-135 2>&1 | tail -5

echo "---_bound_runner---"
rg -n "_bound_runner" lading/commands/lockfile.py -A 20

echo "---echo_stdout in lockfile.py---"
rg -n "echo_stdout" lading/commands/lockfile.py

echo "---echo_stdout in tests---"
rg -n "echo_stdout" tests/unit/test_lockfile.py

echo "---design doc check---"
fd -i "design" docs
rg -n "LockfileRepository|LockfileInspectionRepository|lockfile" docs/lading-design.md 2>/dev/null

echo "---issue 82 mentions---"
rg -n "issue 82|`#82`" docs/developers-guide.md

echo "---git log recent---"
git log --oneline -10

Length of output: 2574


🏁 Script executed:

#!/bin/bash
echo "---_bound_runner---"
rg -n "_bound_runner" lading/commands/lockfile.py -A 20

echo "---echo_stdout in lockfile.py---"
rg -n "echo_stdout" lading/commands/lockfile.py

echo "---echo_stdout in tests---"
rg -n "echo_stdout" tests/unit/test_lockfile.py

echo "---design doc files---"
fd -i "design" docs

echo "---design doc lockfile mentions---"
rg -n "LockfileRepository|LockfileInspectionRepository|lockfile" docs/lading-design.md 2>/dev/null

echo "---issue 82 mentions in developers-guide---"
rg -n "issue 82|`#82`" docs/developers-guide.md

echo "---git log recent---"
git log --oneline -10

Length of output: 4932


@leynos Confirm: both failed checks are resolved on pr-135 (commit 5cf208b).

Testing (Overall) — resolved. tests/unit/test_lockfile.py now contains test_adapter_bound_runner_forwards_echo_stdout (Line 531), which drives the bound runner with echo_stdout=False and asserts the recording double captures echo_stdout is False (Line 549). This closes the regression gap: _bound_runner's runner_with_env wrapper (Line 266) forwards **runner_kwargs unchanged, and the new test now proves it.

Developer Documentation — resolved. docs/lading-design.md now documents both ports: the bump-side LockfileRepository port and CargoLockfileRepository adapter (Lines 381–387), and the publish-side LockfileInspectionRepository port and CargoLockfileInspectionRepository adapter (Lines 495–506), including the domain helpers that depend on the port. docs/developers-guide.md now uses the #82 GitHub-issue-link form throughout (Lines 215, 224, 335, 347) — no bare "issue 82" mentions remain.

No further action is required for either check. Do not open a follow-up issue; there is no remaining scope gap here.

leynos and others added 4 commits July 9, 2026 12:36
BumpOptions, a public domain dataclass, exposed a CommandRunner field,
leaking execution infrastructure into the domain boundary.

Introduce the LockfileRepository protocol in bump_lockfiles as the
port through which the bump domain projects and regenerates Cargo
lockfiles, with CargoLockfileRepository as the cargo-backed adapter
bound to a command runner. BumpOptions now carries
lockfile_repository instead of command_runner; the CLI binds the
adapter to its selected runner at the composition root, and bump
falls back to the default adapter when no repository is injected.

The port's documented scope is bump-side lockfile projection and
regeneration; publish-side discovery and validation continue to take
a CommandRunner directly. Tests inject a recording repository to
verify live runs regenerate and dry runs only project, without
touching Cargo.

Closes #82
Extend the issue #82 repository/VCS port abstraction to the publish
side. Previously `lockfile.py` discovery and freshness validation, and
the `publish_preflight` domain that drives them, took a `CommandRunner`
directly, mixing git VCS, filesystem, and cargo execution with the
freshness-classification logic.

Add a `LockfileInspectionRepository` port and a git/cargo-backed
`CargoLockfileInspectionRepository` adapter (binding a runner and the
optional pre-flight environment). `_validate_lockfile_freshness` and
`_collect_stale_lockfiles` now depend only on the port;
`_run_preflight_checks` is the composition root that binds the adapter,
and tests inject a port double at the `_validate_lockfile_freshness`
seam. This is the publish-side counterpart to the bump-side
`bump_lockfiles.LockfileRepository`, so neither lockfile domain holds a
raw command runner.

Rewrite the pre-flight validation tests to inject a recording port
double and add adapter tests covering env binding, delegation, and the
`manifest_exists` predicate. Update the developer guide to document the
publish-side port and drop the bump-side-only scope caveat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the CodeScene Low Cohesion finding on
`tests/unit/test_bump_lockfile_rebuild.py` by moving the injected-port
integration tests into a focused module. `_RecordingLockfileRepository`,
`test_run_uses_injected_lockfile_repository`, and
`test_dry_run_projects_through_lockfile_repository` move verbatim into the
new `tests/unit/test_bump_lockfile_repository.py`; the original file keeps
the monkeypatch-based rebuild scenarios and `_LockfileSkipScenario`.

Imports are trimmed to what each file needs: the new module imports only
`cabc`, `pathlib`, `bump`, and the workspace builders, and the original
drops the now-unused `collections.abc` import. No production code changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve reviewer findings on the issue #82 lockfile port work:

- lockfile.py: `CargoLockfileInspectionRepository._bound_runner` now
  forwards `echo_stdout` (and any other runner keyword) to the bound
  runner instead of discarding it; only `env` is defaulted to the bound
  base environment. Implemented as a transparent `**kwargs` proxy so
  callers that omit `echo_stdout` leave the underlying runner's default
  intact and existing runner doubles need no change.
- test_lockfile.py: `_recording_runner` now records `echo_stdout`; adds
  `test_adapter_bound_runner_forwards_echo_stdout`, which drives the
  env-bound runner with `echo_stdout=False` and asserts it is forwarded
  unchanged. A `_RecordedCall` type alias keeps the annotations tidy.
- bump_lockfiles.py: expand the `LockfileRepository` Protocol and
  `CargoLockfileRepository` adapter method docstrings to full NumPy-style
  Parameters/Returns/Raises, documenting the `LockfileRegenerationError`
  behaviour. (The port methods keep docstring-only bodies; no `...`.)
- test_bump_lockfile_repository.py: justify the module-docstring
  `# noqa: E501` (the one-line summary is fixed; wrapping trips D205/D209).
- docs/developers-guide.md: use the file's `issue #82` reference style.
- docs/lading-design.md: document the bump- and publish-side lockfile
  repository ports.

The "consolidate the recording runner doubles" suggestion is skipped:
the publish `CallTrackingRunner` (a class recording 2-tuples with env
discarded, in a non-importable publish conftest) and the lockfile
`_recording_runner` are materially different, and merging them would
churn three unrelated publish tests for no behavioural gain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lodyai
lodyai Bot force-pushed the issue-82-bump-repository-port branch from 5cf208b to 269f4e9 Compare July 9, 2026 10:39
codescene-access[bot]

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 9, 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.

codescene-access[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

- lockfile.py: expand the discover_tracked_lockfiles and
  validate_lockfile_freshness docstrings on both the
  CargoLockfileInspectionRepository adapter and the
  LockfileInspectionRepository protocol to full NumPy Parameters/Returns
  sections (protocol stubs stay docstring-only, no ellipsis).
- test_lockfile.py: attach failure messages to the bare asserts in the
  five CargoLockfileInspectionRepository adapter tests.

The suggestion to group the adapter tests under a test class is skipped:
the entire test suite is module-level functions (no test classes in any
file), so a lone class here would be an inconsistency island; CodeRabbit
itself rated it a poor tradeoff.

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

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

@leynos

leynos commented Jul 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following now been resolved?

tests/unit/test_lockfile.py (2)

468-566: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff
Group the new adapter tests inside a test class.
These four new functions sit at module scope rather than grouped in a class with test_-prefixed methods.
As per path instructions, "Group related tests using class with method names prefixed by test_."

🤖 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 @tests/unit/test_lockfile.py around lines 468 - 566, The new adapter tests
are still defined as module-level functions instead of being grouped under a
test class. Move the four test_adapter_* cases into a single class with
test_-prefixed methods so the related coverage stays organized, keeping the
existing assertions and helper usage around CargoLockfileInspectionRepository
and _bound_runner unchanged.

Source: Path instructions
468-566: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Attach messages to the new bare asserts.
Every new assertion (lines 481-483, 519, 543-544, 561-565) fires without a failure message. Add one so a red run tells you what broke without reaching for the traceback.
As per path instructions, "Use assert …, "message" over bare asserts."

🧪 Example fix for one assertion
-    assert result == (tmp_path / "Cargo.lock",)
-    assert calls == [
-        (("git", "ls-files", "**/Cargo.lock", "Cargo.lock"), tmp_path, base_env, True)
-    ]
+    assert result == (tmp_path / "Cargo.lock",), "expected discovered lockfile"
+    assert calls == [
+        (("git", "ls-files", "**/Cargo.lock", "Cargo.lock"), tmp_path, base_env, True)
+    ], "git ls-files call did not receive the bound env"
🤖 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 @tests/unit/test_lockfile.py around lines 468 - 566, The new tests in
test_adapter_discovers_lockfiles_binding_env,
test_adapter_validates_freshness_binding_env,
test_adapter_without_env_leaves_runner_env_untouched,
test_adapter_honours_injected_manifest_exists, and
test_adapter_bound_runner_forwards_echo_stdout use bare asserts that should
include failure messages. Update each assert in these test functions to use an
assert-with-message form so failures clearly state what expectation broke while
keeping the existing checks and symbols like discover_tracked_lockfiles,
validate_lockfile_freshness, _bound_runner, and manifest_exists easy to locate.

Source: Path instructions

lading/commands/lockfile.py (1)

266-276: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Flesh out method docstrings with full structured docs.
discover_tracked_lockfiles and validate_lockfile_freshness — on both the adapter and the LockfileInspectionRepository protocol — carry only single-line summaries. These are public interfaces; add Parameters/Returns sections in numpy style.
As per path instructions, "Docstrings must follow the numpy style guide... full structured docs for all public interfaces."
Also applies to: 304-318

🤖 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/lockfile.py around lines 266 - 276, The public docstrings
for discover_tracked_lockfiles and validate_lockfile_freshness are only one-line
summaries and need full NumPy-style structured documentation. Update both the
adapter methods and the LockfileInspectionRepository protocol definitions to
include Parameters and Returns sections, describing workspace_root,
manifest_path, and the returned tuple[Path, ...] or LockfileFreshness. Keep the
docs consistent with the existing symbols discover_tracked_lockfiles,
validate_lockfile_freshness, and LockfileInspectionRepository.

Source: Path instructions

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
git fetch origin pull/135/head:pr-135 2>&1 | tail -5
git checkout pr-135 2>&1 | tail -5
git log --oneline -5

echo "---class grouping check---"
rg -n "^class |^def test_adapter" tests/unit/test_lockfile.py

echo "---bare asserts around adapter tests---"
sed -n '460,570p' tests/unit/test_lockfile.py

echo "---lockfile.py docstrings---"
sed -n '255,320p' lading/commands/lockfile.py

Length of output: 8656


@leynos, checked against commit 5a95b0e on pr-135.

Bare asserts — resolved. Every assertion in test_adapter_discovers_lockfiles_binding_env, test_adapter_validates_freshness_binding_env, test_adapter_without_env_leaves_runner_env_untouched, test_adapter_honours_injected_manifest_exists, and test_adapter_bound_runner_forwards_echo_stdout now carries a failure message (e.g., "git ls-files should receive the bound env", "echo_stdout forwarded unchanged").

Docstrings — resolved. CargoLockfileInspectionRepository.discover_tracked_lockfiles and validate_lockfile_freshness now carry full numpy-style docstrings with Parameters and Returns sections.

Class grouping — not resolved. The five test_adapter_* functions (Lines 468, 487, 508, 522, 547) still sit at module scope rather than inside a test class. Given this finding was flagged as trivial and a "poor tradeoff" in the original review, leave it as-is unless you want it addressed. If you'd like it fixed anyway, here is a prompt:

In tests/unit/test_lockfile.py, group the five adapter-focused tests into a single class so related coverage stays organised.

1. Create a class `TestCargoLockfileInspectionRepositoryAdapter` (or similarly descriptive name) immediately preceding the current location of `test_adapter_discovers_lockfiles_binding_env`.
2. Move these five functions into the class as methods, renaming each to drop the module-level `test_adapter_` prefix redundancy only if it improves readability, otherwise keep names unchanged and prefix each method with `self` as the first parameter:
   - `test_adapter_discovers_lockfiles_binding_env`
   - `test_adapter_validates_freshness_binding_env`
   - `test_adapter_without_env_leaves_runner_env_untouched`
   - `test_adapter_honours_injected_manifest_exists`
   - `test_adapter_bound_runner_forwards_echo_stdout`
3. Do not alter any assertions, helper usage (`_recording_runner`, `_RecordedCall`), or docstrings within the moved tests.
4. Leave `_recording_runner` and `_RecordedCall` at module scope since they are shared fixtures used elsewhere in the file.
5. Run `make check-fmt`, `make lint`, `make typecheck`, and `make test` and confirm all tests still pass.

[resolve]

Move the five test_adapter_* functions into a new
TestCargoLockfileInspectionRepositoryAdapter class as methods, keeping
their names, docstrings, assertions, and helper usage (_recording_runner,
_RecordedCall) unchanged. The _recording_runner factory and _RecordedCall
alias stay at module scope since they are shared across the file.

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

🤖 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_lockfile.py`:
- Around line 471-475: The three adapter tests repeat the same
Cargo.toml/Cargo.lock setup, so extract that shared manifest/lockfile
preparation into a pytest fixture instead of duplicating the write_text calls.
Add the fixture in the shared test setup area (for example conftest.py or a
fixtures module) and update test_adapter_discovers_lockfiles_binding_env,
test_adapter_without_env_leaves_runner_env_untouched, and
test_adapter_honours_injected_manifest_exists to use it while keeping each
test’s specific assertions intact.
🪄 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: 606682bc-b3f3-45ed-a7bc-e0e0ba3ba1f3

📥 Commits

Reviewing files that changed from the base of the PR and between 269f4e9 and 7c3e81b.

📒 Files selected for processing (2)
  • lading/commands/lockfile.py
  • tests/unit/test_lockfile.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 tests/unit/test_lockfile.py
Three CargoLockfileInspectionRepository adapter tests wrote the same
root Cargo.toml/Cargo.lock pair inline. Introduce a module-local
_cargo_workspace pytest fixture (mirroring the existing _metrics_registry
pattern) and apply it via @pytest.mark.usefixtures on the three tests that
need the pair, dropping the duplicated write_text calls. Each test keeps
its tmp_path parameter and assertions unchanged; the two adapter tests
that do not need a workspace on disk are left untouched.

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

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 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.

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.

Remove infrastructure _CommandRunner from public BumpOptions and abstract VCS/filesystem behind a repository port

2 participants