The work cannot be split safely
The ticket cannot become an ordered list of per-repository jobs: the dependencies form a cycle, or a repository it needs is not in the workspace. Nothing has been built, so nothing is at risk.
The pipeline takes a ticket and delivers finished work: it plans, writes the code, runs the tests, reviews its own output, fixes what the review finds, opens a pull request. Against a single repository it needs no supervision.
Real tickets cross repositories. An API change lands in the service that provides it, then in the client library that calls it, then in the tool that wraps that client, in that order.
That is where the pipeline stops.
A stop is the pipeline halting and handing back to a person. It reports what it found, what it expected and what it needs, then does nothing further.
It is not a crash and not a wrong answer. It is the pipeline declining to guess, and usually that is correct: carrying on past a mismatch produces code that compiles, passes its own tests, and breaks the contract the next repository depends on.
The problem is frequency. Every stop interrupts a person.
The goal: reduce how often a stop is necessary, without turning a legitimate stop into a guess.
Both halves matter. Stopping less often by learning to guess would score better on the primary metric while being worse in every way that counts.
A bigger context window does not solve it. Four questions have no answer today once a task crosses a repository boundary:
Four points, each a different kind of missing knowledge.
The ticket cannot become an ordered list of per-repository jobs: the dependencies form a cycle, or a repository it needs is not in the workspace. Nothing has been built, so nothing is at risk.
Either the plan needs something from a dependency that no contract states, or the ticket forces an architecture decision nobody has made. The first is a missing fact. The second is a missing human judgement.
A repository delivered, and what it provides no longer matches what the next one was told to expect. The run stops before the next repository starts, so no work is done against a stale assumption. Note what this is not: the pipeline found the problem and named the right repository. It knows. It is not permitted to act.
The final gate checks the whole graph, including edges the step-by-step check cannot see: those where a repository was never told what to expect and only declared afterwards what it used. Same shape as point 3, but everything is already built.
The method is validated and the measuring instrument works. The headline result cannot exist yet, because the thing that creates stops on purpose is still being built.
Landed 10 Aug.
Read the workspace mode end to end, inventoried every blocker it can report.
Docker built, baseline runs pass on both models, injection suite designed. Remaining: build the four scenarios and author two more tickets.
32 stop sites, each classified by whether it lacks permission or information. This reshaped the rest of the plan.
Made optional by T4: the gaps that cause stops are not the kind better retrieval fixes. If it runs, it runs narrow and is scored separately.
The main design work. What to re-check after a change, what to carry forward, how to tell a fix from a loop.
Measurement contract fixed, baseline columns collected. Waiting on T6.
Two columns collected. The switching experiment needs the scenario suite first.
evidence: strong
evidence: strong
evidence: strong for engineered faults, not claimed for judgement calls
evidence: moderate: two instances, observed rather than designed
Three repositories that genuinely depend on each other, in a chain.
| Repository | Role in the chain |
|---|---|
moby/moby |
The engine. Defines the API and the Go client library everything else calls. |
docker/cli |
The docker command. Consumes the engine's client library. |
docker/compose |
docker compose. Consumes both the engine's client and the CLI. |
Three dependency edges from one ticket, with a forced order: the engine lands before the CLI, which lands before Compose.
A real dependency chain at a workable size. Large enough that the contracts between repositories are genuine, small enough that a full run finishes in an hour or two and can be repeated.
The dependencies are real and versioned. Each repository has a substantial test suite that runs without exotic infrastructure. And a ticket's surface travels visibly, from an engine API option through a command-line flag into a Compose flag, which makes behaviour easy to observe.
Carving one repository into pieces was rejected as the only test bed: it exercises context selection, but not discovery, merge sequencing or version skew, which need real boundaries.
Most of the setup effort went into making the three compile against each other locally. Two edges were routine. The third was not: the CLI carries no standard module file at its root, only a vendored variant, so the usual redirection fails silently. Presenting the vendored files under the names the tooling expects fixes it. Worth recording because it decides whether a test bed is usable at all, and it is invisible until you try.
Both written as real tickets, with acceptance criteria and exact expected help text.
Three repositories. A new soft limit on container processes: added to the engine API,
exposed as a docker run flag, surfaced as a Compose flag. One detail is deliberate: for
the Compose-to-CLI edge the ticket does not state what Compose should expect, so the run
has to handle an unstated dependency.
Two repositories. A timeout option on the engine's client library, surfaced as a global CLI flag. A single edge, stated precisely.
Four complete runs across two tickets and two models, all clean-path. Every run completed: ten cross-linked draft pull requests, every edge satisfied, no human intervention, no stops.
Two behaviours worth naming:
It handled a mismatch that was not one. The ticket named a new field on a particular type. Both models built it nested one level deeper, mirroring how the neighbouring option is structured. A strict reading would have called that a contradiction and stopped. Both judged the named field still reachable and carried on. That judgement separates a useful stop from an annoying one.
A review caught what the tests could not. On the two-repository ticket, the new flag was wired into a constructor with no production callers. Every unit test passed and the feature did nothing. The self-review caught it, one bounded fix cycle followed, round two was clean.
Four faults we inject on purpose, one per kind of stop. Each is a situation we know should halt the run, so “stops went down” can be measured against something real. Each carries a manifest recording where the fault was planted, which is the ground truth for scoring whether the pipeline goes back to the right repository.