From 4a3aa0b5b17025500480907ebb5f880eb7c98936 Mon Sep 17 00:00:00 2001 From: Konrad Heimel Date: Sun, 16 Aug 2026 14:40:12 +0200 Subject: [PATCH 1/6] :white_check_mark: test(examples): add the P5-EX epic exit gate (EX-S10) hack/examples/ex_exitgate_test.sh proves the epic's invariants in one invocation: yaml/json/tfvars/tf all present in class match paths; C1-C8 fixtures present (C8 is an inline cases.yaml entry per the REQ-EX-S07-04 amendment, not a directory) with C8 measured REVIEW; schema freeze is ref-relative against the v0.1.0 tag, not a working-tree diff (D-132); internal/core untouched vs origin/main (three-dot); task check still wires dogfood-examples/docs-gates/ dogfood-wiring-test; the README-format-vs-dogfood inventory (S01) is re-invoked and named-PASS verified; the provider fence and D-002 sanitization stay green. Every check is mutation-tested in place (deleted C-series dirs, flipped decisions, HEAD/branch/empty base-ref substitutions, unauthorized schema edits in a scratch sandbox) so the gate whose job is catching vacuous gates is not itself one. Marks backlog REF-EX CLOSED (P5-EX EX-S06/EX-S07, exit-gated by EX-S10) and updates the stale Phase-5 roadmap row. --- hack/examples/ex_exitgate_test.sh | 798 ++++++++++++++++++++++++++++++ openspec/specs/backlog.md | 4 +- 2 files changed, 800 insertions(+), 2 deletions(-) create mode 100755 hack/examples/ex_exitgate_test.sh diff --git a/hack/examples/ex_exitgate_test.sh b/hack/examples/ex_exitgate_test.sh new file mode 100755 index 0000000..cf85362 --- /dev/null +++ b/hack/examples/ex_exitgate_test.sh @@ -0,0 +1,798 @@ +#!/usr/bin/env bash +# REQ-EX-S10-01..08 — the P5-EX epic exit gate. +# +# ONE invocation proves the epic's invariants so EX cannot be marked done with +# a missing format, a missing C-series case, stale docs, or schema drift: +# +# (1) yaml/json/tfvars/tf are all present in the three shipped packs' class +# match paths (REQ-EX-S10-01), independently derived here (not just +# re-trusting S01's own extraction). +# (2) REF-EX C1-C8 fixtures exist (REQ-EX-S10-02); C8's decision is REVIEW, +# not APPROVE (REQ-EX-S10-03). +# (3) the schema freeze is REF-RELATIVE against the released tag v0.1.0, not +# a working-tree diff (REQ-EX-S10-04 / AUD-S18 / D-132). +# (4) internal/core is untouched on this lane vs origin/main (REQ-EX-S10-07). +# (5) task check still wires dogfood-examples, docs-gates and its own +# dogfood-wiring-test guard (REQ-EX-S10-05). +# (6) a format claimed in examples/README.md that dogfood does not walk +# fails, by re-invoking S01's own gate (REQ-EX-S10-06). +# (7) the provider fence still holds (REQ-EX-S07-05, re-verified here). +# (8) D-002 sanitization is green (REQ-EX-S10-08). +# +# C8 AMENDMENT (read before editing the C-series checks below): REQ-EX-S07-04 +# recorded that C8 (companion-file delete) canNOT be expressed as a +# directory-form case with its own expect.yaml — readSingleFilePair errors +# when head/ is absent, and the directory form has no way to say "this +# file is gone". C8 shipped as an INLINE `cases.yaml` entry (case name +# `companion-delete`) in examples/packs/infra-vars/.assent/tests/vars/, with +# its `expect.decision` nested under `cases: - name: ... expect: decision:`. +# REQ-EX-S10-03's "C8 expect.yaml" wording and the DoD's "C1-C8 fixture +# directories" both predate that amendment. This gate checks the fixture that +# actually exists (the amendment is the authority — S07 already shipped and +# reviewed it) rather than inventing a fake C8 directory to match stale prose. +# +# ANTI-VACUITY DISCIPLINE (hack/audit/exitgate_test.sh's standard, carried +# forward — this epic's reviewers found a real bug in 3 of its 4 prior +# lanes, and THIS is the gate whose entire job is catching vacuous gates): +# +# * Every check is a FUNCTION over explicit input paths/args, never a +# straight-line assertion over a global — so every one of them can be +# mutation-tested by calling it again with a violating input. +# * Every check run against the real tree (must be GREEN) also gets at +# least one mutation run against a violating input (must be RED, and RED +# FOR ITS OWN STATED REASON — expect_red pins a message fragment, so a +# check going red for the wrong reason is caught too). +# * Every ABSENCE/UNCHANGED assertion (schema freeze, internal/core) gets a +# POSITIVE control that the comparison base really lists the expected +# path, so a pathspec typo that silently matches nothing cannot report +# "unchanged" vacuously. +# * The schema-freeze and internal/core checks both take their BASE REF as +# a parameter and validate its SHAPE before diffing — `HEAD` (or the +# empty string) compared against itself is empty by construction, which +# is exactly the disarm this gate exists to refuse (D-132). Mutation +# tests below call the checks with `HEAD` and confirm they redden for +# that specific, stated reason — not just "some check somewhere failed". +# * No `grep -q` on the read end of a pipe under `set -o pipefail` (SIGPIPE +# exits 141); matches go to a file. Portable ERE only (no `\t`/`\s`/`\b`, +# no `grep -P`) — this repo's CI runs Linux/GNU grep, dev runs BSD/macOS. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" + +WORK="$(mktemp -d)" +cleanup() { rm -rf "$WORK"; } +trap cleanup EXIT + +fail() { + echo "FAIL: $*" >&2 + exit 1 +} + +expect_green() { #