01 · Problem

The problem

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. The pipeline stops at the repository boundary.

What a stop is

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.

A stop 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 and make the pipeline worse.

Why it is hard

A bigger context window does not solve it. Four questions have no answer today once a task crosses a repository boundary:

  • Which repositories should this change be validated against?
  • What carries forward to the next step, and what gets dropped?
  • When something turns out wrong, which repository do we go back to?
  • Is this a real fix, or the same failure again? Inside one repository the pipeline already counts repeat failures and gives up rather than thrashing. Across repositories there is no equivalent.

Where a run stops

Four points, each a different kind of missing knowledge.

One multi-repo run, and the four points where it can stop A ticket is split into repositories. Each repository in turn is delivered, what it now provides is extracted, and that is checked against what the next repository expects. An integration gate then checks the whole graph before the pull requests are cross-linked. Four numbered markers show where the run can stop: at the split, while planning a repository, at the check between repositories, and at the integration gate. repeated for each repository, in dependency order Ticket Split intorepositories Deliver thechange Extract what itnow provides Check it againstwhat the nextone expects Integrationgate Cross-linkedpull requests 1 2 3 4

The work cannot be split safely

The ticket cannot become an ordered list of per-repository jobs, because the dependencies form a cycle or a repository it needs is not in the workspace. Nothing has been built, so nothing is at risk.

A repository cannot plan its work

BLOCKED-CONTRACT-GAP · BLOCKED-DECISION-GAP

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.

What was built contradicts what the next repository expects

BLOCKED-CONTRACT-DRIFT

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. The pipeline found the problem and named the right repository. It knows. It is not permitted to act.

A consumed surface is missing at the end

BLOCKED-CONTRACT-DRIFT

The final gate checks the whole graph, including edges the step-by-step check cannot see. Those are the edges 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.

02 · Status

Where we are

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.

T1
Access to the pipeline repository Done

Landed 10 Aug.

T2
Review the pipeline and catalogue how it stops Done

Read the workspace mode end to end, inventoried every blocker it can report.

T3
Build and instrument a multi-repo test bed In flight

Docker built, baseline runs pass on both models, injection suite designed. Remaining: build the four scenarios and author two more tickets.

T4
Map every stop, and what each one lacks Done

32 stop sites, each classified by whether it lacks permission or information. This reshaped the rest of the plan.

T5
Compare context mechanisms Deferred

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.

T6
Design the cross-repo loop and its backtracking rules Pending

The main design work. What to re-check after a change, what to carry forward, how to tell a fix from a loop.

T7
Prototype, then measure against current behaviour Pending

Measurement contract fixed, baseline columns collected. Waiting on T6.

T8
Model strategy, including per-stage switching Pending

Two columns collected. The switching experiment needs the scenario suite first.

03 · Findings

What we've found

F1

Better code retrieval would not reduce how often the pipeline stops

evidence: strong

Hypothesis
The pipeline stops because it lacks knowledge about the other repositories, so a better way to look things up (semantic code search, or a queryable graph) should reduce stops.
What we did
Derived all 32 stop sites from the pipeline's own definition rather than from what happened to fire in runs. For each: what triggers it, what is missing, and which kind of gap it is. A permission gap means it knows what is wrong and may not act. An information gap means it lacks a fact. Then checked against two complete runs on each of two models.
What held
Five permission gaps, three information gaps, and none of the information gaps sit on the path to fewer stops. The dominant pattern is the opposite of the hypothesis. The pipeline detects the problem, describes it precisely, names the correct repository, and stops because acting is outside its authority.
So what
Comparing retrieval mechanisms drops from required to optional. The design effort moves to the loop itself, to what the pipeline may do when it finds a problem, and how it goes back without going in circles.
F2

A baseline of zero stops is a requirement, not a failure

evidence: strong

Hypothesis
A test bed earns its place by showing plenty of stops in its baseline. Those stops are the headroom the work removes.
What we did
Two complete runs on each of two models with nothing injected. Five repository deliveries, two work splits, three cross-repo checks, two integration gates, ten cross-linked pull requests, zero human interventions.
What held
Zero stops on every clean run, both models. At face value that reads as a test bed with nothing to measure.
So what
The criterion was wrong, not the test bed. A clean run that stops on its own is a noisy instrument. You cannot attribute a stop to an injected fault if the harness manufactures its own. Counted stops come from injected faults, which is what the scenario suite provides.
F3

