Skip to content

Say what Trips is, replace the Runner impl count with the shape, and guard the count - #415

Merged
blooop merged 2 commits into
mainfrom
docs/runner-seam-and-trips
Aug 25, 2026
Merged

Say what Trips is, replace the Runner impl count with the shape, and guard the count#415
blooop merged 2 commits into
mainfrom
docs/runner-seam-and-trips

Conversation

@blooop

@blooop blooop commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Builds #409, under map #406. The decision it writes down is #405's resolution.

What was verified before writing

grep -rn "impl Runner for" rust/ returns nine: ProcessRunner
(devlaunch-runner/src/lib.rs:419), FakeRunner
(devlaunch-test-support/src/fake_runner.rs:370), and seven test-local wrappers
(ScriptedRunner, FakeDevpodRealGit, Devpod, StubbedLfs, FakeGit,
Trips, Rebuilding). Three hold a real ProcessRunner and fall through to
it: FakeDevpodRealGit (flows/listing.rs:1208), Devpod
(flows/lifecycle.rs:3417), StubbedLfs (flows/workspace_clone.rs:4026). All
of #405's numbers hold.

One nuance found while checking: StubbedLfs routes on the lfs subcommand
rather than the program name, and only capture reaches the real runner — its
passthrough, session and detach are canned. The doc says so.

What changed

  • Runner's doc (devlaunch-runner/src/lib.rs) — the shape, not a count.
    One production impl, one shared fake carrying the response table and
    DevpodMachine, wrappers over those two. The seven wrappers are not counted,
    because a count rots the way "once" did, but the three routers are named as
    Say what Trips is, and correct the Runner impl count #409 asks: FakeDevpodRealGit, Devpod, StubbedLfs, with the last flagged
    as the one not to read by analogy. What replaces the count is the two shapes a
    reader needs to recognise — part real and recorder.
  • Trips' doc (flows/provision.rs) — a recorder, not a third fake devpod;
    answers by call index, never reads argv to decide, holds no workspace state, so
    a corpus row (given state → argv → exit + then state) has nothing to bind to
    at either end, and driving it over conformance.json would assert a fixture
    against itself. Points at DevpodMachine as the fake the corpus does cover.
  • Trip::script's doc — which argv it re-parses (setup_pass), and that the
    expect is the guard, with --command pinned by SSH_VALUE_FLAGS and a
    corpus row measured at v0.26.1. Took the ticket's optional one-liner:
    &self.argv[at + 1].get(at + 1).expect("a --command with a payload"), so
    a moved payload names the assumption that broke. Test-side only.
  • devlaunch-test-support/src/devpod.rs's module doc — one paragraph saying
    the two hand-written fakes are the whole population the corpus has to cover,
    and saying where the response table sits: it is an argv reader, and it is
    outside the corpus because a scripted entry is one test saying what it wants
    back, not a claim about real devpod.

The guard

devlaunch-runner/tests/one_seam.rs (new, 9 tests). It scans the workspace and
fails if anything but ProcessRunner implements Runner outside test code. That
is the half of the new doc that is load-bearing and the half worth a test: a
wrapper appearing or disappearing is what the doc is now written to survive, but
a second production impl is not a stale sentence — it is a second way for
devlaunch to start a process, and the seam has stopped being one.

The unit is a token, not a line, which is the fix for round one's findings. The
first version matched the literal text impl Runner for at the start of a
trimmed line, and impl<'a> Runner for X and impl devlaunch_runner::Runner for X both walked past it; #[cfg(test)] was only honoured when mod tests { was
the very next line, so an #[allow(...)] between them made a whole test module
read as production. Now comments and literals are blanked out first (offsets
preserved), the trait is matched by the last segment of whatever path names it
after optional generics, and #[cfg(test)] gates a brace-matched region with any
attributes in between skipped. That last change also closes a hole nobody had
reached: the old scan called everything below an inline test module test code,
so the way past the guard was to write underneath it.

Seven of the nine tests run scan() over source held as a string, so the
spellings are exercised without dropping probe files into the tree. All seven
were run against the old line logic first and four were red — both bypasses, the
false positive, and the below-the-module hole. End to end, the review's own
probe2.rs/probe3.rs now fail the guard by name and probe4.rs does not.

