Skip to content

PTV-SCF-0001 P2 · C3 — gate G4 implemented

Date: 2026-08-13 Status: open Supersedes: none Superseded-by: none — current Phase: PTV-SCF-0001 P2 · Golden prompts as skills Chunk: C3 · carried item G4-impl · acceptance gate G-P2-5 Terminal state of this record: a gate that was asserted by CI and evaluated by nobody now runs. It closes no phase.

G4-impl, deferred by P1’s round via the F-02 ruling (docs/decisions/2026-08-12-requirement-tree-root-set.md:137-144):

gate G4 (“every new DR traces to at least one SR; every SR traces to root”) is asserted by CI in the SDD’s own mutation protocol and has no implementation. host/tests/estate.test.ts checks the shape of traces fields and cannot check reachability, because estate.yaml does not carry the SR graph.

P1 found six orphans by hand-reading a mermaid block — precisely the manual check G4 exists to replace. The gate was in the table, in the sequence diagram, and in nothing that runs.

PathWhat
host/src/requirements/trace.tsparser + reachability evaluation
host/src/requirements/cli.tsnpm --prefix host run requirements:check, exit 1 on E_ORPHAN_REQUIREMENT
host/tests/requirements.test.ts15 tests, including both halves of G-P2-5 run as processes
host/tests/fixtures/seeded-orphan.mdthe negative case, so the gate’s failure is demonstrated rather than asserted
checks.yamlPTV-CHK-0036, mode: code, status: built
.github/workflows/integration-spine-ci.ymla governance-gates job running G4, G3 and the host suite

R-7 · The graph is parsed from the SDD, not copied into a data file. The requirement tree has no machine-readable home: estate.yaml carries each element’s traces field but not the SR relation set, which is exactly why the existing test could only check the shape of a trace. The alternative — mirror the tree into YAML and check the mirror — creates a second copy that drifts from the authored one, and it would drift silently in exactly the case the gate matters. So the checker reads the three requirementDiagram fences in docs/PETROVA-SDD-BASELINE.md. Parsing the source of truth is unpleasant; checking a copy of it is worse.

R-8 · The root set is enumerated, never inferred. ROOT_SET is the six requirements the F-02 ruling ratified, written out with a citation. Inferring roots as “whatever has no parent” would be less code and would make the gate incapable of failing: every future orphan would be a root by construction. A test pins this — it asserts the parentless set equals the ratified set today, so the day someone adds an unrooted requirement, the inference shortcut is visibly wrong rather than quietly accommodating.

R-9 · PTV-SR-0015 is reported, not raised. §3.3 appends it as prose and states its trace to SR-0001 in a sentence. A sentence is not an edge, so G4 cannot evaluate it. Failing the baseline over it would make the gate report on the document’s authoring style rather than on traceability; passing it silently would count an unevaluated requirement as traced. It is emitted as a notice: line, exit code unchanged, and raised below as friction.

