Contracts
gate-hardening-contracts.md
Gate Contracts — Coord-Authority Gate Hardening (01KW4T2F)
Phase 1 contracts. Four gate contracts that the WPs must satisfy. Each is content-anchored (CT7/NFR-001), non-vacuous, and self-mutation-tested (NFR-002). These are the binding acceptance shapes for /spec-kitty.tasks to decompose against.
Contract A — Call-shape arm flag-rule (FR-001 + FR-008 + exclusions)
Subject: callshape_violations(func, *, read_funcs) in tests/architectural/test_gate_read_literal_ban.py.
FLAG a kind-read (read_funcs ∈ {IDENTITY, LANES}) iff its first arg is one of:
1. (existing) two-hop local Name bound (same function) from _COORD_AWARE_CALLSHAPE_RESOLVERS and NOT also bound from _PRIMARY_FOLD_CALLSHAPE_FUNCS. 2. (existing) inline Call to a coord-aware resolver. 3. (FR-001 NEW) function PARAMETER whose caller (one hop up) binds that arg from a coord-aware resolver WITHOUT a primary fold. *Requires (a) WIDENING _COORD_AWARE_CALLSHAPE_RESOLVERS to align with the read-arm's _TOPOLOGY_ROUTED_READ_RESOLVERS (5 names — add _find_feature_directory / _resolve_setup_plan_feature_dir), without which the _run_documentation_wiring ← setup_plan binding is not recognized as coord-aware and the residual is unreachable; and (b) a MODULE-SCOPED CALLER INDEX (the per-function callshape_violations(func, , read_funcs) signature gains caller/module context). Re-run the NFR-004 census over the widened surface. 4. (FR-008 NEW) Attribute** that is coord-bearing and NOT a sanctioned primary attribute (.target_feature_dir / primary-fold-bound field).
NEVER FLAG:
- A first arg bound from / built by a primary-fold seam (sanctioned routed shape).
- A plain parameter whose caller-binding is primary/seam-bound or non-coord-aware (FR-001 boundary — re-pin
test_callshape_arm_identity_passes_parameter_dirto stay consistent; do NOT leave it contradictory). - The sanctioned exclusions: leaf primitive
require_lanes_json, payload helper_mission_identity_payload, and theread_eventsSTATUS-leg reads inside_STATUS_BEARING_MODULES(C-007). Read-func-scoped, NOT blanket-module: the exclusion is_STATUS_READ_FUNCS = {"read_events"}only — identity/lanes reads inexecutor.py/recovery.py(incl. an injectedresolve_mission_identity(run.feature_dir), SC-006) stay IN-SCOPE and flaggable.
Self-mutation (NFR-002 / SC-001 / SC-006) — each shape ships its own:
_VIOLATION_CROSS_FUNCTION(param bound from coord-aware caller) → RED; clean counterpart (param from primary-fold caller) → GREEN._VIOLATION_ATTRIBUTE(resolve_mission_identity(run.feature_dir)in executor-shape) → RED;.target_feature_dircounterpart → GREEN.
Scope-unify (FR-002): the IDENTITY scan family also covers merge/ + lanes/ + core/worktree_topology.py. SC-006: an injected identity read off a coord-aware dir in merge/executor.py — BOTH the parameter shape AND the run.feature_dir attribute shape — is caught post-mission.
Anti-vacuity: the live scan SEES the in-scope read call sites (the _count_read_call_sites floors; raise the floor census after FR-002 widens scope).
Contract B — Named shrink-only census (FR-003)
Subject: _CALLSHAPE_KNOWN_RESIDUALS (and its sibling for the widened identity scope) in test_coord_read_residuals_closeout.py.
- Every in-scope param/attribute-fed kind-read is routed OR present in the census with a tracker reference.
- Shrink-only: a NEW un-pinned flag → RED; a stale pin no longer flagged → RED (remove it).
- The sanctioned exclusions (Contract A) are NOT census entries — they are never-flagged true exclusions.
- C-005: coordinate (do not rot) the shrink-only
_DIR_READ_KNOWN_RESIDUALS#2167 pin. - NFR-004: full
tests/architectural/green on the current tree; the census enumerates every in-scope param/attribute read site with a route-or-pin disposition (zero false positives).
Contract C — Rationale-map ↔ frozenset cross-check (FR-006)
Subject: a machine-read PARTITION_RATIONALE: dict[MissionArtifactKind, tuple[partition, rationale, load_bearing_consumer]] in test_write_surface_placement_guard.py, cross-checked against _PRIMARY_ARTIFACT_KINDS / _PLACEMENT_ARTIFACT_KINDS (src/mission_runtime/artifacts.py).
- (a) Exhaustive: every
MissionArtifactKindmember has an entry (missing → RED). - (b) Split equality: the map's derived PRIMARY/STATUS split
==the live frozensets (re-home a kind without editing its rationale → RED → SC-003). - (c) All-kinds anti-mutant: parametrized across ALL load-bearing kinds — forcing each kind into the opposite partition makes its
resolve_placement_only(...).refassertion go RED (not just SPEC). - Net-new only / NFR-005: exhaustive + disjoint + the SPEC anti-mutant already exist (
test_full_partition_resolves_per_membership); add NO line-pins; keep allowlist-free (CT7 exemplar, C-004).
Contract D — #2197 routing + husk no-op (FR-004 / FR-005 / FR-007)
D.1 Routing (FR-004, caller-only)
Subject: runtime_bridge.py::_build_finalized_override_query_decision (the preview_claimable_wp(feature_dir) call).
- Pass a PRIMARY
planning_dir(kind-aware seam /primary_feature_dir_for_mission) AND the coordstatus_dir=(coord-aware leg), mirroringworkflow.py::_preview_claimable_wp_for_mission. discovery.preview_claimable_wpalready carries thestatus_dir=leg-split — this is a CALLER-ONLY change (C-003: the single sanctioned production routing edit).- C-001/C-002: the status leg stays coord-aware; consume
resolve_planning_read_dir, do not author_read_path_resolverinternals.
SC-002 (behavioral revert-fails): an EXECUTED test against the divergent coord fixture asserts the returned DOMAIN value preview.wp_id (NOT a resolved-path equality). Reverting the FR-004 routing → coord husk → wrong/empty wp_id → RED.
D.2 Scan-scope un-mask (FR-005)
- Extend the live identity/lanes scan families to include
src/runtime/next/, with aruntime/nextread-site floor (mirroring_count_read_call_sites) proving non-vacuity. Non-vacuous on the current tree:runtime_bridge.pyalready carries ≥3 in-family identity reads (get_mission_type(feature_dir)at ~:2547/:3237/:3392); each gets an explicit route-or-pin disposition under the Contract B census so the scope extension lands NFR-004-green. - DECOUPLING (binding): the FR-004 read is a
tasks/-dir, parameter-fed shape the call-shape arm cannot see — it is gated BEHAVIORALLY by SC-002, NOT by this scan. The scan does NOT "gate FR-004"; it closes the runtime/next blind spot for the identity/lanes families. - NFR-003 (gate-unmask-cannot-self-validate): pair with a pre-merge verbatim full-
tests/architectural/dry-run recorded in the PR body.
D.3 Husk no-op (FR-007 / SC-004)
Subject: check_pre30_layout (src/specify_cli/upgrade/pre30_guard.py).
- Clean no-op against (a) a production-shaped STATUS-only husk (real
status.events.jsonl+meta.json, notasks/— via the fixture, NOT an empty dir which short-circuits identically). - Clean no-op against (b) a
tasks/-present-but-non-legacy variant (exercises theLEGACY_LANE_DIRS/.mdbranch inis_legacy_format→ still no-op). - Fixture ownership (single consumer — no contention): SC-002 (IC-B) uses the EXISTING
coord_topology_missionfixture; thetasks/-present-non-legacy husk variant is consumed ONLY by FR-007, so IC-C owns and adds it.
Cross-cutting (all contracts)
- NFR-001 (CT7): zero new
file.py:NNNratchet anchors; content-anchor viacomposite_key. - NFR-002: every new/extended gate ships a self-mutation test (synthetic offender → RED, revert → GREEN) + an anti-vacuity floor.
- NFR-005: net test-suite-friction non-positive — no new allowlist/manual-denylist maintenance surface beyond the reused auto-discovery pattern.