Rejected: a syn dev-dependency, which is exact but pulls a second copy of syn
with the full feature into every build of this workspace for one test; and
sealing the trait, which cannot tell a production impl in devlaunch-core from a
#[cfg(test)] one in the same crate without exporting the seal.

Two smaller things from the same review: the doc's recommended
grep -rn "impl Runner for" returned 11 lines for 9 impls, so the enumeration is
now named as the scan (which prints every impl with its file and verdict) and the
grep is kept as the eyeball version, marked as over-reporting; and the walk skips
a build directory by CACHEDIR.TAG as well as by the name target.

Gate

From rust/: cargo test --workspace green (1253 + 41 + 114 + … all pass,
including aid's interactive suite — #401's flake did not appear), cargo clippy --locked --all-targets -- -D warnings clean, cargo fmt --check clean.

No behaviour changes outside the one test-side expect.

…hape

The trait's doc said it was "implemented once for real processes and once for
tests". There are nine impls: ProcessRunner, FakeRunner, and seven test-local
wrappers over those two. Three of the wrappers hold a real ProcessRunner and
hand it whatever they do not fake -- FakeDevpodRealGit, lifecycle's Devpod,
workspace_clone's StubbedLfs -- which is the fact a reader currently learns by
grepping and then by reading a fixture.

A corrected count would rot the same way, so the doc states the shape instead:
one production impl, one shared fake carrying the response table and
DevpodMachine, and wrappers over those two, with the grep named as the
enumeration. What it does say about the wrappers is the two shapes worth
recognising before reading one -- part real (a fake devpod beside a real git)
and recorder -- neither of which is a number.

The half of that shape which is load-bearing is now asserted rather than
claimed: tests/one_seam.rs scans the workspace and fails if anything but
ProcessRunner implements Runner outside test code. A second real implementation
is not a stale doc, it is a second way to start a process. Proven by adding one
and watching it fail.

Trips gets the doc #405 settled: it is a recorder, not a third fake devpod. It
answers by call index from a list the test handed the constructor, never reads
argv to decide anything, and holds no workspace state -- so a corpus row, which
is given state -> argv -> exit + then state, has nothing to bind to at either
end, and driving it over conformance.json would assert a fixture against itself.
The next person asking why it is not in the corpus finds the answer at the
definition. devpod.rs's module doc says the same thing from the other side: the
two hand-written fakes are the whole population the corpus covers, and the
Runner wrappers reach DevpodMachine through FakeRunner rather than being a third.

Also Trip::script, which re-parses the argv setup_pass builds: it now says so,
and `&self.argv[at + 1]` becomes `.get(at + 1).expect("a --command with a
payload")`, so a payload that moved says which of the two assumptions broke
instead of panicking on an unnamed index.

Closes nothing; #409, under map #406.

@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 250000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR replaces stale Runner implementation-count documentation with a durable description of the production seam, shared fake, and wrapper shapes, adds a source-scanning guard against new production implementations, and clarifies the separate roles of Trips and DevpodMachine in test and corpus coverage.

Sequence diagram for the production Runner seam guard

sequenceDiagram
    participant Test as one_seam test
    participant Walk as Workspace source scan
    participant Impl as Runner implementations

    Test->>Walk: scan workspace for impl Runner for
    Walk->>Impl: classify production vs test code
    Impl-->>Walk: ProcessRunner and test implementations
    Walk-->>Test: production implementations
    alt only ProcessRunner is production
        Test-->>Test: pass
    else another production implementation exists
        Test-->>Test: fail
    end
Loading

File-Level Changes

Change Details Files
Reframe the Runner documentation around implementation shapes and make the production seam enforceable.
  • Document ProcessRunner as the sole production implementation and FakeRunner as the shared test fake.
  • Describe wrappers as either part-real runners or recorders without maintaining a fragile implementation count.
  • Add workspace scanning tests that classify implementations and fail on additional production Runner implementations.
