mirror of
https://github.com/saymrwulf/ltl-accumulator-verified.git
synced 2026-09-05 20:10:43 +00:00
P2-b: close classes 9 and 15 — the instruments, and the places nothing looked
CLASS 15 — a Lean file where no phase was looking. The dead-file scan read Proofs/*.lean and gen/LTLAcc/*.lean and nothing else. A module at the verification root, or under any other gen/ subdirectory, was neither compiled nor rejected — while remaining importable by name, since LEAN_PATH contains both roots. That is a source of the corpus that no phase reads and no pin covers, which is exactly what the dead-file gate exists to forbid; it was simply looking in two places instead of everywhere. Now nothing may live in either root but the two enumerated sets. CLASS 9 — the instruments' own declaration surface. AxiomCheck.lean and Inventory.lean perform the audit and are therefore not corpus, so nothing inventoried what THEY declare. Inventory.lean now walks both: AxiomCheck by module index, and itself as the module still being elaborated, whose declarations are the ones the environment reports with no originating module. That is what makes the inventory cover the instrument that produces it rather than exempting itself. The policy is not "declare nothing" — this file legitimately declares its machinery. It is that an instrument may declare only inert definitions. An axiom here would widen the trusted base without appearing in any certificate's cone; a theorem here would be a claim no certificate covers and no allowlist pins. A flat ban on theorems was WRONG and was measured to be wrong: defining a function by well-founded recursion makes the elaborator emit its own obligations, and axiomCone._proof_1 rejected this very file. The distinction that holds is whether a theorem is a claim someone wrote or an artefact of a definition declared alongside it — an artefact's name extends the name of a constant declared with it. Observed surface: 18 declarations, 16 def and 2 generated obligations, no axiom, no standalone claim. The drivers are byte-pinned already, so this does not pin WHICH definitions they contain — that would add a thing to maintain without adding a thing to catch. It adds the property byte-pinning cannot give: that no instrument declares an axiom or a claim, whatever its bytes are. selftest_audit.sh: 10 cases -> 14. Case 12 uses an INDENTED axiom, because Phase 1's source grep catches an unindented one and the point is to reach the kernel-side walk standing behind it. TWO DEFECTS IN THE TEST HARNESS, found while adding the cases. · The scratch tree copied verification/ only, but the button also reads README.md and STATEMENT-MAP.md from the repository root. check.sh therefore ALWAYS died in Phase 3c in the scratch tree, which made every `if check.sh; then <attack not caught>` guard unfirable — check.sh could not pass in there even with no attack at all. Only the diagnostic greps were doing any work. The documents are now copied, and the negative test below proves the guard is live: with the driver-surface check disabled, check.sh PASSES a tree whose inventory driver declares `axiom driver_cheat : False`. · Case 9 was the last case when it was written and left its rogue gen file in place. Harmless then; the new cases inherited it. Cleaned up between the blocks rather than inside case 9, so that case still tests what it did. Also fixed while here: Phase 3b compared the compile manifest against Inventory.lean by grepping the WHOLE FILE for a backticked module name, so prose counted — a doc comment naming a module broke the count, and in the other direction a doc mention of a module missing from the array would have satisfied the presence check and hidden the omission. It now reads the arrays. Both new gates negative-tested by removal. Button green, self-test green.
This commit is contained in:
parent
8530cc73f4
commit
9c78ce88ae
5 changed files with 265 additions and 14 deletions
|
|
@ -66,8 +66,29 @@ cross-checked against the inventory's independently computed cones.
|
|||
asserted against the allowlist/CONES/harness by check.sh Phase 3c on
|
||||
every run — stale-count drift is now a red button, not an erratum:
|
||||
review R4-1, after three consecutive rounds of hand-edit failures.)
|
||||
`verification/selftest_audit.sh` attacks the gate with nine injection
|
||||
cases (attributed/indented/private/instance declarations, a nested
|
||||
namespace reusing an audited basename, a smuggled axiom, a deleted
|
||||
declaration, and unmanifested Proofs/ and gen/ modules) — each must
|
||||
fail the exact production gate.
|
||||
`verification/selftest_audit.sh` attacks the gate with fourteen
|
||||
injection cases (attributed/indented/private/instance declarations, a
|
||||
nested namespace reusing an audited basename, a smuggled axiom, a
|
||||
deleted declaration, and unmanifested Proofs/ and gen/ modules) — each
|
||||
must fail the exact production gate. Four were added on 2026-07-31 and
|
||||
close two classes the earlier suite did not reach:
|
||||
|
||||
* **A Lean file where no phase was looking.** The dead-file scan read
|
||||
`Proofs/*.lean` and `gen/LTLAcc/*.lean` and nothing else, so a module
|
||||
at the verification root or under any other `gen/` subdirectory was
|
||||
neither compiled nor rejected — while remaining importable by name,
|
||||
since `LEAN_PATH` contains both roots. Cases 10 and 11 forbid both.
|
||||
* **The instruments' own declaration surface.** `Proofs/AxiomCheck.lean`
|
||||
and `Proofs/Inventory.lean` perform the audit and are therefore not
|
||||
corpus, so nothing inventoried what THEY declare. `Inventory.lean` now
|
||||
walks both — including itself, as the module still being elaborated —
|
||||
and fails closed on an axiom, or on a theorem that is not an artefact
|
||||
of a definition declared alongside it. Cases 12 and 13 attack each
|
||||
driver; case 12 uses an INDENTED axiom, because Phase 1's source grep
|
||||
catches an unindented one and the point is to reach the kernel-side
|
||||
walk behind it.
|
||||
|
||||
Both new gates were negative-tested by removal. With the driver-surface
|
||||
check disabled, `check.sh` PASSES a tree whose inventory driver declares
|
||||
`axiom driver_cheat : False` — which is the whole reason the check
|
||||
exists.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
e7d422f0be9a9e6f5465058292e30c711d52856428da2b01c470a57ec181540c AUDIT-MANIFEST.txt
|
||||
7ab1bfbafc8ac9ab34d1e0eaa71ba2f1f9912121345cc1af281bcb1d2ec30ad2 check.sh
|
||||
9118c39009196247655c3517626bba5447a59cf16484b1bb296ef6bc143fab13 check.sh
|
||||
070147e2667053bd5d5e1174b969fc6c91bfcf15ded1a5bff57754e15f416885 fidelity/lean_defs.py
|
||||
5d82462a002ac9fc782e95afe78b7719ba64b6410b5d2bfa620fe5317367dbf2 fidelity/run_fidelity.py
|
||||
503babb3f4e6aff82ebd59e8752469ecd60fba440ed3b11b3f97c2b655fbd9bf gen/LTLAcc/HashExternal.lean
|
||||
|
|
@ -8,8 +8,8 @@ f66fb98d2a09503d9bd0d60dc964545eea6dc94b9bbb9246d1021195b79f2601 inventory_gate
|
|||
736ea4be712e1b5bcda10ecb466f0dec7008a2a36eabdfd77563976299c43cce lean-guard
|
||||
ce4c4e3d87434b9663f46de25ce34b48a0cf0d392e0a320a0787b4674a2d7b61 lean-toolchain
|
||||
eda93f520546a692926b2a46bcb79332e1795879e5083327a8bf2404aca5cf87 Proofs/AxiomCheck.lean
|
||||
25344c8441077277addd6b4eb769b2c0e3b173afc28d3a6e8aeba0580c22a87f Proofs/Inventory.lean
|
||||
de5cdf01724fd4333b0652769bfede3ee150fedfa0a6c4ffbc8e7ab065843928 Proofs/Inventory.lean
|
||||
0b8a0fc6947af1d1e600a756eb2b07dc88d189b21df3220c4501a41be8b33f1e run_bare.sh
|
||||
67a44e0db818efc48dede26f73f76f1221424bb23e40e29d859b12d0d300403b selftest_audit.sh
|
||||
dd975f4682035fe3313ce85b70f1aa1e411f9534ff62a29f5591630859c55a75 selftest_audit.sh
|
||||
3d5898161d663eccad162269a5a6c102319077e22e1f2d89a8bfcab6926d29f6 selftest-harness.sh
|
||||
cf6d4d8210e224a054d4ab693c28c83e7a9ddebda05da47d6ec311d825a606c0 selftest_statements.sh
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import Proofs.Binding3
|
|||
import Proofs.Refactor
|
||||
import Proofs.Theorem3
|
||||
import Proofs.PinStore
|
||||
import Proofs.AxiomCheck
|
||||
|
||||
open Lean
|
||||
|
||||
|
|
@ -52,6 +53,17 @@ def corpusModules : Array Name :=
|
|||
`Proofs.Consistency, `Proofs.Binding3, `Proofs.Refactor,
|
||||
`Proofs.Theorem3, `Proofs.PinStore]
|
||||
|
||||
/-- The audit INSTRUMENTS, as opposed to the corpus. They are Lean modules in
|
||||
the audited tree, so what they declare is part of this repository's
|
||||
surface — but they are not proofs, and nothing may rest on them.
|
||||
|
||||
`Proofs.AxiomCheck` is reachable here because this module imports it; this
|
||||
module ITSELF has no module index while it is being elaborated, so its own
|
||||
declarations are the ones the environment reports with no originating
|
||||
module, and they are checked that way below. That is what makes this
|
||||
inventory cover the instrument that produces it. -/
|
||||
def driverModules : Array Name := #[`Proofs.AxiomCheck]
|
||||
|
||||
def kindOf : ConstantInfo → String
|
||||
| .axiomInfo _ => "axiom"
|
||||
| .defnInfo _ => "def"
|
||||
|
|
@ -151,6 +163,63 @@ def ppAll (e : Expr) : MetaM String := do
|
|||
for l in sorted do
|
||||
IO.println l
|
||||
IO.println s!"INV-COUNT|{sorted.size}"
|
||||
-- ── CLASS 9: the instruments' own declaration surface ────────────────────
|
||||
-- The loop above walks the CORPUS. It says nothing about the two modules
|
||||
-- that perform the audit, and until 2026-07-31 nothing else did either: an
|
||||
-- `axiom` or a `theorem` added to Proofs.AxiomCheck or to this file was
|
||||
-- invisible to every phase of the button. Both are covered here.
|
||||
--
|
||||
-- Proofs.AxiomCheck is reachable by module index because this module imports
|
||||
-- it. THIS module has no index yet — it is still being elaborated — so its
|
||||
-- own declarations are exactly those the environment reports with no
|
||||
-- originating module, which is how the inventory covers the instrument that
|
||||
-- produces it rather than exempting itself.
|
||||
--
|
||||
-- The policy is not "declare nothing": this file legitimately declares the
|
||||
-- machinery above. The policy is that an instrument may declare only inert
|
||||
-- definitions. An `axiom` here would widen the trusted base without
|
||||
-- appearing in any certificate's cone; a `theorem` here would be a claim
|
||||
-- that no certificate covers and no allowlist pins.
|
||||
let mut drvIdxs : Array Nat := #[]
|
||||
for m in driverModules do
|
||||
match env.getModuleIdx? m with
|
||||
| some i => drvIdxs := drvIdxs.push i
|
||||
| none => throwError "INVENTORY ERROR: driver module {m} is not imported"
|
||||
let mut drvNames : Std.HashSet Name := {}
|
||||
let mut drvConsts : Array (Name × ConstantInfo) := #[]
|
||||
for (n, ci) in env.constants.toList do
|
||||
let here : Bool :=
|
||||
match env.getModuleIdxFor? n with
|
||||
| some i => drvIdxs.contains i
|
||||
| none => true -- declared by this file, still being elaborated
|
||||
if here then
|
||||
drvNames := drvNames.insert n
|
||||
drvConsts := drvConsts.push (n, ci)
|
||||
let mut drv : Array String := #[]
|
||||
for (n, ci) in drvConsts do
|
||||
let k := kindOf ci
|
||||
-- An AXIOM in an instrument is never acceptable: it would widen the trusted
|
||||
-- base without appearing in any certificate's cone.
|
||||
if k == "axiom" then
|
||||
throwError "DRIVER SURFACE VIOLATION: {n} is an axiom declared by the audit \
|
||||
infrastructure. An instrument may not widen the trusted base."
|
||||
-- A THEOREM needs care rather than a flat ban. Defining a function by
|
||||
-- well-founded recursion makes the elaborator emit its own proof
|
||||
-- obligations — `LTLAccAudit.axiomCone._proof_1` is one, and a flat ban
|
||||
-- rejected this very file. The distinction that matters is whether the
|
||||
-- theorem is a CLAIM someone wrote or an ARTEFACT of a definition here:
|
||||
-- an artefact's name extends the name of a constant declared alongside it,
|
||||
-- a standalone claim's does not.
|
||||
if k == "theorem" && !drvNames.contains n.getPrefix then
|
||||
throwError "DRIVER SURFACE VIOLATION: {n} is a standalone theorem declared by \
|
||||
the audit infrastructure. An instrument may declare definitions \
|
||||
and whatever the elaborator generates for them — never a claim \
|
||||
of its own, which no certificate covers and no allowlist pins."
|
||||
drv := drv.push s!"DRV|{n}|{k}"
|
||||
let drvSorted := drv.qsort (· < ·)
|
||||
for l in drvSorted do
|
||||
IO.println l
|
||||
IO.println s!"DRV-COUNT|{drvSorted.size}"
|
||||
-- FAIL CLOSED: the statement surface must cover the inventory exactly. If
|
||||
-- these ever diverge, some constant is inventoried but unbound — which is
|
||||
-- precisely the gap this section exists to close.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,13 @@ CORES="${LEAN_MAX_CORES:-0-3}"
|
|||
|
||||
GEN_MODULES=( LTLAcc/HashExternal )
|
||||
PROOFS=( Basic Completeness Extract Descent Consistency Binding3 Refactor Theorem3 PinStore )
|
||||
# The audit infrastructure, named ONCE. These are not corpus — they are the
|
||||
# instruments — but they are Lean modules in the audited tree, so the dead-file
|
||||
# scan must know them by membership rather than by two hard-coded basename
|
||||
# comparisons, and Phase 3b must inventory what they declare. CLASS 9: until
|
||||
# 2026-07-31 nothing looked at the drivers' own declaration surface, so an
|
||||
# `axiom` or a `theorem` added to either was invisible to every phase.
|
||||
DRIVERS=( AxiomCheck Inventory )
|
||||
|
||||
# Certificates and their exact expected cones (observed via #print axioms,
|
||||
# never guessed; any drift in EITHER direction is a failure).
|
||||
|
|
@ -202,8 +209,7 @@ lake env bash -c "
|
|||
done
|
||||
for f in Proofs/*.lean; do
|
||||
b=\$(basename \"\$f\" .lean)
|
||||
[ \"\$b\" = AxiomCheck ] && continue # audit infrastructure, compiled in Phase 3
|
||||
[ \"\$b\" = Inventory ] && continue # audit infrastructure, compiled in Phase 3b
|
||||
case \" ${DRIVERS[*]} \" in (*\" \$b \"*) continue;; esac # audit infrastructure, compiled in Phase 3/3b
|
||||
case \" ${PROOFS[*]} \" in (*\" \$b \"*) ;; (*) echo \"DEAD FILE: \$f\"; exit 1;; esac
|
||||
done
|
||||
# gen/ gets the same unmanifested-source check (review round 2, GPT M1)
|
||||
|
|
@ -211,6 +217,21 @@ lake env bash -c "
|
|||
b=\"LTLAcc/\$(basename \"\$f\" .lean)\"
|
||||
case \" ${GEN_MODULES[*]} \" in (*\" \$b \"*) ;; (*) echo \"DEAD FILE (gen): \$f\"; exit 1;; esac
|
||||
done
|
||||
# CLASS 15. The two loops above look only INSIDE Proofs/ and gen/LTLAcc/, so
|
||||
# until 2026-07-31 a Lean file anywhere else was invisible: one at the
|
||||
# verification root, or under gen/AnythingElse/, was neither compiled nor
|
||||
# rejected. It could be imported by name from a manifested module — the
|
||||
# manifest names modules, and LEAN_PATH includes both roots — which is a
|
||||
# source of the corpus that no phase reads and no pin covers. Nothing may
|
||||
# live in either root but the two enumerated sets.
|
||||
shopt -s nullglob
|
||||
for f in *.lean; do echo \"DEAD FILE (verification root): \$f\"; exit 1; done
|
||||
for d in gen/*/; do
|
||||
[ \"\$d\" = 'gen/LTLAcc/' ] && continue
|
||||
for f in \"\$d\"*.lean; do echo \"DEAD FILE (gen subdirectory): \$f\"; exit 1; done
|
||||
done
|
||||
for f in gen/*.lean; do echo \"DEAD FILE (gen root): \$f\"; exit 1; done
|
||||
shopt -u nullglob
|
||||
"
|
||||
if grep -q "uses 'sorry'" "$LOG"; then echo "STUB: sorry detected"; exit 1; fi
|
||||
rm -f "$LOG"
|
||||
|
|
@ -263,16 +284,56 @@ lake env bash -c "
|
|||
|
||||
# The inventory's corpus-module list must BE the compile manifest — both
|
||||
# directions, so neither can drift from the other silently.
|
||||
# Read the module LISTS, not the file. This comparison used to grep the whole
|
||||
# of Inventory.lean for a backticked name, which meant any PROSE mention of a
|
||||
# module counted: a doc comment naming `Proofs.AxiomCheck` broke the count, and
|
||||
# — worse in the other direction — a doc mention of a module missing from the
|
||||
# array would have satisfied the presence check and hidden the omission. The
|
||||
# manifest is the arrays; read the arrays.
|
||||
MODLISTS=$(sed -n '/^def corpusModules/,/\]/p;/^def driverModules/,/\]/p' "$HERE/Proofs/Inventory.lean")
|
||||
for m in "${GEN_MODULES[@]}" "${PROOFS[@]}"; do
|
||||
mod=$(echo "$m" | sed 's|^LTLAcc/|LTLAcc.|; s|^\([A-Z]\)|Proofs.\1|; s|^Proofs\.LTLAcc\.|LTLAcc.|')
|
||||
grep -qF "\`$mod" "$HERE/Proofs/Inventory.lean" || {
|
||||
grep -qF "\`$mod" <<<"$MODLISTS" || {
|
||||
echo " MANIFEST DRIFT: $mod compiled by check.sh but not inventoried"; COVFAIL=1; }
|
||||
done
|
||||
NMANIFEST=$(( ${#GEN_MODULES[@]} + ${#PROOFS[@]} ))
|
||||
NINV=$(grep -oE '`(LTLAcc|Proofs)\.[A-Za-z0-9_.]+' "$HERE/Proofs/Inventory.lean" | wc -l)
|
||||
# The drivers are named in Inventory.lean too, now that it walks their
|
||||
# declaration surface — so they count on both sides of this equality.
|
||||
for d in "${DRIVERS[@]}"; do
|
||||
[ "$d" = Inventory ] && continue # covered as the current module, which has
|
||||
# no module index while it elaborates and
|
||||
# so is not named in its own module list
|
||||
grep -qF "\`Proofs.$d" <<<"$MODLISTS" || {
|
||||
echo " MANIFEST DRIFT: driver Proofs.$d is not inventoried"; COVFAIL=1; }
|
||||
done
|
||||
NMANIFEST=$(( ${#GEN_MODULES[@]} + ${#PROOFS[@]} + ${#DRIVERS[@]} - 1 ))
|
||||
NINV=$(grep -oE '`(LTLAcc|Proofs)\.[A-Za-z0-9_.]+' <<<"$MODLISTS" | wc -l)
|
||||
[ "$NMANIFEST" = "$NINV" ] || {
|
||||
echo " MANIFEST DRIFT: check.sh compiles $NMANIFEST modules, Inventory lists $NINV"; COVFAIL=1; }
|
||||
|
||||
# CLASS 9. The driver-surface block must actually have RUN. Its violations are
|
||||
# raised inside Lean, so a walk that silently did not execute would look exactly
|
||||
# like a clean one — the same vacuous-pass shape the INV-COUNT trailer exists to
|
||||
# close. Require the trailer, and require it to agree with the lines.
|
||||
NDRV=$(grep -c '^DRV|' "$INVLOG" || true)
|
||||
DRVTRAILER=$(grep '^DRV-COUNT|' "$INVLOG" | tail -1 | cut -d'|' -f2)
|
||||
if [ -z "$DRVTRAILER" ] || [ "$DRVTRAILER" != "$NDRV" ]; then
|
||||
echo " DRIVER SURFACE NOT OBSERVED: trailer=${DRVTRAILER:-absent}, observed $NDRV lines"
|
||||
COVFAIL=1
|
||||
elif [ "$NDRV" -eq 0 ]; then
|
||||
echo " DRIVER SURFACE NOT OBSERVED: the instruments declare nothing at all,"
|
||||
echo " which cannot be true — Inventory.lean declares its own machinery."
|
||||
COVFAIL=1
|
||||
else
|
||||
echo " driver surface: $NDRV declarations across the audit instruments, no axiom, no claim"
|
||||
fi
|
||||
# NOTE ON WHAT THIS DOES NOT DO. It does not pin WHICH definitions the
|
||||
# instruments declare — a new inert `def` in a driver is allowed. That is
|
||||
# deliberate: both drivers are byte-pinned in HARNESS.sha256 (Phase 0c), so
|
||||
# their contents cannot drift unnoticed, and a second policy file listing their
|
||||
# internals would add a thing to maintain without adding a thing to catch. What
|
||||
# the check above adds is the property byte-pinning cannot give: that no
|
||||
# instrument declares an AXIOM or a CLAIM, whatever its bytes are.
|
||||
|
||||
# CONES ⊆ allowlist with IDENTICAL cones: the #print-axioms-pinned table
|
||||
# and the environment inventory are two independent computations of the
|
||||
# same facts — any disagreement is a failure of one of them.
|
||||
|
|
|
|||
|
|
@ -39,6 +39,16 @@ trap 'echo "(scratch tree kept for inspection: $WORK)"' ERR
|
|||
echo "=== audit-gate self-test (scratch: $WORK) ==="
|
||||
cp -a "$SRC" "$WORK/verification"
|
||||
T="$WORK/verification"
|
||||
# The button also reads two documents from the REPOSITORY root, one level above
|
||||
# verification/. Copying only verification/ left them missing, so check.sh in
|
||||
# the scratch tree always died in Phase 3c with DOC DRIFT — which meant every
|
||||
# `if check.sh; then <this attack was not caught>` guard below was unfirable:
|
||||
# check.sh could not pass in here even with no attack at all, so those guards
|
||||
# asserted nothing. Only the diagnostic greps were doing any work. Copy the
|
||||
# documents so a genuinely-undetected attack would now show up as a PASS.
|
||||
for d in README.md STATEMENT-MAP.md; do
|
||||
[ -f "$SRC/../$d" ] && cp "$SRC/../$d" "$WORK/$d"
|
||||
done
|
||||
cp "$T/Proofs/PinStore.lean" "$T/PinStore.pristine"
|
||||
|
||||
# Recompile the injected leaf module + the inventory, then run the gate.
|
||||
|
|
@ -148,6 +158,96 @@ grep -q "DEAD FILE (gen): gen/LTLAcc/Rogue.lean" "$T/check9b.out" || {
|
|||
echo " ✗ case 9b: check.sh failed without DEAD FILE (gen) diagnosis"; tail -5 "$T/check9b.out"; exit 1; }
|
||||
echo " ✓ case 9b pinned but unmanifested: check.sh dies with DEAD FILE (gen)"
|
||||
|
||||
# Case 9 was the last case when it was written, so it left its rogue file and
|
||||
# its pin in place — harmless then, but the cases below inherit the tree. Undo
|
||||
# it here rather than in case 9, so that case keeps testing exactly what it
|
||||
# tested before.
|
||||
rm -f "$T/gen/LTLAcc/Rogue.lean"
|
||||
( cd "$T" && grep -v ' gen/LTLAcc/Rogue.lean$' HARNESS.sha256 > .h && mv .h HARNESS.sha256 )
|
||||
|
||||
# ── CLASS 15: a Lean file where no phase was looking ────────────────────────
|
||||
# Until 2026-07-31 the dead-file scan read Proofs/*.lean and gen/LTLAcc/*.lean
|
||||
# and nothing else. A module at the verification root, or under any other gen/
|
||||
# subdirectory, was neither compiled nor rejected — while being importable by
|
||||
# name, since LEAN_PATH contains both roots. A source of the corpus that no
|
||||
# phase reads and no pin covers is precisely what the dead-file gate exists to
|
||||
# forbid; it was simply looking in two places instead of everywhere.
|
||||
|
||||
# 10 — a stray module at the verification root. NOTE: Phase 0c does not stand
|
||||
# in front of this one. Its required-pin set is executables plus
|
||||
# gen/**.lean, so a root .lean is invisible to it; the Phase 2 check added
|
||||
# for this class is the only gate here.
|
||||
printf '/- rogue -/\ntheorem rogue_root : 1 = 1 := rfl\n' > "$T/Rogue.lean"
|
||||
if SKIP_FIDELITY=1 "$T/check.sh" > "$T/check10.out" 2>&1; then
|
||||
echo " ✗ case 10: check.sh PASSED with a stray Rogue.lean at the verification root"; exit 1
|
||||
fi
|
||||
grep -q "DEAD FILE (verification root): Rogue.lean" "$T/check10.out" || {
|
||||
echo " ✗ case 10: check.sh failed without the root diagnosis"; tail -5 "$T/check10.out"; exit 1; }
|
||||
echo " ✓ case 10 stray module at the verification root: DEAD FILE (verification root)"
|
||||
rm -f "$T/Rogue.lean"
|
||||
|
||||
# 11 — a module in a gen/ subdirectory that is not LTLAcc/. Two gates again,
|
||||
# and both are exercised for the same reason as case 9.
|
||||
mkdir -p "$T/gen/Rogue"
|
||||
printf '/- rogue -/\ntheorem rogue_sub : 1 = 1 := rfl\n' > "$T/gen/Rogue/Extra.lean"
|
||||
if SKIP_FIDELITY=1 "$T/check.sh" > "$T/check11a.out" 2>&1; then
|
||||
echo " ✗ case 11a: check.sh PASSED with an unpinned gen/Rogue/Extra.lean"; exit 1
|
||||
fi
|
||||
grep -q "does not match HARNESS.sha256" "$T/check11a.out" || {
|
||||
echo " ✗ case 11a: check.sh failed without the harness-set diagnosis"; tail -5 "$T/check11a.out"; exit 1; }
|
||||
echo " ✓ case 11a unpinned module in a foreign gen subdirectory: harness-set mismatch"
|
||||
|
||||
( cd "$T" && sha256sum gen/Rogue/Extra.lean >> HARNESS.sha256 )
|
||||
if SKIP_FIDELITY=1 "$T/check.sh" > "$T/check11b.out" 2>&1; then
|
||||
echo " ✗ case 11b: check.sh PASSED with a pinned gen/Rogue/Extra.lean"; exit 1
|
||||
fi
|
||||
grep -q "DEAD FILE (gen subdirectory): gen/Rogue/Extra.lean" "$T/check11b.out" || {
|
||||
echo " ✗ case 11b: check.sh failed without the subdirectory diagnosis"; tail -5 "$T/check11b.out"; exit 1; }
|
||||
echo " ✓ case 11b pinned but in a foreign gen subdirectory: DEAD FILE (gen subdirectory)"
|
||||
rm -rf "$T/gen/Rogue"
|
||||
( cd "$T" && grep -v ' gen/Rogue/Extra.lean$' HARNESS.sha256 > .h && mv .h HARNESS.sha256 )
|
||||
|
||||
# ── CLASS 9: the instruments' own declaration surface ───────────────────────
|
||||
# The audit drivers are not corpus, so nothing inventoried what THEY declare.
|
||||
# Both are byte-pinned, so each attack must re-pin to reach the new gate —
|
||||
# which is the point: byte-pinning stops drift, it does not stop an author.
|
||||
# What follows is what byte-pinning cannot give.
|
||||
|
||||
# 12 — an axiom in the inventory driver itself, INDENTED. Phase 1 greps
|
||||
# Proofs/*.lean for `^axiom `, so an unindented one is caught there and
|
||||
# never reaches the new gate. Indentation is the documented evasion of
|
||||
# that regex — measured on Lean v4.30.0-rc2 — so this case is the one
|
||||
# that actually exercises the kernel-side driver walk rather than the
|
||||
# source scan standing in front of it.
|
||||
sed -i 's|^#eval show MetaM Unit| axiom driver_cheat : False\n\n#eval show MetaM Unit|' \
|
||||
"$T/Proofs/Inventory.lean"
|
||||
grep -q '^ axiom driver_cheat' "$T/Proofs/Inventory.lean" || {
|
||||
echo " ✗ case 12: could not inject the indented axiom (case would be vacuous)"; exit 1; }
|
||||
( cd "$T" && grep -v ' Proofs/Inventory.lean$' HARNESS.sha256 > .h \
|
||||
&& sha256sum Proofs/Inventory.lean >> .h && mv .h HARNESS.sha256 )
|
||||
if SKIP_FIDELITY=1 "$T/check.sh" > "$T/check12.out" 2>&1; then
|
||||
echo " ✗ case 12: check.sh PASSED with an axiom declared in Proofs/Inventory.lean"; exit 1
|
||||
fi
|
||||
grep -q "DRIVER SURFACE VIOLATION" "$T/check12.out" || {
|
||||
echo " ✗ case 12: check.sh failed without the driver-surface diagnosis"; tail -5 "$T/check12.out"; exit 1; }
|
||||
echo " ✓ case 12 indented axiom in the inventory driver: DRIVER SURFACE VIOLATION"
|
||||
cp "$SRC/Proofs/Inventory.lean" "$T/Proofs/Inventory.lean"
|
||||
( cd "$T" && grep -v ' Proofs/Inventory.lean$' HARNESS.sha256 > .h \
|
||||
&& sha256sum Proofs/Inventory.lean >> .h && mv .h HARNESS.sha256 )
|
||||
|
||||
# 13 — a standalone claim in the OTHER driver. A theorem there is not a
|
||||
# compiler artefact of any definition, which is exactly the distinction
|
||||
# the gate draws: it must admit `axiomCone._proof_1` and reject this.
|
||||
printf '\ntheorem driver_claim : 1 = 1 := rfl\n' >> "$T/Proofs/AxiomCheck.lean"
|
||||
( cd "$T" && grep -v ' Proofs/AxiomCheck.lean$' HARNESS.sha256 > .h && sha256sum Proofs/AxiomCheck.lean >> .h && mv .h HARNESS.sha256 )
|
||||
if SKIP_FIDELITY=1 "$T/check.sh" > "$T/check13.out" 2>&1; then
|
||||
echo " ✗ case 13: check.sh PASSED with a standalone theorem in Proofs/AxiomCheck.lean"; exit 1
|
||||
fi
|
||||
grep -q "DRIVER SURFACE VIOLATION" "$T/check13.out" || {
|
||||
echo " ✗ case 13: check.sh failed without the driver-surface diagnosis"; tail -5 "$T/check13.out"; exit 1; }
|
||||
echo " ✓ case 13 standalone claim in the axiom-check driver: DRIVER SURFACE VIOLATION"
|
||||
cp "$SRC/Proofs/AxiomCheck.lean" "$T/Proofs/AxiomCheck.lean"
|
||||
|
||||
rm -rf "$WORK"
|
||||
trap - ERR
|
||||
echo "=== SELF-TEST GREEN: 10 attack cases defeated + positive control ==="
|
||||
echo "=== SELF-TEST GREEN: 14 attack cases defeated + positive control ==="
|
||||
|
|
|
|||
Loading…
Reference in a new issue