Contracts
gate-contracts.md
Gate Contracts — tasks-py-degod-wave2-01KWH9EQ
Both gates live in NEW tests/architectural/test_tasks_command_surface.py and MUST be non-vacuous per DIRECTIVE_043 / spec C-006: concrete floor + self-mutation proof + shrink-only exception semantics. Both must be CI-visible (carry fast + architectural-selected markers; never absorbed into _gate_coverage_baseline.json).
Gate 1 — AST 0-inline-dumps (FR-007, SC-002)
future siblings (closes move-next-door evasion). src/specify_cli/agent_tasks_ports.py is the ONE sanctioned json.dumps home (the Render adapter) and is deliberately outside the glob; a comment in the gate names this.
1. ast.Call on Attribute(value=Name(id=<json-alias>), attr="dumps") — covering import json AND import json as <alias> (track import aliases per file); 2. from json import dumps (+ as <alias>) and calls to that name; 3. name-rebinding: any assignment whose RHS resolves to json.dumps/imported dumps, and calls to the bound name.
repo-relative paths, shrink-only (a growth assertion mirrors test_integration_boundary.py).
containing the offender → detector returns non-empty), per the test_commit_target_kind_guard.py pattern.
ports.render.json_envelope — see kitty-specs/tasks-py-degod-wave2-01KWH9EQ/contracts/").
- Scope: directory glob
src/specify_cli/cli/commands/agent/*.py— ALL current and - Detection (AST, immune to docstrings/strings): flag
- Allowlist: empty frozenset at ship time (0 sites). Exceptions, if ever, are
- Non-vacuity: one theater test per detection form (synthetic source string
- Failure message: names file:line + the remediation ("route through
Gate 2 — Whole-file LOC ceiling (FR-011, NFR-004, SC-001)
composite_key N/A — no line-keyed entries; rationale in research.md D5).
lowers it to the achieved size in the same commit as the move; final value min(achieved, 1400).
standing assert _CEILING <= 1400 mission-cap assertion, so a ceiling above 1400 is a RED test, not a judgment call. If the honest final size exceeds 1400: the WP moves to blocked, the delta-from-4569 analysis goes to the Activity Log + a #2305 comment, and the operator decides — never a self-certified higher ceiling.
makes the check function fail (test the extracted check function directly, not the live file).
the registration shim".
- Form:
len(tasks.py read_text().splitlines()) <= _CEILING(plain scalar; CT1 - Ratchet-down protocol:
_CEILINGstarts at 4569 (current), each relocation WP - Escalation rule — mechanically backstopped: the gate ships (at WP09) with a
- Non-vacuity: self-mutation test — a synthetic source with
_CEILING + 1lines - Failure message: current LOC, ceiling, and "add behavior to sibling modules, not
Marker obligations (FR-009 tie-in)
The new gate file and the byte-freeze suite carry markers selected by existing CI gates (fast; the architectural directory is additionally selected by the git_repo or integration or architectural shard). Both files appear in the FR-009 marker-census artifact with their selecting gate named.
Gate 2 — RETIRED (operator ruling, 2026-07-03)
The whole-file LOC ceiling served as the mission's ratchet-down instrument (4569 → 1205, lowered same-commit per relocation WP, verified at accept) and is retired with the mission on PR #2308: raw size metrics belong to Sonar's quality gate, not pytest (tests-as-friction). The anti-regrowth intent lives on as the registration-shim header guidance in tasks.py. Gate 1 (AST dumps — semantic) remains suite-owned and standing.
parity-contract.md
Parity Contract — tasks-py-degod-wave2-01KWH9EQ
The executable contract for this pure-parity mission (spec C-003). Four layers; ALL must hold at every commit of every WP.
Layer 1 — Contract harnesses (pre-existing, unmodified)
(10 byte-exact --help fixtures; JSON legs shape-checked via _shape()).
incl. T004 (move_task coord skip-exit-0 + wrong-leg detector) and T005 (mark_status/map_requirements refuse-exit-1) — the #2300 divergence pins (C-001).
are never adjusted to absorb a diff; a parity delta reverts the move.
tests/specify_cli/cli/commands/agent/test_tasks_cli_contract.py— 27 casestests/specify_cli/cli/commands/agent/test_tasks_cli_contract_coord.py— 16 cases- The ONLY sanctioned edit: FR-012 ratchet re-points (below). Expected-output fixtures
Layer 2 — Byte-freeze suite (NEW, committed before any routing change)
fixtures/tasks_cli/json/byte_contracts.json.
asserting result.stdout == expected_stdout (byte equality; never len()==N).
per D3), and the status --json indent=2 leg.
placeholders.
its own commit BEFORE the first Stream A/Stream B production change.
tests/specify_cli/cli/commands/agent/test_tasks_json_bytes.py+- One case per emission site (13 total; site→subcommand map in research.md D3), each
- Cases must cover: the compact success legs, the compact error legs (trigger conditions
- Fixtures use production-shaped mission data (real-format slugs/ULIDs), not
- Commit order (C-003 parity-ATDD): the suite lands GREEN against the pre-change tree in
Layer 3 — Ratchet re-point rule (FR-012) — post-tasks squad hardened
resolve move_task/status/map_requirements by name from tasks.py's AST, the coverage session is single-file (include=[tasks.py]), and _branch_coverage_by_function returns 100.0 when total == 0 — a naive re-point is therefore VACUOUSLY GREEN (measures nothing, passes every floor).
map feeding both the per-function AST resolution and a multi-file include= set, with the vacuous else 100.0 arm replaced by a hard failure (total == 0 on a floored function = plumbing bug). WP06/WP07 each ADD their relocated module to the map/include.
(locally lowered floor or dropped scenario, output pasted, restored) — never a recorded percentage. Exactly 100.0 on a function with known branches = review-reject.
_mutating_function_line_ranges, and the include/analyze wiring. Any edit to _run_all_scenarios, floor VALUES, or assertion bodies is out-of-scope and rejected.
wrapper, or leaving the vacuous fallback alive. Re-pointing is expressly NOT fixture adjustment.
harness's internal names — never to be confused with this mission's WP01 subtasks.
_BRANCH_COVERAGE_FLOORS/_mutating_function_line_ranges()in the coord harness- WP05 performs the one-time plumbing rewrite: a
{floored_name: (module, qualname)} - Acceptance evidence per re-point: a demonstrated RED fire of the re-pointed entry
- Diff-scope rule:
git diffon the coord harness may touch ONLY the floors mapping, - Forbidden: deleting the ratchet, lowering a floor, leaving it measuring the thinned
- Label note: harness case labels T004/T005 (skip-arm / refuse-arm) are the coord
Layer 4 — Seam interception checks (NFR-002)
kitty-specs/tasks-py-degod-wave2-01KWH9EQ/seam-checklist.md, columns symbol | tasks.py binding (line) | routed-via-_tasks? | interception/identity test id | monkeypatch sites swept. WP02 creates it; every family WP appends its rows. Free-form Activity-Log prose is not the checklist.
(cheap, non-fakeable — no spot-checking); interception (leg c) by sentinel-patch tests through the _tasks.<attr> route for the defensively-patched symbols.
- One committed checklist for the whole mission:
- Binding (leg a) is proven by parametrized
is-identity tests over the FULL move-set - "The ~370 tests pass" alone is NOT acceptance evidence for this layer.
Acceptance evidence per WP
1. 43/43 harness + 13/13 byte-freeze green (unmodified fixtures). 2. Targeted family tests + coord harness (for commit-router WPs) green. 3. Seam checklist ticked with interception evidence. 4. mypy --strict on changed src+tests together + ruff — zero findings. 5. LOC ceiling ratcheted down to the new tasks.py size.