rust/devlaunch-runner/src/lib.rs
rust/devlaunch-runner/tests/one_seam.rs
Clarify that Trips is a call-indexed recorder rather than a corpus-covered fake devpod.
  • Explain why Trips has no state or argv-derived behavior suitable for conformance fixtures.
  • Point corpus coverage to DevpodMachine reached through FakeRunner.
  • Make Trip::script validate the expected --command payload explicitly with get/expect.
rust/devlaunch-core/src/flows/provision.rs
rust/devlaunch-test-support/src/devpod.rs

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 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.21%. Comparing base (57955a3) to head (1dead6d).

Additional details and impacted files
Flag Coverage Δ
python 42.98% <ø> (ø)
rust 95.57% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
shipped code (rust) 95.57% <100.00%> (ø)
harness and tooling (python) 42.98% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Reviewed at merge-base 57955a3...c7edddb. The guard test was run, and then attacked, in a scratch worktree; every probe below was actually compiled and run.

Preflight: ci, rust, e2e, prek, public-api, packaging, coverage green. review/gate are red only for the missing wf-review report.

Standards

The doc claims check out. grep -rn "impl Runner for" rust/ gives nine impls: ProcessRunner (devlaunch-runner/src/lib.rs:442), FakeRunner (devlaunch-test-support/src/fake_runner.rs:370), ScriptedRunner, StubbedLfs, Trips, Rebuilding<'_>, FakeGit, FakeDevpodRealGit, Devpod. Three hold a real ProcessRunner and fall through to it — FakeDevpodRealGit (flows/listing.rs), Devpod (flows/lifecycle.rs), StubbedLfs (flows/workspace_clone.rs) — and StubbedLfs's nuance is as the PR body describes: it routes on the lfs subcommand, and only capture reaches real processes. The Trips characterisation is accurate: record (provision.rs:2456) pushes argv into seen and returns answers[(seen.len()-1).min(answers.len()-1)], never consulting argv to decide, and the struct holds no workspace state. Trip::script's .get(at + 1).expect("a --command with a payload") returns the same element in every passing case (&String coerces to &str) and replaces an unnamed index panic with a named one — an improvement, no new panic path.

Now the guard, which is where the findings are. devlaunch-runner/tests/one_seam.rs passes as shipped (2/2), and it is not vacuousthe_scan_finds_the_implementations_it_is_meant_to_judge really does anchor on ProcessRunner and on ≥2 test impls, and a plain second production impl is caught. I dropped impl Runner for Sneaky {} into devlaunch-core/src/probe1.rs and got exactly the failure the PR promises:

assertion `left == right` failed: Runner is the one seam onto the OS, and ProcessRunner is meant to be the
only implementation of it outside test code. Found: [
    "Sneaky (devlaunch-core/src/probe1.rs)",
    "ProcessRunner (devlaunch-runner/src/lib.rs)",
]
  left: 2
 right: 1

1. CONFIRMED, and the most serious finding here — two trivial bypasses. implementer() matches only a line whose trimmed text begins literally with impl Runner for . I put both of these in devlaunch-core/src/ as ordinary production files, with probe1.rs removed, and the guard stayed green:

// devlaunch-core/src/probe2.rs
pub struct Sneaky2<'a>(&'a str);
impl<'a> Runner for Sneaky2<'a> {}

// devlaunch-core/src/probe3.rs
pub struct Sneaky3;
impl devlaunch_runner::Runner for Sneaky3 {}
running 2 tests
test only_process_runner_does_real_work ... ok
test the_scan_finds_the_implementations_it_is_meant_to_judge ... ok
test result: ok. 2 passed; 0 failed

Neither spelling is exotic. The first is what a wrapper looks like — impl<R: Runner> Runner for Timed<R> — which is precisely the future the new doc anticipates; note that today's Rebuilding<'_> only escapes it because it happens to be written with '_ rather than impl<'a>. The second is the natural spelling in any crate that does not use devlaunch_runner::Runner, i.e. the likeliest form a genuinely new second seam would take. A guard whose whole job is "a second production impl must not arrive silently" currently lets both arrive silently. Matching ^\s*impl(<[^>]*>)?\s+(\w+::)*Runner\s+for\s+ would close both.

