Lane and worktree friction research
Reader: the operator and the core maintainers. You already know the topologies, the consolidation gates and the status model; this note does not explain them.
Bottom line. Counted as one family, the lane, worktree and coordination machinery is the largest source of friction in the last three months. It is also the dominant source of consolidation and git defects. It is not "most of the friction": it accounts for about a quarter to a third of it, and that is an upper estimate. The persistent per-lane checkout on its own accounts for less than a tenth. The bigger share comes from the coordination topology and the consolidation transaction around it. Recommended direction, in order:
- Tighten the current model now:
- one status read contract;
- an owner for cleanup after a pull request lands;
- pruning by proof.
- Stop lane branches carrying status.
- Write coordination status through the Java write service without a coordination worktree.
- Make lane checkouts short-lived.
The operator ruled on the decisions on 2026-11-06 (section 5.4). Two rulings go further than this note recommends. The default topology becomes size-based, which reverses #5100. Short-lived lane checkouts start now, in parallel with the tighten slice. Do not drop lanes.
Research mission: kitty-specs/lane-worktree-friction-research-01M6QS30/. Every number below
comes from an evidence file there, and each file records the commands that reproduce it.
1. Sources and method
Window: 2026-08-06 to 2026-11-06.
| Source | Population | Evidence file |
|---|---|---|
Tooling-friction tracers (kitty-specs/*/traces/) |
1,169 entries from 239 files | research/tracer-entries.part{1,2,3}.csv, research/tracer-summary.md |
| GitHub issues with a reliability, bug, priority, git, consolidation or status label | 1,365 of 2,550 opened in the window | research/issue-classification.part{1,2}.csv, research/issue-summary.md |
| Named example issues and their linked PRs | 16 issues plus 7 related | research/named-examples.md |
Merged PRs and git churn on main |
see section 2.4 | research/pr-classification.csv, research/pr-churn-summary.md |
| Code, architecture docs and ADRs | lanes, coordination, workspace, consolidation, status | research/mechanisms-and-options.md |
Doctrine in spec-kitty-open-packs (23686fb) |
built-in and internal packs, command skills | research/doctrine-scan.md |
Each tracer entry and issue got one primary cause code, a model attribution and a severity:
| Code | Cause |
|---|---|
| CT | coordination topology and teardown windows |
| WL | worktree lifecycle and cleanup |
| WC | dirty, occupied or wrong-branch write checkouts |
| CG | consolidation gate verdicts on lane content |
| SD | status-surface divergence (a lane checkout's status copy versus the resolved surface) |
| HC | harness or agent confusion about which checkout to use |
| UF | upgrade and migration fan-out across worktrees |
| HT | hooks and lane tips |
| DP | disk and performance |
| NL | not lane or worktree related |
Model attribution says what the friction depends on:
worktree: a persistent per-lane checkout;lane-branch: a branch per lane;coord: the coordination topology;single_branch;none.
Severity is one of data loss, wrong verdict, forced override or wasted time.
How accurate the classification is:
- Tracers. Classified by ordered rules. The analyst reported 85-90% agreement on lane versus not lane, but did not record that sample. An independent re-check found 14 of 16 rows correct, and both errors counted an entry as lane-related when it was not.
- Issues. Classified by a keyword and label heuristic. On a recorded fresh holdout of 45 issues it agreed on lane versus not lane for 42 of 45 and on cause code for 40 of 45. The independent re-check found 16 of 16 correct on lane versus not lane, but only about 75% correct on cause code, mostly because status-surface reads were coded CT instead of SD.
- Severity. The weakest column, at 23 of 45 on the holdout. Treat severity counts as indicative only.
- Validation artifacts. See
research/validation/README.md.
2. Cause taxonomy and numbers
2.1 Share of friction
| Measure | Tracers | Issues |
|---|---|---|
| Lane, worktree or coordination related (any code but NL) | 376 / 1,169 = 32% | 328 / 1,365 = 24% |
Within catfooding issues |
n/a | 123 / 330 = 37% |
Depends on a persistent per-lane checkout (worktree model) |
111 = 9.5% | 94 = 6.9% |
Depends on the coordination topology (coord model) |
166 = 14% | 137 = 10% |
| Share of data-loss records | 12 / 22 = 55% | 41 / 129 = 32% |
| Share of data-loss or wrong-verdict records | 24 / 48 = 50% | 164 / 462 = 35.5% |
Share of domain:consolidation / domain:git issues |
n/a | 72% / 63% |
By cause code (tracers / issues):
| Code | Tracers | Issues | Typical shape |
|---|---|---|---|
| CT | 177 | 119 | coordination chosen by default and removed by hand; teardown races; projection before teardown |
| WL | 96 | 48 | lane worktree missing .venv or packs/; stale or stranded worktrees; unsafe removal |
| WC | 56 | 52 | shared write checkout dirty or occupied; hard resets of the root checkout |
| CG | 8 | 48 | approval bound, lane moved after approval, mixed lanes, evidence windows |
| HC | 36 | 9 | wrong checkout imported through a shared editable venv; commits racing across checkouts |
| SD | 1 | 23 | lane checkout's fork-time status copy read as truth |
| UF | 0 | 12 | upgrade commits into every checkout |
| DP | 0 | 13 | disk use of many checkouts |
| HT | 2 | 4 | lane-tip recorder hooks |
| NL | 793 | 1,037 | tests and CI, planning and tasks tooling, charter and doctrine, review process, upgrade, status model |
Trend. The issue share rises from 15.7% in August to 25.9% in October; within catfooding it goes from 31% to 46%. The first six days of November show 37.7% (61 issues). The tracer share falls over the same months, from 38% to 26%. Coordination teardown windows lead the issues from August to October. Consolidation gate verdicts lead in early November.
Most severe issues (most are P0 or P1; #7198 is P2 and #6791 has no priority label):
- #4753: merge cleanup force-removes lane worktrees with no dirty check.
- #4752: an ungated hard reset of the root checkout.
- #4981, its regression #5570, and #6791: coordination teardown destroys later commits, some at exit 0.
- #5666: a rollback resets the target past a teammate's commit.
- #7198: the late window reads the coordination worktree's working tree, not the branch.
- #7173: 48 landed worktrees, about 36 GB, were never cleaned up, and an upgrade would have committed across 53 extra checkouts.
2.2 Verdict on the operator's observation
The observation is qualified, not confirmed. Three findings support it:
- Largest family. Counted as one family of nine cause codes, lane, worktree and coordination friction is the largest in both sources.
- Dominant in git and consolidation. It accounts for 72% of
domain:consolidationissues and 63% ofdomain:gitissues. Within catfooding its share of issues is rising. - Costly cases, with a caveat. It accounts for half of tracer data-loss and wrong-verdict entries and 35.5% of such issues. Both counts rest on the weak severity column. The more reliable priority labels do not show the skew: the lane share of P0 or P1 issues is 22.4%, slightly below its 24.0% share of all labelled issues. Treat "over-represented in the costly cases" as unproven for issues.
Four findings go against "most":
- Minority share. It is 24-32% of all friction, not a majority, and both figures are upper estimates.
- Aggregation effect. "Largest" depends on that grouping. Taken one cause code at a time, the biggest lane code (CT: 177 tracer entries, 119 issues) is smaller than tests and CI (238 tracer entries, 290 issues).
- Persistent checkouts are rare. Persistent lane checkouts, which "the worktree system" suggests, are under 10%.
- Label selection. Across all 2,550 issues, not just the labelled 1,365, the lane share is at most about 13%.
Most of what operators feel as worktree pain is the coordination topology and the consolidation transaction. The coordination worktree, the coordination branch's projection before teardown and the resume and rollback windows all touch checkouts, so they are easy to read as worktree problems.
Three biases push the measured share in opposite directions:
- Tracer coverage. 25 of 70 Missions in an earlier recon never wrote past their seed entry
(
docs/reports/tracer-friction-recon/2026-09-26/README.md). This hides friction of every kind. - Issue authorship. Issues come largely from QA and squad rounds by two authors, and regression chains such as #4981, #5570, #6871 and #7121 count one defect class several times. This inflates the lane share.
- Subject matter. Missions whose own subject was lanes or coordination contribute a large block of the tracer entries. Without them the tracer share is about 28-30%. Two counts of those Missions differ (42 and 29), and neither is recorded as an artifact.
- Classifier errors. The independent tracer re-check found only false positives, so 32% is an upper estimate.
2.3 Where the operator's ruling bites
The status-surface divergence class (SD) looks small in count: 1 tracer entry and 23 issues.
It is undercounted, because the issue heuristic coded several status-surface reads as CT
(#4970, #4975 and #6299 in the re-check sample).
Its records are severe, though: wrong verdicts, forced --force moves (#6466) and data loss at
exit 0 (#7198). The ruling that a lane checkout's own status files never count is right, but
today it is a convention, not a structure. Section 3.1 explains why.
2.4 Engineering effort spent
Effort follows the same pattern: a large, concentrated share that is still a minority. The
sources are research/pr-churn-summary.md and research/pr-classification.csv.
Merged PRs.
| Population | Lane, worktree or coordination share |
|---|---|
| All 1,305 merged PRs | 166 PRs (12.7%), 11.7% of churn (14.3% with each PR capped at the 95th percentile). 27 of these carry NL:* codes (lane allocation, refactors); without them the count is 139 |
The 748 PRs that touch src/ |
18.4% of PRs, 19.9% of churn |
Lane PRs are about three times larger than the rest (median 1,869 lines against 621).
By cause code:
| Cause | PRs | Churn (lines) |
|---|---|---|
| CG | 40 | 117k |
| SD | 33 | 133k |
| CT | 18 | 61k |
| WC | 16 | 117k |
By model:
| Model | PRs |
|---|---|
| lane-branch | 73 |
| coord | 46 |
| worktree | 23 |
| single_branch | 21 |
Commits on main. 546 of 3,514 src/ commits (15.5%) touch the lane-model areas: lanes/,
coordination/, workspace/, consolidation/, the lane-tip recorder and mission_runtime/.
Their fix share is 67.8%, against a 65.3% baseline for all of src/. Only consolidation/ is
clearly above the baseline, at 70.5%. consolidation/executor.py has the highest fix density
of any hot file: 54 of its 69 commits are fixes.
Release notes. 228 of 525 Fixed entries in CHANGELOG.md for 4.0.0rc1 to rc8 (43%)
mention lane, worktree or coordination terms, peaking at rc5 with 96 of 175. This keyword count
overcounts, but it shows where users met the fixes.
3. Mechanisms
The mechanisms below come from the named examples (research/named-examples.md) and the code
reading (research/mechanisms-and-options.md, which carries file and line citations).
3.1 Copies of shared state in N checkouts, read from a working tree
A lanes_with_coord Mission with N lanes keeps its state in several places:
- the repository root checkout;
- the coordination worktree;
- N lane worktrees, each with a committed fork-time copy of
status.events.jsonl(hidden by sparse checkout only in coordination topologies); - temporary merge, bake and review worktrees.
Readers split into two kinds:
- Working-tree readers:
status/aggregate.py:291(MissionStatus._resolve_read_dir),coordination/surface_resolver.py:1209-1300, andruntime/next/committed_authority.py, which reads the repository root's working tree on purpose. - Git-ref readers:
done_bookkeeping.pyand the status-surface guard.
There is one resolver body (resolve_status_surface_with_anchor,
coordination/surface_resolver.py:1256). At least eight wrappers sit around it, and they return
different directories when a coordination branch is declared but its worktree is empty or not
yet created. In numbers:
resolve_status_surface*is named in 26 files: 62 call sites by the analyst's count, 93 non-definition lines including imports by a plain grep;- 12 call sites build
coord_feature_dir(paths by hand.
This is how #6466 happened: a lane checkout's single-event fork-time log was read as truth, and
the seam "documented its repo_root as 'the primary checkout' but never enforced it" (#6884).
It is also how #7198 happened: "the union stage merges the coordination worktree's working-tree
event log, not branch commits". Lane auto-rebase even carries a dedicated merger for
status.events.jsonl conflicts (lanes/auto_rebase.py:357). Under the operator's ruling those
lane copies are pure carrying cost.
3.2 The coordination branch is a second home that must be projected before teardown
Coordination topologies write lifecycle state to the coordination branch. Consolidation must then project it onto the target before it tears the triple down, which opens a late window:
- #4981, its regression #5570, and #6791 with its first-run twin #6871: data loss or a wrong verdict at exit 0;
- #7198 and #7199: open.
#7265 asks for one design pass on "what the late window must prove and on which tip, covering
resume" before more patches land. The tracers show the cheaper end of the same cost: 62 entries
describe specify choosing the coordination topology on a protected branch, followed by the
scaffold being removed by hand.
3.3 Checkouts outlive the session and the merge, and removal needs ever more proofs
A Mission that lands through a pull request merge leaves its lane and coordination worktrees, its
branches, the .kittify/workspaces records and the lane-tip refs with no owner:
accept --mode prdoes not tear down.- The new landed-worktree prune reports Mission branches as
mission_ownedand leaves them alone (workspace/landed_worktrees.py:20-21and:375).
#7173 states it directly: "No step owns removal" and "The merge outlives the session". Each safe removal then needed another proof:
- #7211: reflogs are lost on removal;
- #7212: check-then-act branch deletes;
- #7214: the landing proof is bound to
origin/<primary>; - #7196: "a Mission lane mid-rebase was judged
landedand pruned (reproduced)".
#7264 collects these into one list of proof obligations for any destructive git operation.
Hand deletion then trips DESTROYED_LANE or LANE_WORK_TIP_UNKNOWN on a later implement.
3.4 Operations fan out into every checkout
spec-kitty upgrade wrote and committed .kittify/metadata.yaml separately in the root checkout
and in every lane worktree (#5457), and #7173 found that an upgrade would have committed across 53 extra checkouts, including open
pull request branches. The
current upgrade skips Mission worktrees (upgrade/worktree_selection.py:164). That stops the
fan-out but leaves long-lived lanes on stale .kittify/ content.
Tracer friction has the same shape:
- 47 WL entries are lane worktrees with no
.venvorpacks/tree (BuiltInPackMissingafter about 95 seconds). - A shared editable venv makes a lane worktree import the root checkout's
src/, for examplekitty-specs/pack-shipped-builtin-override-sanction-01M45WB7/traces/tooling-friction.md:10.
3.5 Anchors and records outlive the attempt that wrote them
The consolidation record outlived its attempt and forced --release-branch (#6770). A resume's
captured coordination tip went stale (#7199). Recovery machinery exists only because branches and
checkouts can disappear or be rewritten while a checkout lives:
- lane-tip refs and their recorder hooks (
lanes/lane_tip.py,policy/lane_tip_recorder.py); - the destroyed-lane decision table (
lanes/worktree_allocator.py:417-520); - the
CRASH_RECOVERYroute; - workspace tombstones;
git reset --hardon resume across four checkout roles;resync_checkouts_to_tip;COORD_STATUS_SURFACE_DIVERGED.
3.6 Each topology has its own guard copy, and single_branch has its own locks
#6715 found a guard that "returns early for any topology other than single_branch".
single_branch turns the one write checkout into a lock across Missions:
- #5550: "the occupant is a WP that no supported command can move".
- #5676: still open.
3.7 Doctrine does not tell agents where status lives
The packs tell implementers to do "ALL file operations" inside the lane worktree (implement guidelines). The internal glossary routes status, notes and the issue matrix to the coordination branch. No pack doctrine says which checkout to read status from. The doctrine also uses three different worktree concepts:
- spec-kitty lane worktrees;
- harness
isolation: "worktree"; - manual PR-landing worktrees.
"Lane" has two meanings as well: a WP lifecycle state and an execution workspace. Agents get
contradictory checkout rules too: never write to the root checkout, yet single_branch WPs run
there. The command skills still teach manual stale-lane refreshes (git merge kitty/mission-<slug> inside a lane worktree). See research/doctrine-scan.md.
3.8 The structural fact: attribution and approval gates read refs; a few readers still need checkouts
The consolidation attribution and approval gates read lane branches and refs, not lane checkouts:
- the approval bound
check_lane(consolidation/approved_bound.py:465); - the
lane_headprobe (status/lane_head.py:103, underrefs/heads/); - the lane re-check at the gate;
- the claim build;
- closed-world, blob and line attribution;
- the work package evidence check (
wp_evidence).
Lane consolidation already runs in temporary detached worktrees.
Five readers still depend on a checkout:
- The for_review gate. It requires the lane checkout to exist (
lanes/for_review_gate.py:280) and counts commits withgit rev-list <base>..HEADrun inside it (lanes/_git.py). Without a live checkout, anin_progress -> for_reviewmove fails, which invites--force. - The late projection window. It reads the coordination worktree's working-tree log (#7198).
- The dirty-checkout preflight. It reads lane checkouts.
- Resume and rollback resync. They reset and resync checkouts.
- Auto-rebase and folding dependency lanes in at allocation. Both run in the lane checkout.
Auto-rebase and the dependency fold-in could run in a temporary worktree. The other three need reworking first.
So lanes can stay branches while their checkouts become short-lived, provided the for_review
gate moves onto refs/heads/<lane> first.
Size of the surface.
| Package | Source lines | Commits in window | Fix share |
|---|---|---|---|
lanes/, coordination/, workspace/, consolidation/ (with policy/lane_tip_recorder.py) |
57,263 | see section 2.4 | see section 2.4 |
- Section 2.4 gives the commit counts from the deeper history fetch.
- About 45 named codes tie to lanes, worktrees, the coordination worktree or the status surface.
- The tests for these packages run to about 122,000 lines.
4. Options
Topology use today (meta.json of the 440 Missions created since 2026-08-06):
| Topology | Missions |
|---|---|
single_branch |
176 |
lanes |
74 |
coord |
53 |
lanes_with_coord |
5 |
| none recorded | 132 |
Reproduce this table with the command in kitty-specs/lane-worktree-friction-research-01M6QS30/research.md.
4.1 How the Java write service changes the picture
ADR 2026-10-22-1 makes one resident write service per repository (D-2). Every lane worktree,
the coordination worktree and the root checkout talk to it, so writer races go. Divergence on the
read side does not:
- The service resolves "which partition, worktree and branch" (D-3).
- During the strangler the CLI still sends the resolved write location.
- Only the file adapter ships (D-6). A non-file adapter needs its own ADR, because the gates rely
on git ancestry and on
lane_headstamps.
So the service is the natural owner of a single status surface. On its own it does not stop a command from reading a checkout's stale copy.
4.2 Options compared
| Option | Removes | Keeps or breaks | Effort | Java service |
|---|---|---|---|---|
| (a) Tighten the current model | the SD class (by gate); the stranded-worktree part of WL; part of CT (after the #7265 design pass) | nothing structural; new refusals in the window before the coordination worktree exists; keeps the 57,000-line surface | small per slice | complementary: the read contract becomes the service's request context (open question 2) |
(g) Lane branches stop carrying status.events.jsonl |
the lane status copies that #6466 read as truth; the lane auto-rebase merger for that file | nothing a gate needs, since lane copies never count under the ruling | small | independent of the service |
| (b) Lanes as branches, short-lived checkouts | most of WL and HC; the after-merge stranding for lanes; most lane-checkout recovery code | keeps the attribution and approval gates, lane_head stamps, lane-tip refs and parallelism; breaks long-lived IDE and agent sessions (harnesses keep a cwd across turns); amends ADR 2026-04-03-1 decision 3 ("one long-lived branch and one worktree" per lane) |
medium | status writes need no lane path |
(c) Default single_branch, lanes opt-in |
the coordination default on the primary branch (62 tracer entries); lane and coordination worktrees on the common path | reverses the binding operator decision on #5100; on a protected target, the root checkout leaves the primary branch on every create; serializes concurrent Missions on one write checkout (#5550, #5676); widens the population without an approval bound | medium | simplest case |
| (d) Drop lane worktrees and code lanes | most of lanes/; the mixed-lane machinery |
parallel multi-agent execution, the reason lanes exist (ADR 2026-04-03-1); per-lane attribution gates lose their subject; test churn (about 57,000 lines in tests/consolidation) |
large | no gain over (b) plus (e) |
| (e) Status written to the coordination branch by git plumbing; no coordination worktree | the coordination worktree's codes (COORD_STATUS_SURFACE_*, COORDINATION_WORKTREE_*, most COORD_SEED_*); the "declared but not created" wrapper rules; the #7198 class |
keeps the coordination branch as the lifecycle home (#7266 favours keeping it); applies only to coordination topologies; breaks events tail |
medium to large | must go through the service (see below) |
Option (a): tighten the current model.
- One status read contract, defined per topology.
- An owner for cleanup after a PR landing.
- Prune by proof (#7264).
- The for_review gate moved onto the lane ref.
Option (g): lane branches stop carrying status.events.jsonl. Apply the sparse exclusion
that coordination topologies already use (coordination/workspace.py:416) to lanes too, or
filter the tree when a lane is cut. The lane auto-rebase merger stays while existing lane
branches still carry the file.
Option (b): lanes as branches, short-lived checkouts. A lease is held per command or per session, and releasing it commits or refuses.
- Effort. Reshape
worktree_allocator.py(about 2,180 lines) into a lease. Move auto-rebase and dependency merges to temporary worktrees. Move the for_review gate onto refs. Update the prompts and skills inspec-kitty-open-packs. - Hidden costs. The lane-tip hook exits on a detached HEAD (
policy/lane_tip_recorder.py:67), so temporary-worktree merges need explicit record points or they feedLANE_WORK_TIP_UNKNOWN. Every re-entry takes the crash-recovery route. Review worktree creation and review's lane fast-forward assume a live checkout (agent/workflow.py:2298). - Main new risk. Uncommitted work at release.
Option (c): default single_branch, lanes opt-in.
- Today's code. The non-pr-bound default is
coordwhenever the current branch is the primary branch, protected or not. Protection is read only on the--pr-boundarm (cli/commands/agent/mission_create.py:441-449). - The decision it reverses. The binding operator decision on #5100 makes
single_branchexplicit-only (mission_create.py:386-392). - The approval bound. Closing the
single_branchapproval-bound residual is not small. On one shared write branch, later work packages' commits sit past an earlier approval stamp, so the bound needs per-WP windows (the #5046 machinery) or an amendment to ADR2026-10-04-5. - Effort. The default logic, the
specifyhelp text that mirrors it, docs and tests.
Option (e): status written to the coordination branch by git plumbing. There is no
coordination worktree; reads use git show <ref>:<path>.
- Through the service only. It must go through the service, because ADR
2026-10-22-1D-5 forbids a Python writer. - A step of its own. Make it a step after migration step 4, framed as a change to the git port and write location (open question 2), not a storage adapter. Step 4 already moves every row family in one cut, and the migration gates each step on the previous one.
- Coordination branches only. A compare-and-swap plumbing write to a branch that is checked
out (the primary or Mission branch in
single_branchorlanes) would desync that checkout. - The four premises. D-6 names four premises that each need their own ADR before storage
changes: ADR
2026-10-13-2D-2, ADR2026-10-01-2D-6, ADR2026-02-09-3merge semantics, and the gates' reliance on git ancestry andlane_head. Check (e) against each. - Tail reader.
events tailreads the log with a byte-offset cursor over a file (status/tail_reader.py:70).
No option removes everything. Compare-and-swap branch deletes (#7212) and the landing proof against the right remote (#7214) are needed under every model. Removing a branch always needs the #7264 proofs.
5. Recommendation
Ranked:
- (a) Tighten, now.
- Every other option depends on it.
- It targets the classes with the worst severity per record: SD, the after-merge part of WL, and the CT late window.
- (g) Stop carrying status on lane branches. It is cheap, it removes the copies the operator's ruling already disowns, and it needs no new authority.
- (e) Write status to the coordination branch through the Java write service, with no
coordination worktree.
- It addresses most of the coordination-topology friction (the largest lane cause) without reversing #5100.
- It keeps protected-primary Missions working.
- It is sequenced after migration step 4.
- (b) Short-lived lane checkouts for Missions that use lanes. Do this after the for_review
gate moves onto refs and ADR
2026-04-03-1is amended. - (c) Only as an explicit operator decision.
- The evidence favours it on friction (62 tracer entries).
- It reverses #5100, moves the root checkout off the primary branch, and widens the unbounded population.
- It is not recommended before the
single_branchapproval bound has per-WP windows.
- (d) Reject. It costs parallelism for no benefit that (b) plus (e) do not give.
5.1 Phased path
| Phase | Content | Exit evidence |
|---|---|---|
| 0. Contain | Land the open safety fixes under the #7264 proof-obligation ADR, in the order the triage issues set: #7239, #7237, #7240. Do the #7265 design pass, then land #7228 absorbing #7226. Land #6884. | the #7264 ADR accepted; #7198, #7199, #7211, #7212 and #7214 closed |
| 1. One read contract | See the list below the table | gate green with an empty allowlist; wrappers collapsed; the for_review gate passes with the lane checkout absent |
| 2. Ownership and copies | Give Mission worktrees an owner after a PR landing: accept --mode pr, or the prune with a merge-commit proof. Apply (g). Fix the pack doctrine upstream in spec-kitty-open-packs: add a status rule and use one worktree vocabulary. |
no stranded Mission worktrees after a PR landing; no lane branch carries status.events.jsonl |
| 3. Status off the coordination checkout | Write the ADR set that D-6 requires. Deliver (e) as a step after write-service migration step 4. | coordination worktree removed; COORD_STATUS_SURFACE_* codes retired |
| 4. Short-lived lane checkouts | Amend ADR 2026-04-03-1. Introduce lease-based lane checkouts with explicit lane-tip record points. Run auto-rebase and dependency merges in temporary worktrees. Migrate by dropping clean, recorded lane worktrees. |
no lane worktrees left between sessions |
Phase 1 covers four changes:
- One read contract, defined per topology. Read status from the ref only where the write path
commits in the same unit of work. Otherwise read one named working-tree surface. The flat
status shell appends without committing, which is why
runtime/next/committed_authority.pyreads the working tree. - No wrapper-local fallback.
- No read of a lane checkout's status copy.
- The for_review gate moved onto
refs/heads/<lane>, plus an empty-allowlist architectural gate.
5.2 First slice
Phase 1: one status read contract. It turns the operator's ruling of 2026-11-06 into structure:
- It finishes what #6884 starts.
- It collapses the wrappers onto
resolve_status_surface_with_anchor. - It defines the read semantics per topology.
- It moves the for_review gate onto the lane ref.
- It gates the call sites.
It also settles the Java service's open question 2, which needs one location rule before resolution can move into the service. Run it as its own software-dev Mission; this note does not start it.
Phase 0's #7264 ADR is the parallel priority for the data-loss cases (#4753, #4752).
5.3 Decisions the operator needs to make
- Default topology (#5100). Keep the binding decision that
single_branchis explicit-only (recommended for now), or reverse it? If you reverse it,single_branchneeds per-WP approval windows first, and you must accept that the root checkout leaves the primary branch on create. - Coordination branch. Keep it as the lifecycle home, with the #7266 artefact-placement decision? (e) removes only its worktree.
- Status by plumbing. Approve the ADR set D-6 requires, and the step after migration step 4 that writes the coordination branch without a worktree?
- Lane status copies. Approve (g): lane branches stop carrying
status.events.jsonl. - Short-lived checkouts. Amend ADR
2026-04-03-1decision 3? Accept that releasing a lane checkout commits or refuses, which changes behaviour for agents and IDE sessions? - Cleanup owner after a PR landing.
accept --mode pr, or the landed-worktree prune with a merge-commit proof? - Destructive operations. Adopt the #7264 proof-obligation ADR as the single authority for prune, lane teardown and consolidation cleanup?
5.4 Operator rulings (2026-11-06)
These rulings are stijn-dejongh's of 2026-11-06. The dispatcher session relayed them into the research session; they are recorded here as received.
| # | Decision | Ruling |
|---|---|---|
| 1 | Default topology (#5100) | Reverse explicit-only. The default is chosen by Mission size: single_branch when the finalized tasks have at most 2 WPs, or when lane computation yields a single lane; lanes otherwise. An explicit --topology always wins. |
| 2 | Coordination branch | Keep it as the lifecycle home for coordination topologies. |
| 3 | Lane status copies | Approved: lane branches stop carrying status.events.jsonl. |
| 4 | Status by plumbing | Approved: write the ADR set for the Java write service writing coordination status without a coordination worktree. This follows migration step 4 (ADR 2026-10-22-1 D-6). |
| 5 | Short-lived checkouts | Approved, start now: amend ADR 2026-04-03-1 for short-lived per-command lane checkouts, where releasing a checkout commits or refuses. This runs in parallel with the tighten slice. The for_review gate must move onto the lane branch. |
| 6 | Cleanup after landing | Both accept and prune, without waiting for the PR merge, which is asynchronous. Worktree and branch cleanup is the final step of the consolidation phase, under the destructive-operation proofs. doctor workspaces --prune stays the proof-based backstop for anything left. |
| 7 | Destructive operations | Adopted: the #7264 proof list is the single authority for every destructive git operation (prune, lane teardown, consolidation cleanup), enforced through git/destructive_guard.py. |
What the rulings change in this note:
- Recommendation order. Ruling 5 moves (b) up beside (a). Ruling 1 replaces "(c) only as an operator decision" with a size-based default.
- Design point for the ruling 1 ADR. Topology is chosen at
mission create, before any WP exists. The size rule therefore has to apply atfinalize-tasks, either by deciding there or by re-stamping a provisional topology. The re-stamp must not strand a coordination branch or a lane allocation made in between. - Risks that grow with ruling 1. More Missions become
single_branch, so more of them are neither stamped nor approval-bounded (section 4.2). Per-WP approval windows should land with the size-based default, or before it. #5550 and #5676 (the shared write checkout as a lock across Missions) matter more as well. - Effect of ruling 6. Cleanup moves out of "after the PR merge" and into consolidation. Section 3.3's no-owner gap closes for Missions that consolidate through the CLI. The prune remains for checkouts that never reach that step.
5.5 Proposed slices (for the operator to approve; none filed or started)
| Slice | Ruling | Content | Depends on |
|---|---|---|---|
| S1. One status read contract | (a) | One read contract per topology: read from a ref only where the write commits in the same unit of work. Collapse the wrappers onto resolve_status_surface_with_anchor. Add an empty-allowlist call-site gate. Finish #6884. |
none |
| S2. For_review gate on the lane branch | 5 | Count lane commits with git rev-list <base>..refs/heads/<lane> instead of HEAD in the lane checkout (lanes/for_review_gate.py:280, lanes/_git.py). Exit: the gate passes with the lane checkout absent. |
none |
S3. ADR 2026-04-03-1 amendment: short-lived lane checkouts |
5 | Lease per command, release commits or refuses. Explicit lane-tip record points for temporary detached worktrees (the hook exits on a detached HEAD, policy/lane_tip_recorder.py:67). Re-entry without the crash-recovery route. Harness cwd across turns. Review worktree and fast-forward. Migration of existing lane worktrees. |
S2 for the implementation, not the ADR |
| S4. Destructive-operation authority | 7 | Accept the #7264 ADR. Enforce it through git/destructive_guard.py. Land #7239, #7237 and #7240 under it, in that order. |
none |
| S5. Cleanup as the final consolidation step | 6 | Worktree and branch teardown becomes the last consolidation step, also on the accept --mode pr route, without waiting for the PR merge. doctor workspaces --prune is the backstop. |
S4 |
| S6. Lane branches without status copies | 3 | Sparse exclusion or a tree filter at lane cut for lanes as well as coordination topologies (coordination/workspace.py:416). A migration for existing lane branches. Retire the lane auto-rebase merger for the file once no lane carries it. |
S1 |
| S7. Size-based default topology | 1 | ADR reversing #5100: the size rule, its home (finalize-tasks, decide or re-stamp), the explicit --topology override, the specify help text, docs and tests. |
per-WP approval windows for single_branch (S8) |
S8. single_branch approval bound |
1 (risk) | Per-WP approval windows on a shared write branch (the #5046 machinery) or an amendment to ADR 2026-10-04-5. |
none |
| S9. Coordination status without a coordination worktree: the ADR set | 2, 4 | The ADRs D-6 requires: committed-ledger publication (ADR 2026-10-13-2 D-2), the read API pull (ADR 2026-10-01-2 D-6), event-log merge semantics (ADR 2026-02-09-3), and the gates' reliance on ancestry and lane_head. Also covers the events tail cursor. Delivered after write-service migration step 4. |
write-service step 4 |
| S10. Late-window design pass | (a), phase 0 | The #7265 design pass, then #7228 absorbing #7226. | none |
| S11. Doctrine upstream | (a) | In spec-kitty-open-packs and the command skills: a status-surface rule, one worktree vocabulary, current consolidate wording, cleanup and recovery guidance for DESTROYED_LANE and LANE_WORK_TIP_UNKNOWN. |
S1, S3 |
6. Limitations
- Classification is rule-based with sampled hand checks. Read the cause shares within a few points, and treat severity counts as indicative only.
- Tracer coverage is uneven. Op records, retrospectives and design-decision tracers were not mined for friction.
- The issue population is label-selected. Across all 2,550 issues opened in the window, the lane share is at most about 13%.
- The CT code is broad. It includes resume, abort and rollback windows that are only partly caused by the coordination topology.
- "Lane" is overloaded (WP lifecycle lane versus execution lane), so the keyword counts overstate the topic. The classifications corrected this by hand where sampled.