# ============================================================================= # L0 STATIC — the specification is immutable to agents (R-TEST-06) # Catalog: docs/14 L0 row ("Pre-apply, in CI") — runs WITHOUT a cluster. # # A governance rule is made testable by testing its ENFORCEMENT MECHANISM, not by # observing that no agent misbehaved today. # # GIVEN the enforcement mechanism (ci/spec-immutability.sh + ci/SPEC_PIN), # WHEN it is driven against hermetic THROWAWAY spec repositories in each state, # THEN a clean spec at the pinned SHA passes; an uncommitted edit under docs/ or # decisions/, an untracked spec addition, and an unadopted committed drift each # exit 1 and block the merge; an absent spec repo exits 3 (honest skip, never # green); an unpinned SPEC_PIN fails closed; and the merge gate runs the stage # and blocks on its failure. # # THE REAL SPEC REPO IS NEVER TOUCHED: every case runs against a git repo created under # mktemp with SPEC_REPO pointed at it and a copy of ci/ carrying a fixture SPEC_PIN. # # STATUS: EXECUTABLE. `check.sh` is a pure offline assertion script; conformance/run.sh # --layer l0 and ci/l0.sh run it directly, and this Chainsaw Test wraps the SAME script. # NOT proven (named in check.sh's header): that the Gitea server REQUIRES this status # check before merge — branch protection is operator configuration outside the repo. # ============================================================================= apiVersion: chainsaw.kyverno.io/v1alpha1 kind: Test metadata: name: spec-immutability-enforced labels: conformance.home.k3s/R-TEST-06: "true" annotations: conformance.home.k3s/requirements: "R-TEST-06" conformance.home.k3s/layer: "L0" # NO `scope` ANNOTATION, DELIBERATELY — this invariant IS path-scoped, and was audited as # such (2026-09-12). Its entire input surface is ci/ (spec-immutability.sh, SPEC_PIN, # gate.sh) plus the read-only spec repo, which no commit here can change. The gap was that # ci/touched-requirements.sh keyed .gitea/*|ci/* to R-GITOPS-01 + R-TEST-02 + R-TEST-03 but # NOT to R-TEST-06 — a leftover from when R-TEST-06 was enforced only by the gate STAGE and # carried by no test, so attributing it false-blocked the coverage gate. This test now # carries the R-TEST-06 label, so R-TEST-06 was added to that path-map entry in the same # change; a path-scoped invariant belongs in the path map, not in an unconditional run. conformance.home.k3s/safety-critical: "false" conformance.home.k3s/executable: "true" conformance.home.k3s/intent: >- A spec modification is detected and blocks the merge; an unverifiable spec skips honestly rather than passing; and the gate wires that stage as blocking. spec: steps: - name: drive-the-spec-immutability-gate description: >- Run the real ci/spec-immutability.sh against throwaway spec repos in every state and assert its exit code, then assert the gate wires it as blocking. try: - script: content: bash ./check.sh