01 Β· Status

Where we are

The test bed and injection suite are built and validated. The first measurement column β€” claude-sonnet-5 β€” was collected 12 Aug. The next piece of work is the cross-repo loop design (T6).

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 Done

Docker bed built and instrumented. Injection suite validated β€” four controls plus a standing probe. First measurement campaign ran 12 Aug.

T4
Map every stop, and what each one lacks Done

32 stop sites, each classified by whether it lacks permission or information β€” the stops are driven by permission gaps, not missing facts. The campaign added one more entry β€” a stop can be evaded by rewriting the record it checks against.

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, next up. What to re-check after a change, what to carry forward, how to tell a fix from a loop. The legitimate stops (S4–S6) must survive it.

T7
Prototype, then measure against current behaviour In flight

The β€œbefore” column β€” clean path plus injected scenarios on claude-sonnet-5 β€” was measured 12 Aug. The β€œafter” waits on the T6 loop.

T8
Model strategy, including per-stage switching Pending

Only claude-sonnet-5 is measured; other models are pending. Per-stage switching queues behind the loop design.

02 Β· 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.

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

BLOCKED-UNMAPPED-SCOPE

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

03 Β· Test bed

Docker

Three real repositories with a real dependency 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.

One ticket produces three dependency edges in a forced order β€” engine before CLI before Compose. The dependencies are real and versioned, each repository has a substantial test suite, and a ticket's surface travels visibly β€” from an engine API option to a command-line flag to a Compose flag.

The test tickets

Three repositories. A soft limit on container processes, landing as an engine API option, a docker run flag, and a Compose flag. One detail is deliberate β€” the ticket does not state what Compose should expect from the CLI, 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.

Three more tickets exist only to seed scenarios S4–S6. Each is written so the fault sits in the ticket's own text rather than being smuggled in by the harness.

04 Β· Scenarios

Test scenarios

One fault per stop class, planted on purpose. Each scenario has a manifest recording where the fault sits, so a stop is scored against ground truth. S4, S5 and S6 are stops that should happen β€” they count inversely.

Four active controls (S1, S2, S5, S6) plus a standing probe (S4). A third gap scenario (S3) was cut after it never fired for the intended reason β€” the run kept closing the gap itself.
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 now 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.
Why it exists
The pipeline finds the fault, names the right repository, and stops anyway. This is the core case the loop must handle.
Measured
measured 2026-08-12: fired 1/1, correct repository named
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.
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 blast radius is graph-wide.
Why it exists
Same verdict as S1, later moment, more at stake. The pair shows whether backtracking picks the right scope, not just the right repository.
Measured
not measured β€” the collection attempt was killed by a session limit before the fault was seeded, and the diagnosis found that cold runs never produce the shape this scenario needs; disposition pending
S4

A decision only a person can make

What we break
A ticket forcing one architectural decision with product consequences. Two shapes satisfy the acceptance criteria and no design record settles 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
Whether the run stops here turns on judgement, so it is a standing probe scored as a stop rate per model rather than a pass/fail control. So far the fork is noticed, then reclassified as something smaller, and the run carries on.
Measured
measured 2026-08-12: did not fire, 0/1 β€” the probe's stop rate is the datum, and zero is a legitimate value
S5

Work the workspace cannot own

What we break
A ticket naming three areas of work, one of which β€” the registry server β€” is not a repository in the workspace. That requirement has no owner among the repositories present.
Should stop
BLOCKED-UNMAPPED-SCOPE at the work split, before any repository is touched
Correct recovery
None. Stopping is correct. Quote the requirement it cannot place, propose options, touch nothing.
Why it exists
The legitimate-stop control for missing information β€” the run lacks a repository, not a permission. Cheapest scenario to re-run per model.
Measured
measured 2026-08-12: correct stop taken, 1/1
S6

Two instructions that cannot both be followed

What we break
The ticket pins an exported configuration surface to an exact shape; a seeded design record forbids exactly that shape. No rule says which text wins.
Should stop
BLOCKED-DECISION-GAP at the architecture check, in the first repository
Correct recovery
None. Stopping is correct. Quote both texts, propose options, resolve nothing.
Why it exists
After the T6 loop lands this scenario must still stop β€” a loop that resolves the conflict itself is guessing. The pipeline has no rule of its own for two records in conflict, so the stop rests on model judgement and is re-proven per model.
Measured
measured 2026-08-12: correct stop taken, 1/1
Results → 5 measured runs β€” 4 injected scenarios and the clean path β€” against the nine agreed benchmarks, on claude-sonnet-5.