Skip to content

Rider sweep: the small debts this week's reviews parked - #222

Merged
blooop merged 6 commits into
mainfrom
wayfinder/devlaunch-221
Aug 15, 2026
Merged

blooop merged 6 commits into
mainfrom
wayfinder/devlaunch-221

Conversation

@blooop

@blooop blooop commented Aug 15, 2026 •

Copy link
Copy Markdown
Owner

Closes #221

One pass over the eight riders the reviews of PRs #201–#220 parked. The out-of-scope structural riders (shared e2e in_container(), purge-runner dedupe, --before extraction, install.sh path-list pin) stay out.

The eight

# What changed How it was verified
1 clone_repo's partial-clone deletion no longer names ensure_repo as the gate Its one production caller is clone_if_missing, which cannot be called without the RepoLock; ensure_repo and prepare_cold both reach it, so the lock is the invariant, not the route
2 locks.py's token paragraph softened to what RepoLock proves ensure_repo is still public, lock-taking and unmarked in its signature — the token removes the reason to re-lock, it is not a guard against it
3 CHANGELOG entry for the widened cold-prep serialization window, under [Unreleased] The 0.0.26 release commit is an ancestor of PR #201's merge, so it has not shipped in a release — [Unreleased] is where it belongs and no retroactive-note convention is needed
4 ensure_tools → provision_tools, with every call site, comment and test Read the body first: it probes, lends, installs, and reports what it found while the session starts either way. provision is the module's own word (provisioning_disabled, ProbeResult.PROVISIONED)
5 New pin at the bench record schema seam Real writer → real reader, mutation-checked (below)
6 Two comments PR #209's own changes falsified: "same st_ino" and "the bare has no LFS objects" The test compares (st_dev, st_ino); _fill_cache_lfs_store makes the bare the repo's LFS store, so the new text says what is true — the store is filled per ref, after this clone, which is why the smudge is still skipped
7 Prune's byte-identical refusal arms collapsed via isinstance(x, (A, B)); false rationale replaced; README's "none of it goes" tightened ty green (exhaustiveness still holds, as describe_source already showed); prune/purge suites pass unedited
8 Accurate rationale restored for fetch_interval=0 in both fixtures _should_fetch returns elapsed > interval, so 0 means always. It is harmless because lazy_fetch is its only reader and nothing here drives it, and the remotes are on local disk

Item 5: the mutation check

The new test/test_bench_record_schema.py runs one record through both shipped scripts as subprocesses — bench_launch.py --record against a stand-in launch built from timing.py's own constants, then bench_points.py handed the file, --require-stages-on included. No hand-built dict, and it names no field of the record, so there is no third copy of the schema to drift.

Field names skewed in the writer, one at a time:

skew in bench_launch.py new pin test_timing.py test_bench_points.py
total_seconds → total_secs (run) RED RED GREEN
median stage seconds → secs RED RED GREEN
median stage runs → count RED RED GREEN
top-level median → medians RED RED GREEN
total_seconds renamed in the writer and in test_timing.py's own assertions RED GREEN GREEN

The last row is the case this exists for: a rename carried out consistently on one side leaves both pre-existing suites green and the trend silently broken. All skews restored; the suite is green at HEAD.

Item 7: behaviour-neutrality, and one honest finding

The purge and prune suites are unedited and pass. Mutating the collapsed branch:

  • (RemovedWhatItCould, RemovedNothing) → (RemovedNothing,) — 2 tests fail. The partial arm is pinned.
  • (RemovedWhatItCould, RemovedNothing) → (RemovedWhatItCould,) — 55 passed, and test_dl.py, test_purge_partial_removal.py and test_disk_usage.py all stay green too.

So prune's RemovedNothing arm is covered by nothing. The refactor is neutral by construction — both arms had byte-identical bodies before and share one now — but the suite does not prove it for that arm. I did not add a test for it: the ticket scopes this sweep to eight items and this would be a ninth. Rider for the next toucher: prune has no test that reaches RemovedNothing. (--purge's own RemovedNothing arm is covered, in test_purge_partial_removal.py.)