R-10 · The CI job was added, because a gate CI does not run is the defect being fixed. G-P2-5 asks only for a checker and a registry entry. But the SDD places G3 and G4 in CI (§7.2, PTV-DWG-0007), and no workflow ran either — integration-spine-ci.yml runs only the cli suite, and the host suite ran nowhere at all. Shipping G4 into that would have reproduced the exact shape of the defect: an assertion that exists in a table. The new governance-gates job runs G4, both halves of G3, and the host suite, and the workflow’s paths: filter now includes host/**, checks.yaml, estate.yaml and the SDD.

Edge direction, the thing that would have been silently wrong

Section titled “Edge direction, the thing that would have been silently wrong”

Edges are authored parent-first — PTV_SR_0001 - contains -> PTV_SR_0005 — except refines, which reads child-first: PTV_SR_0014 - refines -> PTV_SR_0003, and the F-02 ruling confirms 0014 traces to root via 0003. traces is likewise child-first, DR→SR.

Reversed, the checker still runs, still prints a tidy summary, and reports a different set of orphans. There is no crash to notice. Both directions are therefore pinned by their own tests rather than left to the parser’s reader.

satisfies and verifies are ignored: they run element→requirement and are G2’s business. An unsatisfied requirement is a coverage question; an unrooted one is a provenance question. Only the second is what G4 asserts.

The G4 checker exits non-zero on a seeded orphan requirement and zero on the current baseline, and is registered in checks.yaml. — two runs plus a registry lookup.

RunResult
npm --prefix host run requirements:check (baseline)exit 0 — “G4 ✓ 14 SRs trace to a root, 12 DRs trace to an SR. 1 prose-only requirement(s) unevaluated.”
same, against host/tests/fixtures/seeded-orphan.mdexit 1 — E_ORPHAN_REQUIREMENT: 3 requirement(s) do not trace to a root, naming PTV-SR-0101, PTV-SR-0102, PTV-DR-3900
registry lookupPTV-CHK-0036 in checks.yaml, status: built, refuses_when: E_ORPHAN_REQUIREMENT

The fixture seeds three defects and two controls: an unrooted requirement, a second orphaned only by inheritance from the first (so the walk is shown not to stop at one hop), a DR with no traces edge, and one SR and one DR that must still pass. A fixture where everything fails cannot show the checker discriminating.

Both runs are also executed as processes in host/tests/requirements.test.ts, so the exit codes are evidence in CI rather than a claim in this record.

Full local verification: host 255 tests pass (240 before, 15 new), tsc --noEmit clean, estate:generate --check and domains:generate --check both current.

  • F-15 · PTV-SR-0015 is outside every gate. Appended as prose in §3.3 with its trace stated in a sentence. G4 reports it and cannot evaluate it. The fix is one edge in the §3 diagram, but adding it is a change to the requirement tree, which the F-02 ruling ring-fenced (“no relation is added or removed”). It needs a ruling, not a commit.
  • F-16 · The gate family is now half-run. G3 and G4 run in CI as of this chunk. G1 (identity) and G2 (edge class) run inside the MCP host at propose time and have test coverage, but nothing asserts them against a mutation arriving by hand-edited PR — which is how every change to estate.yaml in this repo actually arrives, since the self-entry carries fleets_allowed: [].
  • F-18 · npm ci in host cannot succeed from a clean clone alone. host depends on cli via file:../cli, and cli’s prepare runs tsc; in a fresh checkout that build has no @types/node, so the install fails inside the dependency’s own prepare step. It passes locally only because cli/node_modules is already there. The CI job installs cli first, which works but is a workaround: the ordering is now knowledge held in one workflow file rather than in the package graph. This is the first time anything installed host from clean, which is why a repo-wide npm ci has never been exercised.
  • F-17 · The host suite had never run in CI. Discovered while wiring R-10, not looked for. 240 tests, including checks-registry.test.ts and the estate gates, were green only when someone ran them locally. Fixed here for the host package; whether other packages have the same gap is unchecked.
  • docs/PETROVA-SDD-BASELINE.md §7.2 (PTV-DWG-0007), §7.3 — G4’s assertion and its placement in CI.
  • docs/decisions/2026-08-12-requirement-tree-root-set.md — the six-root ruling, the ring-fence on the tree’s edges, and the follow-up this discharges.
  • docs/decisions/2026-08-13-ptv-scf-0001-p2-open.md:99,167 — C3’s scope and gate G-P2-5.
  • host/src/estate/cli.ts — the G3 --check shape this mirrors.
  • Subagent: PTV-SCF-0001 P2 C3 (session 2026-08-13)
  • Human: ☑ (proxy) countersign — accepts the G4 implementation, the four rulings above, and the three friction items raised for C5’s round.
    • Countersigned by human:devarno on 2026-08-13, by explicit directive in session (“#269, C2 + C3 approved — countersign in my stead”). Ticked by the agent as scribe, not as signatory: the human act is the directive, and this line is its record. No other part of this document is edited (MR-7).