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.
What was carried into this phase
Section titled “What was carried into this 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.tschecks the shape oftracesfields and cannot check reachability, becauseestate.yamldoes 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.
What lands
Section titled “What lands”| Path | What |
|---|---|
host/src/requirements/trace.ts | parser + reachability evaluation |
host/src/requirements/cli.ts | npm --prefix host run requirements:check, exit 1 on E_ORPHAN_REQUIREMENT |
host/tests/requirements.test.ts | 15 tests, including both halves of G-P2-5 run as processes |
host/tests/fixtures/seeded-orphan.md | the negative case, so the gate’s failure is demonstrated rather than asserted |
checks.yaml | PTV-CHK-0036, mode: code, status: built |
.github/workflows/integration-spine-ci.yml | a governance-gates job running G4, G3 and the host suite |
Four rulings
Section titled “Four rulings”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.
G-P2-5 evidence
Section titled “G-P2-5 evidence”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.
| Run | Result |
|---|---|
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.md | exit 1 — E_ORPHAN_REQUIREMENT: 3 requirement(s) do not trace to a root, naming PTV-SR-0101, PTV-SR-0102, PTV-DR-3900 |
| registry lookup | PTV-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.
Friction raised here, for C5’s round
Section titled “Friction raised here, for C5’s round”- F-15 ·
PTV-SR-0015is 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.yamlin this repo actually arrives, since the self-entry carriesfleets_allowed: []. - F-18 ·
npm ciinhostcannot succeed from a clean clone alone.hostdepends oncliviafile:../cli, andcli’spreparerunstsc; 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 becausecli/node_modulesis already there. The CI job installsclifirst, 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 installedhostfrom clean, which is why a repo-widenpm cihas 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.tsand 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.
References
Section titled “References”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--checkshape this mirrors.
Sign-off
Section titled “Sign-off”- 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:devarnoon 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).
- Countersigned by