Notes

  • Not user-visible, so no CHANGELOG line for it: the ensure_tools rename. devlaunch.tools is internal, the name appears in no README, CHANGELOG or docs, and no CLI surface moved. The one CHANGELOG entry here is item 3, which is a behaviour change that shipped without a line.
  • test/unit/test_tools.py, test/unit/test_launch_serialization.py and test/test_timing.py pinned the old name and were updated deliberately with it; the class became TestProvisionTools and the mock locals provision.

One follow-on commit, after the first push

codecov/patch came back red on exactly one line: dl <ws> up against a container that is already running returns without calling devpod up and tops the tools up on the way past — and nothing exercised it, so renaming the call was enough to make it the only uncovered line in the diff. It is also the arm that makes up the documented recovery for a workspace that missed provisioning, so it was worth a test rather than a waiver. Mutation-checked: delete the top-up and the new test is the one that fails.

CI: pixi run ci exit 0 — ruff, pylint 10.00/10, ty all checks passed, 1745 passed / 24 deselected. All PR checks green, codecov/patch included.

🤖 Generated with Claude Code

blooop added 4 commits August 15, 2026 06:00
`bench_launch.py --record` writes a JSON shape `bench_points.py` reads, and
nothing failed when they drifted. Each side was pinned against its own copy of
the shape -- the writer's suite asserts the fields it just wrote, the reader's
suite drives literals on purpose -- so a field renamed in the writer and in the
writer's own assertions left both green while the trend stopped publishing.

This runs one record through both shipped scripts end to end, with no
hand-built dict between them, and names no field of its own so there is no
third copy to drift.
It probes, lends and installs, and any of those may come up empty -- a host
with nothing to lend, a container the binaries will not run in, an opt-out, a
failed network install. It reports what it found and the session starts either
way, so "ensure" named the answer rather than the work. `provision` is already
the module's own word for this: the opt-out disables "tool provisioning" and
the probe's happy answer is PROVISIONED.

Call sites, comments and the tests that named it move with it.
- clone_repo's partial-clone deletion named ensure_repo as the gate. Its one
  caller is clone_if_missing now, reached from ensure_repo and prepare_cold
  alike, and what makes the rmtree safe is the RepoLock token both routes carry.
- locks.py claimed the token made a second acquisition unwritable. It does not:
  ensure_repo is still public, still lock-taking, and still unmarked. Softened
  to what the token actually proves.
- workspace_clone.py said the hardlink test asserts "same st_ino" (it compares
  (st_dev, st_ino)) and that the bare cache has no LFS objects (it is the repo's
  LFS store now). Both were made false by the PR that sat beside them.
- fetch_interval=0 lost the comment that was its only rationale, and 0 means
  *always* fetch, so the surviving line read as its own opposite.
- CHANGELOG never recorded that cold prep now serializes across the whole
  preparation. Unreleased, because 0.0.26 shipped before it did.
The two arms had byte-identical bodies kept apart by a stated rationale that is
false: exhaustiveness comes from _unhandled_removal's NoReturn parameter, and a
grouped isinstance narrows to it just as well -- which describe_source in the
same module already relies on.

README's "when none of it goes" listed conditions that are not sufficient for
it: a sealed cache root over removable clones is a partial removal, not a
nothing. Tightened to the condition that actually decides the arm.

@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 @blooop, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 15, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors tooling provisioning API naming, tightens repo lock and clone invariants, clarifies documentation and comments around pruning, LFS and fetch intervals, and adds an integration-style test that pins the bench record schema across writer/reader scripts while keeping behavior unchanged elsewhere.

Flow diagram for workspace tools provisioning via provision_tools

flowchart LR
    User["Developer runs dl workspace_up or dl up"]
    DLCLI["devlaunch.dl.workspace_up / _run_cli"]
    RunDevpod["run_devpod (devpod up)"]
    ToolsModule["devlaunch.tools.provision_tools"]
    Workspace["Workspace container"]

    User --> DLCLI
    DLCLI --> RunDevpod
    DLCLI --> ToolsModule
    ToolsModule --> Workspace

    subgraph ToolsProvisioning
        ToolsModule --> Probe["Probe REQUIRED_TOOLS in workspace"]
        ToolsModule --> Lend["Lend host tools into container"]
        ToolsModule --> Install["Install tools when allowed"]
        ToolsModule --> Answer["Return whether tools are now available"]
    end

    Answer --> DLCLI
