Implementation Plan: Coord-Read Residuals — Merge/Lanes Planning Reads + Identity-Read Routing
Branch: mission/coord-read-residuals-2185-2186 | Date: 2026-06-26 | Spec: kitty-specs/coord-read-residuals-merge-lanes-and-identity-routing-01KW2M8V/spec.md Input: Feature specification (#2185 Lane A + #2186 Lane B; children of epic #2160, siblings of #2115).
Summary
Route the PRIMARY-partition reads that still resolve through coord-aware resolvers (landing on the empty -coord status husk after #2106) onto the existing read-path seam. Lane A (#2185): ~10 sites in merge/, lanes/, core/worktree_topology reading lanes.json/tasks//meta.json → resolve_planning_read_dir(kind=...), with per-leg splits where one resolved dir feeds both a PRIMARY and a STATUS leg. Lane B (#2186): command-layer identity/type reads (next_cmd.py, owned workflow.py legs, implement.py:1394) → primary_feature_dir_for_mission + _canonicalize_primary_read_handle. Both lanes are backed by a single net-new call-shape scan arm covering the two shapes the ratchet's literal vocabulary cannot see: lanes.json reads (scope merge/+lanes/+core/worktree_topology.py) and meta.json function-call identity reads (scope cli/commands/). The technical approach is consume-not-author: the resolver seam already exists and is in production use; this mission only re-points call sites and extends the gate. Lands after the implement-loop sibling (inherits its whole-src scanner scope widening — but no #2185 pin hand-off exists, since the literal vocabulary is blind to lanes.json/meta.json reads and the merge/lanes/core cluster has zero pins; re-resolves Lane B line citations against merged main). The #2185 regression backstop is the FR-009 divergent-fixture revert-fails test, not a pin drain.
Technical Context
Language/Version: Python 3.11+ (CLI; ruff + mypy clean, McCabe complexity ≤ 15) Primary Dependencies: the read-path resolver seam — specify_cli.missions._read_path_resolver (resolve_planning_read_dir, primary_feature_dir_for_mission, _canonicalize_primary_read_handle); mission_runtime partition authority (MissionArtifactKind, is_primary_artifact_kind); typer, rich (existing CLI stack). No new runtime dependency. Storage: filesystem planning artifacts (kitty-specs/<mission>/: meta.json, lanes.json, tasks/, status.events.jsonl) across PRIMARY checkout vs. -coord git-worktree husk. No database. Testing: pytest; architectural ratchet gates (tests/architectural/test_gate_read_literal_ban.py, test_resolution_authority_gates.py); the merged sibling's already-divergent real git worktree coord fixture (tests/integration/coord_topology_fixture.py), extended with the FR-009 sentinel-husk-meta variant (NOT write_side/topology_fixtures.py::build_coord). Integration-over-stubs (NFR-004). Target Platform: Linux / macOS / Windows CLI (loopback/local only; no network). Project Type: single (library + CLI; src/specify_cli/). Performance Goals: behavioral parity — read-routing only; no measurable runtime change. PRIMARY routing is a no-op on flat topology (NFR-003). Constraints: STATUS-partition reads stay coord-aware (C-001); no silent fallback on ambiguous/coord-deleted handles (C-002, #1848); consume the resolver, never edit its internals (C-002); surface exclusivity vs. the implement-loop ROUTE surface (C-009-mirror); scripts/tasks/ legacy reader untouched (C-EXCL-2167); does not remove the implement.py:1018 fallback (C-EXCL-FALLBACK); lands after the implement-loop sibling (C-SEQ). Scale/Scope: Lane A ≈ 10 sites (3 mixed PRIMARY+STATUS needing per-leg split) + 1 coord-topology integration test; Lane B ≈ 6 command-layer identity sites + 1 net-new gate arm (with synthetic-AST non-vacuity self-test) + floor recompute. Estimated 5–7 WPs.
Charter Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
- Integration-over-stubs (NFR-004): the #2185 acceptance proof drives real code against a real
git worktreecoord fixture with a divergent husk — PASS by design (FR-009). Unit stubs handing in a primary dir are explicitly disallowed. - Gate-can't-self-validate: the net-new identity arm (Lane B) and its remediation co-land in this mission, validated by a pre-merge full-gate dry run + a committed synthetic-AST non-vacuity self-test — PASS (US3/FR-007/FR-008).
- Terminology canon: prose uses "Mission"; no
feature*aliases on active domain objects — PASS. - Sonar/complexity: read-routing edits keep touched functions ≤ 15; per-leg split extractions get focused tests — PASS by constraint.
- Realistic test data: the coord fixture seeds production-shaped
lanes.json/tasks//meta.json(real ULIDs, real WP ids) — PASS (FR-009). - Canonical sources: consumes the documented resolver seam; no improvised path reconstruction — PASS (C-002).
No charter violations requiring Complexity Tracking.
Project Structure
Documentation (this mission)
kitty-specs/coord-read-residuals-merge-lanes-and-identity-routing-01KW2M8V/
├── spec.md # committed (revised post-squad)
├── issue-matrix.md # committed (#2185/#2186 in-mission)
├── plan.md # this file
├── research.md # Phase 0 (3-agent code-state research, summarized below)
├── data-model.md # Phase 1 — the artifact-kind partition + per-site route table
├── contracts/ # Phase 1 — resolver-consumption contract + identity-arm contract
└── tasks.md # Phase 2 (/spec-kitty.tasks)
Source Code (repository root)
src/specify_cli/
├── merge/ # Lane A: forecast.py, executor.py (mixed), resolve.py, done_bookkeeping.py (mixed)
├── lanes/ # Lane A: merge.py, recovery.py (mixed:356), worktree_allocator.py
├── core/worktree_topology.py # Lane A: single swap co-resolves 3 PRIMARY legs
├── cli/commands/
│ ├── merge.py # Lane A: :269 meta.json
│ ├── next_cmd.py # Lane B: :187/:253/:619 identity/type (re-resolved on merged main)
│ ├── implement.py # Lane B: :1394 (was :1389) shared-variable, own anchor
│ └── agent/workflow.py # Lane B: owned identity legs ONLY (re-resolve vs merged main)
└── missions/_read_path_resolver.py # CONSUMED ONLY — not edited (C-002)
tests/
├── architectural/test_gate_read_literal_ban.py # FR-007 new call-shape arm (lanes.json + identity); FR-011 #2187 pin; FR-006 honest-scope note
├── architectural/test_resolution_authority_gates.py # FR-010 floor recompute (honest census)
└── integration/coord_topology_fixture.py # FR-009: reuse merged divergent fixture + add sentinel-husk-meta variant (NOT build_coord); new coord-topology merge test
Structure Decision: single project. Edits are confined to the owned surfaces above; the resolver seam and the implement-loop ROUTE surface (tasks.py, workflow.py route legs, tasks_dependency_graph.py, workspace/context.py, …) are out of scope (C-009-mirror).
Phase 0 — Research (summary; full findings in research.md)
Three independent code-state agents verified against main:
- Kind corrections confirmed: 6 of 10 #2185 issue labels are wrong (3 sites read
meta.jsonnot LANE_STATE; 1 readslanes.jsonnottasks/);executor/done_bookkeeping/recovery:356are mixed PRIMARY+STATUS (debugger fully tracedexecutor.pyfeature_dir→run.feature_dir→status_feature_dirat:503/:560). Route by real partition. - Husk failure mode real:
meta.json/lanes.json/tasks/are PRIMARY-only;next_cmd.py:187/253swallowFileNotFoundError(silent drop);:619(was:631) falls back to defaultsoftware-devtype (wrong-routing). - Gate blindness confirmed: the scanner's literal vocabulary is
tasks/.mddir-join literals only — it is blind tolanes.json(LANE_STATE) AND tometa.jsonfunction-call reads. So the merge/lanes/core #2185 cluster has zero pins (none can be added), and identity reads escape too → a single net-new call-shape arm covering both shapes is needed (FR-007). - #2115 sequencing:
implement.py:1394(was:1389) is correct only via the:1018fallback; guards must precede fallback removal (C-EXCL-FALLBACK).
Implementation Concern Map
> Concerns are NOT work packages. /spec-kitty.tasks translates these into WPs.
IC-01 — Foundational gate: call-shape arm (lanes.json + identity) + floor + fixture
- Purpose: Build the net-new call-shape scan arm covering BOTH the
lanes.jsonshape (merge/+lanes/+core/worktree_topology.py) and the identity shape (cli/commands/), each with a synthetic-AST non-vacuity self-test; recomputeROUTED_CANONICALIZER_FLOOR(honestly — if seam-routing did not move the census, say so). This is the detector that makes BOTH residual classes observable, since the ratchet's literal vocabulary is structurally blind tolanes.jsonand tometa.jsonfunction-call reads. WP01 also owns the divergent-fixture extension (the FR-009 sentinel-husk-meta variant) so every consumer (its own identity tests + the Lane A per-site tests + WP04) shares one divergence definition. The sentinel variant is a *distinct fixture/parametrization that writes a huskmeta.json(sentinel id6KERGF2ZNFBPR91YEZMARG99KS), explicitly OVERRIDING the basecoord_topology_fixture.pyinvariantassert not (coord_mission_dir / "meta.json").exists()*; its precondition binds to the fixture's actual resolved primary id (01KW2E7AFC0000000000000001, the reused sibling's), asserting the sentinel!= ctx.mission_id. - Relevant requirements: FR-007, FR-009 (fixture extension), FR-010, C-003. (FR-006 honest-scope note + FR-011 narrowed #2187 preflight live with the Lane A merge-cluster concern IC-02 / its pin-presence preflight subtask — not here.)
- Affected surfaces:
tests/architectural/test_gate_read_literal_ban.py,test_resolution_authority_gates.py,tests/integration/coord_topology_fixture.py(sentinel-husk-meta variant). - Sequencing/depends-on: none (foundational). Mirrors the sibling's dedicated gate WP to avoid a shared-ratchet-file merge race; the only drainable pin in scope is #2187 (the #2187 drain subtask).
- Risks: identity-arm scope creep beyond
cli/commands/+agent_utils/status.py(or lanes.json-arm creep beyondmerge/+lanes/+core/) would red-CI on out-of-scope strangers (sync/acceptance/policy) — bound each shape. Gate-can't-self-validate → pair with a pre-merge full-gate dry run.
IC-02 — Lane A: merge cluster routing
- Purpose: Route the
merge/PRIMARY reads by real kind, splitting the mixed sites per-leg (STATUS stays coord-aware). No merge-cluster #2185 pin exists to drain (vocabulary-blind); regression coverage is the FR-007 lanes.json arm + the FR-009 divergent fixture. - Relevant requirements: FR-001, FR-002, FR-003, FR-006 (honest-scope note in the pin-presence preflight subtask), FR-008 (merge cluster = arm + fixture coverage, not a pin drain), FR-011 (narrowed #2187 pin-presence preflight subtask).
- Affected surfaces:
merge/forecast.py(:153+:159),merge/executor.py(mixed split),merge/resolve.py(:98PRIMARY_METADATA),merge/done_bookkeeping.py(:237WP-path leg + comment removal, keep status-transactional legs on primary),cli/commands/merge.py(:269). - Sequencing/depends-on: IC-01 (the call-shape arm + divergent fixture present to test against); rebase onto post-implement-loop
mainfirst. - Risks: over-routing a STATUS leg (NFR-001);
done_bookkeepingstatus legs must stay on the meta-bearing primary dir, not be coord-ified. - Brownfield refinement: in
executor.py, route the:976legs DIRECTLY, per-leg —:981/:1003resolve_mission_identity→ META and:997require_lanes_json→ LANE_STATE seam — keeping therun.feature_dirSTATUS leg coord-aware. These legs live in_run_lane_based_merge(def:947), a different function from the:887PRIMARY anchor in_run_lane_based_merge_locked(def:866); do not thread:887through (the prior plan's threading direction was wrong — verified on mergedmain). Inmerge/resolve.pyroute only:98(meta read); leave:63(handle→dir-name canonicalization at the no-silent-fallback boundary) oncandidate_. Do not reintroduce the silentmaintarget-branch fallback (#2139 neighborhood).
IC-03 — Lane A: lanes/core cluster routing
- Purpose: Route the
lanes/+core/worktree_topology+agent_utils/status.py(show_kanban_status, #2187) PRIMARY reads;recovery.py:356per-leg split. The lanes/core sites have no #2185 pins (vocabulary-blind); the only drainable pin in this cluster is #2187 (show_kanban_status, atasks/literal) — drained by the #2187 drain subtask. - Relevant requirements: FR-001, FR-002, FR-008 (the single genuine pin-drain is #2187).
- Affected surfaces:
lanes/merge.py(:68/:198),lanes/recovery.py(:356mixed,:611LANE_STATE; KEEP:664coord-aware — STATUS-write leg feedingemit_status_transition_transactional@:686),lanes/worktree_allocator.py(:360meta.json),core/worktree_topology.py(:138single swap co-resolves three PRIMARY legs),agent_utils/status.py(show_kanban_status:126tasks/glob → resolver, keep:151read_eventscoord-aware; #2187 pin drain. The adjacent:132resolve_mission_identityleg is the #2186 identity class — route tokind=PRIMARY_METADATA, gated by the FR-007 identity arm whose scope now includesagent_utils/status.py). - Sequencing/depends-on: IC-01 (+ IC-02 gate-file chain).
- Risks:
worktree_allocatorchicken-and-egg (reads meta to discover coord) —kind=PRIMARY_METADATAis topology-blind and correct. Never route the:664STATUS-write leg (C-001/#2155 analog). - Brownfield refinement (sizing):
lanes/recovery.py::scan_recovery_statealready carries# noqa: C901(over the complexity ceiling). The per-leg split must extract the PRIMARY-planning read and the status-events read into named helpers + drop the# noqa+ add focused tests — not add another branch. Guardrail:candidate_feature_dir_for_missionis the C-005 STATUS primitive — re-point PRIMARY reads off it, never remove or "converge away" the coord-aware primitive (would break C-001).
IC-04 — Lane A: coord-topology integration proof
- Purpose: Reuse the merged sibling's already-divergent fixture (
tests/integration/coord_topology_fixture.py: STATUS-only husk — notasks//lanes.json/meta.json) — extended by WP01 with the sentinel-husk-meta variant (huskmeta.jsonpresent-but-wrong,mission_id = 6KERGF2ZNFBPR91YEZMARG99KS≠ PRIMARY;lanes.json+tasks/PRIMARY-only). Add a real merge/recovery/topology integration test that fails — on a returned domain value — if any routed read reverts to coord-aware. Do NOT retrofitwrite_side/topology_fixtures.py::build_coord(non-divergent husk, ~26 consumers). - Relevant requirements: FR-009, NFR-003, NFR-004, SC-001.
- Affected surfaces:
tests/integration/coord_topology_fixture.py(fixture extension owned by WP01),tests/integration/(new coord-topology merge test). - Sequencing/depends-on: WP01 (fixture extension) + IC-02/IC-03 (the routed code under test).
- Risks: a non-divergent husk silently passes a broken routing (the squad's CRITICAL finding) — the HARD-precondition divergence assertion (
assert not (husk/"lanes.json").exists(); husk metamission_id == 6KERGF2ZNFBPR91YEZMARG99KS) is the guard.
IC-05 — Lane B: identity routing + ownership table
- Purpose: Emit a definitive per-site ROUTE/KEEP/owned-by-implement-loop table (cross-checked vs the sibling's ROUTE+KEEP list, re-resolved against merged
main), then primary-anchor the genuinely-owned identity sites — including the shared-variable mixed sites with their own anchor. - Relevant requirements: FR-004, FR-005, FR-007 (consumes IC-01 identity arm), FR-008 (Lane B co-land), C-003 (identity routing matches the implement-loop seam model — primary-anchor + caller-side canonicalization, no silent fallback).
- Affected surfaces (citations re-resolved on merged
main):cli/commands/next_cmd.py(:187/:253/:619),cli/commands/implement.py(:1394own anchor),cli/commands/agent/workflow.py(owned identity legs:1282/:2739clean;:1644shared-variable own anchor) — only legs NOT inside the implement-loop ROUTE scope. - Sequencing/depends-on: IC-01 (the arm must exist before its sites can be ratchet-validated); rebase onto merged
mainto re-resolve citations. - Risks: a site falling into the gap between the two missions (neither routes it) — the ownership table must account for every Lane B site, no "verify later".