2. CONFIRMED — a false positive on legitimate future code. is_test_code requires the #[cfg(test)] line to be immediately followed by the mod ... { line. One ordinary attribute in between and every wrapper in that module reads as production:

// devlaunch-core/src/probe4.rs
#[cfg(test)]
#[allow(clippy::too_many_lines)]
mod tests {
    struct LocalFake;
    impl Runner for LocalFake {}
}
Found: [
    "LocalFake (devlaunch-core/src/probe4.rs)",
    "ProcessRunner (devlaunch-runner/src/lib.rs)",
]
  left: 2
 right: 1

Six files in this tree carry an inline #[cfg(test)] mod tests { with a Runner wrapper below it. Adding #[allow(...)] or #[rustfmt::skip] to any of them breaks this test, with a message that accuses the author of adding a second production seam.

3. Low — the doc's own recommended enumeration now has false hits. The Runner doc tells the reader that grep -rn "impl Runner for" is the enumeration. This PR adds two lines that grep matches and that are not impls: devlaunch-runner/src/lib.rs:395 (the doc sentence quoting the grep) and one_seam.rs:32 (const OPENER: &str = "impl Runner for ";). The grep now returns 11 lines for 9 impls. Small, but it is the same self-invalidation the PR exists to end — worth --include=*.rs | grep -v const in the doc, or just saying "nine at time of writing, and here is how to recount".

4. Low — accuracy of the "Part real" bullet. It says such a wrapper "hands everything else, git above all, to real processes". True of FakeDevpodRealGit and Devpod; not of StubbedLfs, whose passthrough, session and detach are canned and never reach self.real. The PR body says the doc would say "hands it whatever they do not fake" — that phrasing is not what shipped, and it is the more accurate one.

5. Low — devpod.rs's new paragraph. "nothing else in the tree decides a devpod outcome from argv": the response table FakeRunner carries is argv-prefix keyed, and the paragraph three lines above it in that same module doc says it "short-circuits this machine entirely". So per-test scripted devpod outcomes are decided from argv and are not corpus-covered. The sentence is true only if scripted responses are read as not-a-fake, which is defensible but is the thing the sentence should say.

Nit: rust_sources skips directories by the literal name target, so a CARGO_TARGET_DIR under rust/ by any other name puts generated sources into the scan. Very low.

Spec

Against #409.

  • "Say what Trips is ... a recorder, not a fake devpod: it answers by call index and never reads argv to decide an outcome, holds no workspace state, and therefore cannot be driven over a corpus row" — done, and accurate to the code.
  • "say which argv it is re-parsing (setup_pass), and that the expect is the guard ... --command is in SSH_VALUE_FLAGS ... conformance.json:258 carries an ssh ... --command row measured against v0.26.1" — done, all four facts present.
  • "Optional one-liner ... .get(at + 1).expect("a --command with a payload")" — taken, verbatim.
  • "Do not write "seven" as a number that will rot the way "once" did — say the structure and name the three routers, since those are the ones a reader currently learns by grepping."half done. The structure is stated; the three routers are not named. Only FakeDevpodRealGit appears. The PR body describes this as "per the ticket's steer ... the three routers are not listed by name", but the ticket's steer, quoted above, says the opposite in the same sentence that forbids the count. Either a later steer superseded #409 and was not recorded there, or a stated requirement was dropped. This is the one place the diff and the spec disagree, and it wants reconciling on the ticket rather than in a review reply.
  • "Testing the outcome: No behaviour changes, so no new test." — the PR adds one anyway. More than asked for and a good instinct; it is also where findings 1 and 2 live.

Verdict