Loading

Flow diagram for bench record schema pin between writer and reader

flowchart LR
    Test["test_bench_record_schema.py"]
    BenchLaunch["bench_launch.py --record"]
    RecordFile["Bench record file"]
    BenchPoints["bench_points.py --require-stages-on"]

    Test --> BenchLaunch
    BenchLaunch --> RecordFile
    Test --> BenchPoints
    BenchPoints --> RecordFile

    subgraph MutationCheck
        SkewWriter["Intentionally skew field names in bench_launch.py"]
        Observe["Observe which tests fail (new pin vs existing suites)"]
    end

    Test --> SkewWriter
    SkewWriter --> Observe
Loading

File-Level Changes

Change Details Files
Rename devlaunch.tools.ensure_tools to provision_tools and update all call sites, tests, and documentation comments to match the more accurate semantics.
  • Update tools module to expose provision_tools with clarified docstring emphasising provisioning semantics and unchanged boolean result meaning
  • Replace ensure_tools calls with provision_tools in workspace_up, CLI fast‑path, and associated timing/lock paths
  • Rename TestEnsureTools to TestProvisionTools and adjust fixtures, mocks, and assertions in unit tests to use provision_tools naming
  • Ensure launch serialization tests still assert tools provisioning happens on both contended and uncontended paths
devlaunch/tools.py
devlaunch/dl.py
test/unit/test_tools.py
test/unit/test_launch_serialization.py
test/test_timing.py
Tighten clone_repo’s partial-clone cleanup guard to be keyed on the RepoLock invariant instead of a specific entrypoint, and align lock documentation with what RepoLock actually proves.
  • Change clone_repo’s partial clone removal comment to state that the caller must hold RepoLock via clone_if_missing rather than naming ensure_repo as the gate
  • Update locks.py documentation to clarify that RepoLock proves the lock is held but does not prevent re-locking, and that structure of call sites avoids deadlock
devlaunch/worktree/repo_manager.py
devlaunch/worktree/locks.py
Clarify LFS smudge behavior and hardlink tests in workspace clone preparation to reflect current reality of (st_dev, st_ino) checks and cache-backed LFS store semantics.
  • Fix integration-test commentary about hardlink assertions to mention (st_dev, st_ino) pair instead of just st_ino
  • Rewrite LFS smudge skipping rationale to explain bare cache as LFS store, per-ref materialization, and failure modes on first/other workspaces
devlaunch/worktree/workspace_clone.py
Collapse prune_clones’ two refusal arms into a single isinstance branch while preserving type safety and updating README wording around ‘none of it goes’.
  • Refactor prune_clones to use isinstance(outcome, (RemovedWhatItCould, RemovedNothing)) and share refusal list extension logic
  • Update inline comments to reference describe_source grouping and NoReturn-based exhaustiveness of _unhandled_removal
  • Tighten README purge description so ‘none of it goes’ explicitly means no entries were removed under the cache
devlaunch/dl.py
README.md
Add a new end-to-end test that drives bench_launch.py and bench_points.py together to pin the bench record JSON schema at the writer/reader seam and detect drift that existing suites miss.
  • Introduce test/test_bench_record_schema.py to generate real bench records via run_bench and consume them via bench_points.py subprocess calls
  • Define canned COLD and warm runs and helpers that avoid hand-built dicts, asserting only on published point names/values/ranges/extras
  • Verify that missing stages cause bench_points.py to fail with --require-stages-on when driven against records the bench actually wrote
test/test_bench_record_schema.py
test/test_timing.py
Document the widened cold-prep per-repo lock serialization window and associated user-visible message change under the Unreleased section of the CHANGELOG.
  • Add a CHANGELOG entry explaining that cold launches now hold the per-repo lock across host preparation, affecting concurrent cold launches of different branches of the same repo
  • Describe the new consolidated failure message and scope of the behavior change relative to prior release 0.0.26
