diff --git a/CHANGELOG.md b/CHANGELOG.md index bcac63c..a68994a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,9 @@ repository still gets a decision, never by following the link; no release carrie - :memo: docs(walkthrough): fix Step 3 lede self-contradiction on .tf format count - :memo: docs(changelog): regenerate CHANGELOG.md for the EX-S09 lane follow-ups - :memo: docs(changelog): regenerate CHANGELOG.md for the EX-S09 F1 fix +- :memo: docs(changelog): regenerate CHANGELOG.md for the EX-S10 lane +- :memo: docs(changelog): regenerate CHANGELOG.md for the EX-S10 F2/F5 fixes +- :memo: docs(changelog): regenerate CHANGELOG.md for the EX-S10 F6 fix ### Features - :sparkles: feat(docs): gate example pack and format claims against dogfood @@ -147,6 +150,8 @@ repository still gets a decision, never by following the link; no release carrie - :bug: fix(docs-gates): fail on unmapped format tokens and de-confound the tests-dir mutation (EX-S01 review) - :bug: fix(examples): make privilege-tier's proving fixture load-bearing - :bug: fix(docs): pin case+decision pairs, not just case names, in the walkthrough (F1) +- :bug: fix(examples): scope format extraction to match: lines, not the whole classes: block (F2) +- :bug: fix(examples): close the same-line trailing-comment gap in format extraction (F6) ### Refactoring - :recycle: refactor(examples): discover dogfood packs instead of a hardcoded 3-name loop @@ -158,6 +163,7 @@ repository still gets a decision, never by following the link; no release carrie - :white_check_mark: test(examples): close REQ-EX-S05-05's other disjunct (fixture deletion) - :white_check_mark: test(examples): close REF-EX C5 quota-ceiling-from-fact explicitly - :white_check_mark: test(examples): pin REF-EX C8 companion-file delete as REVIEW (known limitation) +- :white_check_mark: test(examples): add the P5-EX epic exit gate (EX-S10) ## [0.2.0] - 2026-08-09 ### Chores diff --git a/hack/examples/ex_exitgate_test.sh b/hack/examples/ex_exitgate_test.sh new file mode 100755 index 0000000..910cd2f --- /dev/null +++ b/hack/examples/ex_exitgate_test.sh @@ -0,0 +1,889 @@ +#!/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() { #