Request changes. The docs are accurate and the Trip::script change is a clean improvement — but the guard is the half of this PR that is meant to outlive the prose, and as written it can be walked past by two ordinary Rust spellings while breaking on an ordinary attribute. Blocking:

  1. implementer() misses impl<'a> Runner for X and impl devlaunch_runner::Runner for X — both demonstrated green above.
  2. opens_a_test_module false-positives when any attribute sits between #[cfg(test)] and mod tests { — demonstrated red above.
  3. The Spec disagreement on naming the three routers (#409 asks for them by name; the doc does not).

Non-blocking: findings 3-5 and the target nit.

Review found two spellings that walked past it and one that broke it, all three
reproduced. `impl<'a> Runner for X` and `impl devlaunch_runner::Runner for X`
dropped into devlaunch-core/src/ as production files left the guard green: it
matched the literal text "impl Runner for " at the start of a trimmed line, and
neither spelling starts that way. The first is the wrapper shape the trait's own
doc invites; the second is how any crate that has not imported the trait names
it, which is the likeliest form of a genuinely new second seam. Meanwhile one
#[allow(...)] between #[cfg(test)] and mod tests { made every wrapper in that
module read as production, because the gate was only honoured when the module
line was literally the next one. Six files here would have broken on that.

The instrument is still "read the source" -- the alternative is a hand-written
list of the wrappers, which is the artefact that rotted in the first place, and
nothing on stable enumerates trait impls from the compiler. What changed is the
unit: a token rather than a line. Comments and literals are blanked out first,
preserving offsets, so a brace in a comment cannot unbalance anything and a
quoted "impl Runner for " is not an impl; the trait is matched by the last
segment of whatever path names it, after optional generics; and #[cfg(test)]
gates a brace-matched region, skipping any attributes between it and the item.
That last one closes a fourth hole nobody had reached: the old scan called
everything below an inline test module test code, so the way past the guard was
to write underneath it.

Rejected: a syn dev-dependency, which is exact but pulls a second copy of syn
with the "full" feature into every build of this workspace for one test, and
sealing the trait, which cannot tell a production impl in devlaunch-core from a
#[cfg(test)] one in the same crate without exporting the seal.

Proof is seven cases running against scan() with source as a string, so no probe
file has to be dropped in the tree to exercise them. All seven were run against
the old line logic first: four red -- both bypasses, the false positive, and the
below-the-module hole. End to end, the reviewer's own probe2/probe3 now fail the
guard by name and probe4 does not.

Also from the review:

- #409 asks to "say the structure and name the three routers". The PR body
  claimed the ticket steered away from naming them; reading it again, it does
  not -- the same sentence that forbids the count asks for the names. So the
  "Part real" bullet names FakeDevpodRealGit, lifecycle's Devpod and
  workspace_clone's StubbedLfs, and says which of the three not to read by
  analogy: StubbedLfs routes on the `git lfs` subcommand rather than the program,
  and only its capture reaches ProcessRunner. The bullet said such a wrapper
  hands "everything else" to real processes, which was true of two of the three.
- The doc's recommended `grep -rn "impl Runner for"` returned 11 lines for 9
  impls, this PR having added two of the false hits -- self-refuting for a change
  about enumerations that rot. The enumeration is now named as the scan, which
  prints every impl with its file and verdict, with the grep kept as the eyeball
  version and marked as over-reporting.
- devpod.rs claimed "nothing else in the tree decides a devpod outcome from
  argv", which the response table three lines above it does. It now says why the
  table is outside the corpus rather than pretending it is not an argv reader: a
  scripted entry is one test saying what it wants back, not a claim about real
  devpod.
- The scan skips a build directory by CACHEDIR.TAG as well as by the name
  `target`, so a moved CARGO_TARGET_DIR cannot feed generated sources into it.

#409, under map #406.
@blooop

blooop commented Aug 24, 2026

Copy link
Copy Markdown
Owner Author

Addressed in 1dead6d. All three blocking findings taken; the two low ones you
marked non-blocking are taken as well.

1 and 2 — the guard. Both were real, and the diagnosis behind them was too:
the line was the wrong unit. The instrument stays "read the source" (nothing on
stable enumerates trait impls from the compiler, and a hand-written list of the
wrappers is the artefact that rotted in the first place), but the unit is now a
token:

  • comments and literals are blanked out first, offsets preserved, so a brace in
    a comment cannot unbalance a region and a quoted "impl Runner for " is not an
    impl;
  • the trait is matched by the last segment of whatever path names it, after
    optional generics, so impl<'a> Runner for X and
    impl devlaunch_runner::Runner for X both land;
  • #[cfg(test)] gates a brace-matched region, with any attributes between it
    and the item skipped.

That last change closes a fourth hole you did not reach: the old scan called
everything below an inline test module test code, so the way past the guard was
to write underneath it. impl Runner for Afterwards after the closing brace read
as test.

Proof. scan() is now callable on source held as a string, so the cases are
seven unit tests rather than probe files. All seven were run against the old line
logic verbatim first, and four were red:

test a_generic_wrapper_is_an_implementation ... FAILED
  left: []  right: ["Sneaky2/production"]
test a_trait_named_by_its_path_is_the_same_trait ... FAILED
  left: []  right: ["Sneaky3/production"]
test an_attribute_between_the_gate_and_the_module_is_still_the_gate ... FAILED
  left: ["LocalFake/production"]  right: ["LocalFake/test"]
test the_gate_ends_where_the_module_does ... FAILED
  left: ["LocalFake/test", "Afterwards/test"]
  right: ["LocalFake/test", "Afterwards/production"]

All nine green now. End to end with your own probes back in the tree:

Found: [
    "Sneaky2 (devlaunch-core/src/probe2.rs)",
    "Sneaky3 (devlaunch-core/src/probe3.rs)",
    "ProcessRunner (devlaunch-runner/src/lib.rs)",
]
  left: 3  right: 1

and probe4.rs is absent from that list, which is the false positive gone. The
workspace scan reports the same nine impls you enumerated, classified one
production and eight test.

Rejected on cost. A syn dev-dependency is exact, but parse_file needs the
full feature, and as a target dev-dependency that is a second copy of syn
compiled into every build of this workspace to serve one test. Sealing the trait
was the other candidate and does not work at all here: it cannot tell a
production impl in devlaunch-core from a #[cfg(test)] one in the same crate
without exporting the seal, which un-seals it.

3 — the spec disagreement. You are right, and the PR body was wrong. I
re-read #409: "say the structure and name the three routers, since those are the
ones a reader currently learns by grepping" is one sentence, and the clause
forbidding the count is the other half of it. No later steer superseded it; the
PR body's claim was a misreading, which I have corrected there too. The "Part
real" bullet now names FakeDevpodRealGit, flows::lifecycle's Devpod and
flows::workspace_clone's StubbedLfs. The seven is still not written as a
number.

4 — the "Part real" bullet. Taken; it was overstated. It said such a wrapper
hands "everything else" to real processes, true of two of the three. It now says
"hands it whatever they do not fake", and names StubbedLfs as the one not to
read by analogy: it routes on the git lfs subcommand rather than the program,
and only capture reaches ProcessRunner.

5 — devpod.rs. Taken, in the form you suggested: rather than defend
"nothing else decides a devpod outcome from argv", the paragraph now admits the
response table is the third argv reader and says why it is outside the corpus. A
scripted entry is one test saying what it wants back from one call, not a claim
about what real devpod does, and pinning it against a fixture would freeze the
exception rather than the behaviour.

Low — the self-refuting grep. Taken. The enumeration is now named as the scan
itself, which prints every impl with its file and its verdict; the grep is kept
as the eyeball version and explicitly marked as over-reporting, since that
sentence and the guard's own fixtures match it.

Nit — target. Taken: the walk skips a directory by CACHEDIR.TAG as well
as by the name, so a relocated CARGO_TARGET_DIR cannot feed generated sources
in.

Nothing left open. Gate from rust/: cargo test --workspace green
(1253 + 41 + 114 + …, and none of #401/#416's known flakes appeared this run),
clippy --locked --all-targets -- -D warnings clean, fmt --check clean.
review/gate will stay red on CI for the repo-wide Sourcery weekly rate limit,
which is not this branch.

@blooop blooop changed the title docs: say what Trips is, and replace the Runner impl count with the shape Say what Trips is, replace the Runner impl count with the shape, and guard the count Aug 25, 2026
@blooop
blooop merged commit c978b93 into main Aug 25, 2026
15 checks passed
@blooop
blooop deleted the docs/runner-seam-and-trips branch August 25, 2026 11:25
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.

1 participant