Contracts

gate-read-seam.md

Contract: Gate-command Planning-read + Write-surface Seam + Ratchet

Internal API/architectural contract (no HTTP surface).

Seam contract — READ side

For every planning-lifecycle GATE/verify command that reads a planning artifact:

read_planning_artifact(M, artifact) :=
    kind = _ARTIFACT_TYPE_TO_KIND[artifact]        # mission.py:1106 lookup
    dir  = resolve_planning_read_dir(repo_root, M.slug, kind=kind)
    return dir / artifact

Seam contract — WRITE side (the write twin, FR-009(e) / FR-004)

For every planning-lifecycle GATE/verify command that resolves a planning-artifact COMMIT / branch, the mission's target_branch is read from meta.json on the PRIMARY surface — never the topology-aware candidate (which under coord topology resolves to the coordination worktree, whose mission dir has no meta.json, silently falling back to the protected repo primary main):

resolve_commit_branch(M) :=
    meta = primary_feature_dir_for_mission(main_root, M.slug) / "meta.json"
    return meta["target_branch"]                   # NOT candidate_feature_dir_for_mission

This is the already-proven shape of resolve_merge_target_branch (core/paths.py:630-675). The write-branch resolvers that MUST adopt it: get_feature_target_branch (core/paths.py), resolve_target_branch (core/git_ops.py), and the finalize-tasks commit-branch resolution (mission.py). (WP00.)

Guarantees

the read resolves to the primary target_branch dir for ALL topologies. (FR-001/002/004)

analysis-report), the read resolves to its placed surface (coord under coord topology) — UNCHANGED. (C-002 status leniency)

target_branch — identical to pre-mission behavior. (NFR-001)

or a bespoke primary-anchor helper — all consume the one seam. (FR-009)

the record-analysis dirty-tree preflight and is DISJOINT from the coord-residue partition; a stale primary spec.md remains non-allowlisted ("real dirt"). (FR-003)

mission's target_branch for ALL topologies — read from meta.json on the PRIMARY surface, never the topology candidate (which falls back to the protected repo primary main). The status/coord commit destinations are UNCHANGED (status events still emit to coord). (FR-004 anti-"resolution to the repo primary" + FR-009(e) finalize-tasks commit; WP00.)

  • G-1: For a PRIMARY-kind artifact (spec/plan/tasks/WP/research/data-model/lanes/meta),
  • G-2: For a STATUS/bookkeeping kind (status.events.jsonl, acceptance-matrix,
  • G-3: For a flattened/single-branch mission, every read resolves to
  • G-4: No gate command reconstructs a planning-read path via topology routing
  • G-5: The self-bookkeeping allowlist (meta.json, provenance) is consulted by
  • G-6 (write twin): For a planning-artifact COMMIT, the resolved commit/branch is the

Ratchet contract (FR-010 — makes G-4 and G-6 enforceable)

An architectural test (tests/architectural/) MUST fail on EITHER arm.

Read arm — if any gate-command entry function:

_find_feature_directory / resolve_feature_dir_for_mission (topology-routed).

  • directly joins <feature_dir>/{spec,plan,tasks,research,data-model}.md, OR
  • resolves a planning-artifact read through resolve_handle_to_read_path /

Write arm (G-6) — if any write-branch resolver (get_feature_target_branch in core/paths.py, resolve_target_branch in core/git_ops.py, the finalize-tasks commit-branch resolution in mission.py):

candidate_feature_dir_for_mission (→ coord → fallback protected repo primary main) instead of primary_feature_dir_for_mission / the kind-aware write seam.

  • resolves a planning-artifact COMMIT/branch by anchoring its meta.json lookup to

Allowed (not flagged): the read seam itself, the write seam (primary_feature_dir_for_mission / resolve_merge_target_branch), STATUS reads off status_feature_dir, STATUS/coord commit destinations, and the self-bookkeeping allowlist. The write arm flags ONLY a write-BRANCH resolution anchored to the candidate dir — not every legitimate topology-aware status read.

The ratchet's non-vacuity is proven by a MANDATORY runnable synthetic-AST self-test (both arms: a violating snippet is FLAGGED, a clean snippet PASSES), with the enumerated surface/resolver set pinned — not a recorded manual mutation log.

Anti-mutant / behavioral assertions (IC-11)

gate command's PLANNING read ref == target_branch AND its STATUS read ref == coord. (Kills "always coord" and "always primary" mutants.)

assertion RED. (Non-vacuous.)

resolve_target_branch / the finalize-tasks COMMIT MUST resolve target_branch (NOT protected main). Reverting the resolver to candidate_feature_dir_for_mission MUST turn the assertion RED (it resolves main). Red proven against the unfixed resolver.

  • A coord-topology fixture (composed <slug>-<mid8> primary dir) MUST show: each
  • Reverting any IC-02/03/04 site to the topology resolver MUST turn its planning-read
  • Write twin (WP00): on a coord-topology fixture, get_feature_target_branch /
  • For #2091/#2088 (Lane B): reverting the product guard MUST turn the new guard RED.

Caller obligations

Every gate command declares the artifact's kind via _ARTIFACT_TYPE_TO_KIND and reads through the seam. No new CLI surface; kind is internal (NFR-003).

On the WRITE side, every command that resolves a planning-artifact commit/branch reads target_branch from meta.json on the PRIMARY surface (primary_feature_dir_for_mission), mirroring resolve_merge_target_branch — never the topology candidate. (G-6, WP00.)