Stop behaviour is deterministic for engineered faults

evidence: strong for engineered faults, not claimed for judgement calls

Hypothesis
Stops depend on model judgement, so counting them partly measures the model's mood and results will not reproduce.
What we did
Seeded one field rename into already-delivered work, with the old name removed, then re-ran the check step. Repeated identically on both models.
What held
Both models stopped at the same point, named the same repository, and reported built against expected. One seeded fault produced exactly one stop, with nothing papered over. Extraction reported the expected surface as absent rather than quietly normalising the rename away.
So what
The instrument is sound for engineered faults, which is three of the four scenarios. The fourth turns on judgement, so it is measured as a rate per model rather than pass or fail.
F4

An interrupted run resumes from its own written record

evidence: moderate: two instances, observed rather than designed

Hypothesis
A run interrupted partway through has to restart from the beginning.
What we did
Not planned. Usage limits killed two repository runs mid-flight, one just after planning with a commit in place, one before its branch existed. Both were re-dispatched.
What held
Both picked up from the written record and repository state with nothing re-run and nothing diverging. Completed stages were trusted, not repeated.
So what
This is evidence for the cheapest candidate design. The run's own record already works as memory across interruptions, so cross-repo backtracking can extend it instead of adding a separate store. It also makes the scenario suite affordable, since a fault can be seeded at a checkpoint and resumed in minutes.
04 · Test bed

The test bed

Why Docker

Three repositories that 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.

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.

The two test tickets

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.

What the baseline runs showed

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.

05 · Scenarios

Test scenarios

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.

Designed and approved, implementation pending. S3 and S4 need their tickets written and validated first. A control that does not fire for the intended reason gets fixed or cut.
S1

Drift caught between repositories

What we break
After the engine delivers, rename the field it just added so the original name is gone, then re-derive what the engine provides. The next repository expects a name that no longer exists.
Should stop
BLOCKED-CONTRACT-DRIFT at the check between engine and CLI, before the CLI starts
Correct recovery
Go back into the engine with the drift evidence, bounded so it cannot cycle. Nothing downstream is built yet, so the blast radius is one edge.
Why it exists
The whole problem in miniature, and already proven by hand. The pipeline finds the fault, names the right repository, and stops anyway.
S2

Drift only the final gate can see

What we break
Wait until Compose has delivered and declared which CLI capability it used, then rename that capability in the CLI. This targets the edge the ticket left unstated, which the step-by-step check cannot examine because there was no stated expectation to compare against.
Should stop
BLOCKED-CONTRACT-DRIFT at the integration gate, with all three repositories built
Correct recovery
Go back into the CLI. Everything is already built, so the response has to weigh a graph-wide blast radius rather than a single edge.
Why it exists
Paired with S1. Same verdict, different moment, different amount of work at stake. The pairing shows whether backtracking picks the right scope, not just the right repository.
S3

A missing fact that already exists upstream

What we break
A ticket whose CLI requirement needs an engine capability that has existed for years but which the ticket never names. The record of what the engine provides carries only what changed and what the ticket named, so the capability is absent from it, and the pipeline may not plan against code it was not told about.
Should stop
BLOCKED-CONTRACT-GAP while the CLI plans its work
Correct recovery
Confirm the capability exists upstream and add it to the record. No repository is re-entered.
Why it exists
The one scenario where the shortfall is a missing fact rather than a missing permission, so it is where a lookup mechanism could help. It makes that question answerable with evidence instead of argument.
S4

A decision only a person can make

What we break
A ticket forcing one architectural decision with product consequences, where two shapes both satisfy the acceptance criteria and no design record settles it. The scenario supplies plausible design records that deliberately do not cover it.
Should stop
BLOCKED-DECISION-GAP at the architecture check, in the first repository
Correct recovery
None. Stopping is correct. A mechanism may propose options, never pick one.
Why it exists
The anti-gaming control. Any change that reduces stops can cheat by learning to guess, and the primary metric would reward it. S4 must still stop after the loop is built. Because it turns on judgement, it is scored as a rate per model, expected high, and it must not fall.
Results → 4 clean-path runs across 2 models, measured against the nine agreed benchmarks.