Contracts
commands.md
Command Contracts: deterministic write commands
CLI command contracts for the deterministic writers. All route through write_target(kind) + commit_for_mission; all emit the structured write-routing result (data-model I-W1/I-W2); all are idempotent (FR-012).
acceptance-verdict (FR-001)
- Inputs:
--mission <handle>,--criterion <id>,--result <pass|fail|pending>,--verification-method <text>,--evidence <text?>,--actor <name>,--json. - Behavior: mutates the criterion row on
acceptance-matrix.json, recomputesoverall_verdict(never authored), preserves negative-invariant provenance, routes to COORD. - Also: canonical
acceptpersists the recomputedoverall_verdicteven with no negative invariants (I-A2; #2318 comment). - Output:
{ok, kind:"ACCEPTANCE_MATRIX", destination_surface, row_or_entry_ref, overall_verdict}. - Errors: unknown criterion → actionable error; unroutable → zero-write refusal (I-W2).
issue-verdict (FR-003)
- Inputs:
--mission <handle>,--issue <#ref>,--verdict <fixed|verified-already-fixed|deferred-with-followup|in-mission>,--wp <id?>,--evidence-ref <text?>,--actor <name>,--json. (Reconciled WP05:verdict/evidence_refover the canonicalIssueMatrixVerdictclosed-set, NOT the earlier--status open|addressed|…sketch — the approve gate does anisidentity check on the enum; see data-model.md "Vocabulary decision".) - Behavior: mutates the row (keyed by
issue_ref) onissue-matrix.json; migrates the mission from legacy.mdon first write (FR-013); routes to COORD. No markdown render. Idempotent (FR-012); keep computed/derived fields authoritative. - Output:
{ok, kind:"ISSUE_MATRIX", destination_surface, row_or_entry_ref, migrated}.
tracer-append (FR-006)
- Inputs:
--mission <handle>,--category <tooling-friction|approach|design-decisions>,--entry <text>,--actor <name>(required, non-empty),--json. - Behavior: appends a dated attributed entry routed to the COORD surface via
commit_for_mission; zero lane-branchkitty-specs/commit; idempotent on identical content; never uses theread_dir(RETROSPECTIVE)short-circuit. - Output:
{ok, kind:"TRACER_FILE", destination_surface:"coord", row_or_entry_ref}. - Errors: empty
--actor→ actionable error (guard #2960).
issue-matrix migrate (FR-013)
- Inputs:
--mission <handle?>(omit for all missions),--json. - Behavior: shared migration sub-module — converts legacy
issue-matrix.md→issue-matrix.json; one-shot bulk swap-over when--missionomitted. Same sub-module backs failover-read + migrate-on-write. - Output:
{ok, migrated_missions:[...], skipped:[...]}.
Discovery / gate behavior (FR-004, FR-005)
detect_issue_references(paths)scansspec.md+tasks/+plan.md+research.md+analysis-report.md+contracts/(one shared canonical definition).- A new merge-time issue-matrix completeness gate is added to
merge_gates.py. - Post-merge review + gates record Gate 4
not_applicablewhen zero canonical references exist; fail-closed when references exist.
coord-authority-gate.md
Contract: Coord-Authority Gate Idiom (FR-010) — implements ADR 2026-06-26-1-single-authority-seam-and-call-site-gate
Not a new ADR — a citation contract for the gate change. Cites 2026-06-24-1 C-006, 2026-06-26-1-single-authority-seam-and-call-site-gate (gate-extension mechanism + non-vacuity confirmation), 2026-07-23-1.
ADR status note (m5): 2026-06-26-1-single-authority-seam-and-call-site-gate is currently status: Proposed but is de-facto shipped (the gate it governs is live). This mission re-pins the floor that ADR governs (Move A) and names it the Move B amendment target. Escalated decision (pending HiC): ratify it to Accepted within WP02, or keep it Proposed-but-shipped with this acknowledgment. Until ratified, treat it as binding-in-practice.
Move A (default, STRENGTHENING) — route decisions/emit.py off the allowlist
Convert decisions/emit.py:_mission_dir:71 to the kind-aware seam so it no longer calls the kind-blind resolve_feature_dir_for_mission. This drops the live write census 4→3 and requires, in the same change: 1. Re-pin COORD_AUTHORITY_WRITE_FLOOR 4→3 and coord_authority_baseline (with rationale). 2. Remove the stale resolution_gate_allowlist.yaml entry (staleness twin-guard). 3. Remove emit.py from _COORD_WRITE_BY_DESIGN (else test_coord_authority_by_design_modules_classified_write reds). The status.events.jsonl COORD write MUST still land on the coord surface (no regression to primary).
Route ONLY emit.py — the other three write sites stay. widen/state.py:63, agent_tasks_ports.py:322, and lanes/recovery.py:765 are by-design sanctioned kind-blind coord writes and MUST remain on resolve_feature_dir_for_mission; the re-pinned floor (3) counts them to prove non-vacuity. Routing all four → census 0 → vacuous gate (forbidden). See the by-design section of write-seam-adoption.md.
Non-vacuity invariant: the live kind-blind coord-write census MUST NOT drop below the re-pinned floor of 3. Lowering the floor again requires the same rationale + a preserved non-vacuity proof.
Move B (conditional) — recognize write_target(<COORD kind>) as sanctioned authority
Author ONLY if a seam-routed writer must resolve via the kind-blind resolver. Recognition MUST be def-use gated (the result of write_target(kind=...) flows to the write), mirroring is_def_use_canonical. A module/name-proxy exemption is forbidden. Move B, if triggered, is an ADR amendment of 2026-06-26-1-single-authority-seam-and-call-site-gate (the gate mechanism lives there), not a contract-only predicate widen — amend the ADR in the same change.
Non-vacuity proof (DIRECTIVE_003 obligation)
Record, in this contract and the gate module docstring, a test proving the gate still FAILS on: (a) a write_target() call with no kind, and (b) a re-introduced kind-blind wrong-surface write. A literal-vs-literal assertion is vacuous and disallowed.
Sequencing
FR-010 is an enabler — it blocks the writer-routing concerns (IC-03…IC-07) only insofar as those route emit.py; new writers routed purely via write_target are invisible to the gate (D-6).
merge-driver-algorithm.md
Contract: Row-Aware Matrix Merge Driver Algorithm (FR-008)
Not a new ADR — a citation contract for the driver rewrite. The row-aware drivers replace the whole-file _write_more_filled_side behavior (merge_driver.py:333/347/357) for spec-kitty-acceptance-matrix and spec-kitty-issue-matrix over the FR-002 structured JSON schema. Cites 2026-06-24-1 (partition authority), 2026-07-23-2 (no consolidation abort).
Inputs — 3-way, base-aware
Git invokes the driver with the merge placeholders:
%O— the common-ancestor (base) version of the matrix.%A— our (current-branch / consolidation-base) version.%B— their (incoming-lane) version.
The driver MUST use all three. A 2-way (%A/%B only) merge cannot distinguish "the other side added a row" from "our side deleted a row" and re-leaks clobber.
Where %O comes from (topology-resolved, confirmed against code 2026-07-29). %O is the git merge-base blob on the seam-resolved matrix surface for the active topology — the coord lineage under a coord topology, the primary/target_branch lineage under a flat (SINGLE_BRANCH/LANES) topology. ISSUE_MATRIX/ACCEPTANCE_MATRIX are COORD-partition kinds (artifacts.py:172-183); declared_read_surface/resolve_placement_only return PRIMARY for them in flat topologies and COORD in coord-topology (resolution.py:1602-1607, :1211-1217). FR-009's recorded primary lane base supplies %O only for PRIMARY-partition artifacts (planning docs); matrices are FR-007-routed and, under coord topology, serialize onto the single per-mission coord worktree (commit_router.py:248-306) — they never diverge on lane branches, so the lane base is orthogonal to their %O. The disjoint-row union therefore bites primarily on flat topology (matrices on target_branch, lanes can diverge them) and on coord↔target integration merges, both drawing %O from the resolved surface's own lineage. There is no hard IC-08 → IC-01 dependency: WP11 needs a real (non-synthetic) %O from the resolved consolidation lineage, which exists independently of WP01's recorded planning SHA.
Row-key canonicalization
Rows are keyed by a canonical identity, not by line position or dict order:
- acceptance-matrix rows →
criterion_id(e.g.FR-001, DoD id). - issue-matrix rows → the canonicalized
issue_ref(e.g.#1726; normalizeGH-1726/#1726/bare1726to one form before keying).
Two rows with the same canonical key are the same row; the merge reconciles their fields. Distinct keys union.
Intra-side collision guard: if two distinct raw rows on one side (%A, %B, or %O) normalize to the same canonical key (e.g. GH-1726 and #1726 in the same file), the driver MUST NOT silently collapse them — it raises a structured error (or deterministically dedupes with a recorded warning). A silent collapse drops a row and is a clobber by another name.
Per-row reconciliation (base-aware three-way)
For each key present in %O ∪ %A ∪ %B: 1. Added on exactly one side (absent in %O, present on one of %A/%B) → take the added row. 2. Added on both sides with equal content → take either; with differing content → field-level merge, else structured conflict (never silent pick). 3. Changed on one side only (differs from %O on exactly one side) → take the changed side. 4. Changed on both sides → field-level merge where fields are disjoint; if the same field diverges, emit a structured conflict result (fail-closed, no consolidation abort per 2026-07-23-2).
Delete-vs-stale disambiguation (the base-aware payoff)
A key present in %O but absent on one side:
- absent on one side, unchanged on the other → intentional delete; drop the row.
- absent on one side, changed on the other → the change wins (a stale non-edit does not resurrect-then-delete); keep the changed row.
A key absent in %O and absent on one side is simply a one-sided add (rule 1) — never a delete. This is precisely the case a 2-way merge gets wrong.
Output & determinism
- Output rows in a stable canonical order (sorted by key) so the merge result is byte-deterministic regardless of input row order — required for idempotence (FR-012) and clean re-merges.
- Preserve computed/derived fields' provenance; the driver reconciles stored fields only and never re-authors a computed verdict (acceptance
overall_verdictstays a property, not a merged field).
Registration (M6 — 3 sites, see IC-08)
The driver binding MUST be updated in all three places or the algorithm is inert on real repos: the .gitattributes pattern (repointed /issue-matrix.md → issue-matrix.json), cli/commands/init.py:73,194 (new-repo attributes), and a new forward migration** (do NOT mutate historical m_3_2_6).
Security campsite (#2970 — E1)
The row-aware rewrite folds the 5 S2083 path-injection BLOCKERs in merge_driver.py: a red-first repro of the injection precedes the fix; the hardened path handling must not weaken any merge-reconciliation rule above.
Verification
- Disjoint-row union test (two lanes, different keys) → no clobber.
- Stale-residue test (base row deleted on one side, untouched on the other) → row dropped.
- Same-field divergence test → structured conflict, no silent pick, no abort.
- Byte-determinism test (shuffled input row order → identical output).
- #2970 path-injection red-first regression.
write-seam-adoption.md
Contract: Write-Seam Adoption (FR-007) — implements ADR 2026-06-24-1 C-006
Not a new ADR — a citation contract for the adoption pass. Cites 2026-06-24-1 (partition + no-parallel-resolver), 2026-06-24-2 (target_branch/meta.json anchor), 2026-07-23-1 (COORD is a resolved surface, not a topology condition).
Rule
Every artifact write that today hand-derives a destination or takes a caller-resolved feature_dir MUST resolve its surface through PlacementSeam.write_target(kind) and materialize via commit_for_mission. No per-command exception to the kitty-specs/ guards; no second write resolver.
The TRUE bypass set (route these — NOT the seam's own engine)
- Caller-resolved-
feature_dirmatrix writers:write_acceptance_matrixcommit sites → route:accept.py::_commit_coord_residuals(viawrite_artifact) andbackfill_provenance.py(coord-aware discovery viaread_dir(ACCEPTANCE_MATRIX)). ✅ landed in WP04. The issue-matrix writer → routed viawrite_target(ISSUE_MATRIX). ✅ landed in WP05. - RAW BY DESIGN, do NOT route (WP04 adjudication, reviewer-confirmed):
gates_core.py::_evaluate_acceptance_matrixpersist-on-accept write is deliberately the raw, uncommitted writer — under--no-commit/mutate_matrix=Trueit MUST mutate but never commit (#1883/#1908); the commit happens at theaccept.pyboundary.post_consolidation.py::verify_deferred_invariantsstays raw: itsconsolidated_treecan be a bare CI/dispatched-Op checkout with no.worktrees/sibling (socommit_for_missiontopology resolution can't apply), the module's docstring makes zero merge-coupling load-bearing, and post-consolidation the coord/primary partition has already collapsed onto one branch —feature_diris the only correct home, so the raw write is NOT a wrong-surface bypass. - The tracer writer (new).
- Of the four coord-authority-gate write sites, route ONLY
decisions/emit.py:71this mission (paired with the FR-010 gate-route in coord-authority-gate.md). ✅ landed in WP02. - The core helper
coordination/write_seam.py::write_artifact— parameterizedwrite_target(kind)+commit_for_mission, zero-write refusal (FR-011) disclosing #3033, idempotent structured result (FR-012). ✅ landed in WP03 (0ad388694). This is what the matrix/tracer writers (WP04/05/07/10) consume. - ~~
#2663:implement.py::_partition_files_for_commitpartition arm.~~ DEFERRED → #3071. - ~~
status/emit.pywrite (#2966 slice, route-only per C-003).~~ DEFERRED → #3071.
Deferred out of this mission (→ #3071, 2026-07-29)
WP03 rediscovered red-first that these two "generic bypass" adoptions collide with deliberate, pinned prior-mission invariants — the very #2160/#2966 clusters C-006 marks out-of-scope:
- #2663 (implement-partition): routing it through the seam breaks
test_implement_coord_idempotency.py(docstring: "the C-004/#2160 deferral leaves it un-partitioned", PR #2662 finding) andtest_precondition_ref_unification.py::TestWriteSideDerivesFromTheSharedExpression(write/read pinned to the literal_commit_target_ref_forexpression;commit_for_missionresolves its own ref fromkindand cannot satisfy the literal-unification guard). Forcing it reopens #2662/#2648/#2650/#2160. - #2966 (
status/emit.py): has no git-commit call — pure filesystem I/O viacanonicalize_feature_dir(a path resolver), so there is nothing for a commit-helper to route without re-implementing transition/commit logic (forbidden by C-003). #2966's author flagged it for its own spec.
Both are resolved in #3071 within the #2160 authority-unification effort (once the verbatim-commit deferral and literal-ref guard can be lifted coherently). Do NOT fold into a cleanup pass.
Do NOT route — by-design sanctioned coord writes (keep the gate non-vacuous)
widen/state.py:63, agent_tasks_ports.py:322, lanes/recovery.py:765 are sanctioned kind-blind resolve_feature_dir_for_mission coord writes. They MUST stay on the kind-blind resolver: the coord-authority gate's COORD_AUTHORITY_WRITE_FLOOR counts them to prove non-vacuity (test_resolution_authority_gates.py:704-723/1661-1716). Routing all four → live write census 0 → the gate asserts over an empty set → vacuous (forbidden by ADR 2026-06-26-1-single-authority-seam-and-call-site-gate). Routing emit.py alone drops the census 4→3; re-pin the floor to 3 (coord-authority-gate.md Move A).
Non-vacuity invariant: the live kind-blind coord-write census MUST NOT drop below the re-pinned floor (3). A future mission that wants to route one of the three MUST add a replacement by-design site or lower the floor with the same rationale + a preserved non-vacuity proof.
Do NOT route (seam's own engine — routing is circular)
commit_router ×4, write_target_degrade, status_transition:300 (the FR-006 mirror), merge infra — the raw resolve_placement_only census is not the target.
Recursion guard (Ledger-M16)
Public boundary → seam; internal callers/leaves (retrospective/writer.py, the read_dir(RETROSPECTIVE) short-circuit) → the leaf directly. A new writer beneath the short-circuit calls the leaf/write_target, never read_dir.
Verification
The read-side census, C-008 shards, and the coord-authority gate stay green; a re-introduced hand-derived write to the wrong surface must still fail a gate (non-vacuity).