CHANGELOG.md
Clarify fetch_interval=0 semantics and test rationale in fixtures and clone race integration tests, noting that it means ‘always fetch’ and is harmless in local‑disk test setups.
  • Add comments in git_fixtures real_managers fixture explaining _should_fetch and lazy_fetch interaction and why fetch_interval=0 is safe
  • Mirror the same rationale in test_clone_race to document behavior in the race harness
test/fixtures/git_fixtures.py
test/integration/test_clone_race.py

Assessment against linked issues

Issue Objective Addressed Explanation
#221 Update the stale clone_repo safety comment in repo_manager.py:219 to reflect the RepoLock-based entrypoint reality. ✅
#221 Soften the RepoLock token docstring in locks.py:18-22 to accurately describe what guarantees it provides. ✅
#221 Add a CHANGELOG entry documenting the widened cold-prep serialization window introduced by #200/PR #201, under the appropriate section according to the repo’s convention. ✅
#221 Rename the misnamed ensure_tools function to a more accurate name (and update all call sites, comments, and tests accordingly). ✅
#221 Add a test pin for the bench record schema seam so that a record written by bench_launch.py --record is successfully parsed by bench_points.py, failing if they drift. ✅
#221 Update the two now-false comments in workspace_clone.py:590 and :625 to match the current behavior regarding (st_dev, st_ino) comparison and LFS objects in the bare cache. ✅
#221 Refactor prune’s handling of byte-identical Removal arms (RemovedWhatItCould/RemovedNothing) to use grouped isinstance handling, remove the false comment rationale, and tighten the README description of the "none of it goes" condition. ✅
#221 Restore accurate rationale comments for fetch_interval=0 in test/fixtures/git_fixtures.py and test_clone_race.py, explaining that 0 means always fetch and why it is harmless in these tests. ✅

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

@codecov

codecov Bot commented Aug 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.96%. Comparing base (82a60fe) to head (a46a17f).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #222      +/-   ##
==========================================
+ Coverage   92.80%   92.96%   +0.15%     
==========================================
  Files          24       24              
  Lines        3681     3679       -2     
==========================================
+ Hits         3416     3420       +4     
+ Misses        265      259       -6     
Files with missing lines Coverage Δ
devlaunch/dl.py 95.37% <100.00%> (+0.36%) ⬆️
devlaunch/tools.py 96.69% <100.00%> (ø)
devlaunch/worktree/locks.py 100.00% <ø> (ø)
devlaunch/worktree/repo_manager.py 91.66% <ø> (ø)
devlaunch/worktree/workspace_clone.py 94.20% <ø> (ø)

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

blooop added 2 commits August 15, 2026 06:08
`dl <ws> up` against a container that is already running returns without ever
calling `devpod up`, and tops the tools up on the way past. That top-up is what
makes `up` the documented recovery for a workspace that missed provisioning,
and nothing exercised it -- so renaming the call was enough to turn it into the
only uncovered line in the diff.

Mutation-checked: delete the top-up and the new test is the one that fails.
…neighbour

Review found both items on the same seam -- a claim the code does not carry.

The collapsed `isinstance(outcome, (RemovedWhatItCould, RemovedNothing))` rests
on the two arms reporting alike, and this PR's own mutation testing showed
nothing held that down: narrowing it to `(RemovedWhatItCould,)` left all 55
tests green, because no prune test ever reached a removal that removed nothing.
The new test drives one -- an orphan clone sealed at its own root, with
`--separate-git-dir` putting the repository outside so every entry under the
seal is a file the seal governs and none of it comes away -- and asserts the
partial arm's contract on it: named with what the system said, siblings still
gone, exit 1. Mutation-checked: the narrowed isinstance now fails on this test
and only this test.

clone_repo's comment said it "is reached only through clone_if_missing, which
cannot be called without the RepoLock token". True of production and not of the
method: it is public, takes no token, and sixteen test sites call it directly
and unlocked. Hedged to the production callers, with what the signature does
not enforce said out loud -- the same overstatement class the locks.py edit in
this PR exists to correct.

@blooop blooop left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This was generated by AI during review.

