Contracts
reader-partition-contract.md
Contract — reader resolves the authoritative partition (Findings A & B)
Tracer-append (Finding A / #4959) — retrospective/tracer_writer.py
Guarantees: 1. _read_current_coord_content fails closed (propagates the seam raise) when the coord surface is unresolved/unmaterialised — never returns "" that the writer turns into a _default_header clobber. 2. An UnicodeDecodeError on an existing traces file → refuse, not "" (corruption is not "no content"). 3. A genuinely-absent file on a resolved coord surface still yields empty (legitimate first append) — no regression for the normal path.
Acceptance (red-first):
- AC-T1: from an unmaterialised-coord checkout,
agent tracer-appendon a populatedtraces/<cat>.mdleaves the file byte-intact and fails closed (0 findings lost). Pre-fix: file clobbered to header+entry. - AC-T2: undecodable byte → refuse, file untouched.
- AC-T3: materialised, present file → appends normally.
Decision ledger (Finding B / #4966) — decisions/service.py
Guarantees: 1. _resolve_mission_id / _mission_dir resolve meta.json via PRIMARY_METADATA (never STATUS_STATE/coord), so a materialised status-only husk no longer yields MISSION_NOT_FOUND. 2. The decision service and acceptance/__init__.py::_has_blocking_clarification_marker read the ledger from the same PRIMARY partition — no split-brain; a deferred decision reaches the index accept reads; accept is not permanently blocked. 3. The decisions/service.py:157-167 docstring is corrected (a materialised coord worktree does not carry meta.json).
Acceptance (red-first):
- AC-D1: on a coord mission with a materialised husk,
agent decision openresolves (noMISSION_NOT_FOUND). Pre-fix:MISSION_NOT_FOUND. - AC-D2: a deferred→resolved decision lets
acceptproceed (no permanent block);verifyreflects the same state. Pre-fix: permanent block, no CLI way out. - AC-D3: non-coord / flat missions' decision flows unchanged (regression).
seam-fail-closed-contract.md
Contract — placement read seam fail-closed (Finding A / #4959)
Surface: src/mission_runtime/resolution.py::_classify_artifact_surface (+ placement_seam/PlacementSeam.read_dir); new exception in coordination/surface_resolver.py.
Guarantees
1. For a coord-partition kind on CoordState.UNMATERIALIZED, the seam raises CoordinationWorktreeUnmaterialized (a StatusReadPathNotFound subclass) instead of returning empty-PRIMARY. 2. DELETED still raises CoordinationBranchDeleted (#4403 unchanged); MATERIALIZED still returns the COORD surface; EMPTY/NONE unchanged (out of scope). 3. PRIMARY-partition kinds never raise (short-circuit before probe) — non-coord topologies (SINGLE_BRANCH/LANES/flat) and all primary reads are unaffected. 4. The new exception's next_step names the materialise recovery (the branch exists), never flatten (that would be false for UNMATERIALIZED). 5. Existing StatusReadPathNotFound / CoordinationBranchDeleted catchers absorb the new sibling unchanged (no regression in sanctioned read-only degraders / aggregators).
Acceptance (red-first)
- AC-S1: a coord-partition
read_diron UNMATERIALIZED raisesCoordinationWorktreeUnmaterialized; pre-fix it returns an empty-PRIMARY path. - AC-S2:
DELETEDstill raisesCoordinationBranchDeleted; a PRIMARY-kind read on the same mission does not raise (regression pin). - AC-S3: sanctioned read-only degraders (
read_dir_degrade.py,review/cycle.py) still degrade (their catch absorbs the sibling); a representative no-catch coordSTATUS_STATEreader now fails loud at a sane boundary (not a raw traceback).