Two-axis review of #222 against ticket #221 at merge-base 82a60fe, plus one closing fix round (a46a17f). Preflight: all checks green, non-empty diff (13 files, +303/−74 pre-fix), MERGEABLE.

Standards

Nothing here blocks. Everything checked at head is true.

  • Verified true (read against the code, not the diff): the prune collapse — a scratch 4-member union with a grouped isinstance and a NoReturn sink still errors on the fourth arm, so the new comment's justification holds. workspace_clone.py's (st_dev, st_ino) claim matches the integration test; the LFS comment matches _fill_cache_lfs_store; both fetch_interval=0 rationales are right; locks.py's hedge is accurate; the CHANGELOG entry's lock-cycle claims are pinned by dl.py:4432 and test_repo_lock_cycles.py.
  • Rename is complete. Zero ensure_tools anywhere at head — source, tests, docs.
  • repo_manager.py:226-233 overclaimed in the new direction: "reached only through clone_if_missing" is true of shipped code but ~16 test sites call clone_repo directly unlocked. [Resolved in a46a17f — hedged to "in production", with a second paragraph stating what the signature does not enforce.]
  • Non-blocking — test_bench_record_schema.py re-declares POINTS/run_points byte-identically to test_bench_points.py while importing other helpers from test_timing.
  • Non-blocking — the README --purge "none of it goes" rewrite rode along undeclared; it is correct (all three RemovedNothing producers enumerated) but user-facing.
  • Non-blocking — two staleness points: tools.py:879 "whether they are now there" is approximate on the LENDABLE arm; the follow-on test's class docstring still says "every workspace dl opens goes through up" while the test asserts the opposite.

Spec

All 8 ticket items present and accurate; the binding out-of-scope list untouched. Nothing blocks.

  • Item 5 — "a record written by the real writer parses through the real reader": satisfied as subprocess-to-subprocess, no hand-built dict, no record field named in the test. Mutation "median"→"medians" in the writer → 5 failed; restored.
  • Item 4 — zero ensure_tools at head; provision_tools matches the body (probes, lends, installs; session starts either way).
  • Item 7 — precedent real (dl.py:1450); prune/purge suites unedited and passing; an injected fourth Removal arm makes ty error exactly at the sentinel — the exhaustiveness claim is true, not asserted. README's rewrite replaces the false necessary condition the ticket flagged.
  • Item 3 — independently verified: v0.0.26 is an ancestor of PR #201's merge and that merge is not in v0.0.26; [Unreleased] → Changed correct.
  • Items 1/2/6/8 — each true against current code. (Item 1's absolutist wording — the Standards axis's finding — resolved in a46a17f.)
  • Deviations: the codecov follow-on test verified real (replacing the top-up at dl.py:4522 fails exactly the new test); the sibling-comment and no-CHANGELOG calls sound. Deviation 2 — leaving the proven-uncovered RemovedNothing arm — was judged acceptable but the weakest of the four: "the collapse is precisely what makes the two arms' interchangeability load-bearing … I'd have taken it." [Taken in a46a17f: test_a_directory_none_of_which_came_away_is_reported_the_same_way, built on a --separate-git-dir clone whose every entry is a file (the shape that genuinely reaches total refusal — a sealed normal clone is still a partial, as test_purge_partial_removal.py pins). The narrowing mutation now fails exactly this test (1 failed, 55 passed); restored, 56/56.]

Verdict

Approve — no blocking findings on either axis; the two residues both axes converged on (the prune-RemovedNothing pin, the "reached only through" overclaim) were closed during review as commit a46a17f, with the mutation and CI re-verified green. Remaining riders for future touchers: dedupe POINTS/run_points with test_bench_points.py, refresh the two stale docstrings named above. GitHub refuses --approve from the PR author's account, so this is posted as a comment; this written verdict governs. Merging with a merge commit per repo rules.

@blooop
blooop merged commit 67e2acf into main Aug 15, 2026
12 checks passed
@blooop
blooop deleted the wayfinder/devlaunch-221 branch August 15, 2026 05:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rider sweep: the small debts this week's reviews parked

1 participant