Review round 3: environment-derived audit surface, self-contained kit
Round-2 external reviews (GPT-5.6 + second Claude) converged on the
coverage gate being evadable (H1/NEW-1); GPT additionally proved the
kit's fidelity target could not run (H2) and the namespace-collision
attack that defeats any source-regex fix. This round adopts GPT's
required correction in full:
- Proofs/Inventory.lean: declaration inventory read from the compiled
Lean environment — every constant of every corpus module, fully
qualified, unfiltered (compiler auxiliaries and _private mangles
pinned too), with kind and axiom cone; own cone walker cross-checked
in-process against core collectAxioms (hard error on divergence).
- verification/inventory-allowlist.txt: all 218 constants pinned.
- inventory_gate.sh: fail-closed diff both directions (UNCLASSIFIED /
STALE), INV-COUNT truncation guard, exactly-one-axiom invariant.
- check.sh Phase 3b rewritten around the gate + manifest⇔inventory
drift checks + CONES⇔inventory cone cross-check (two independent
computations must agree). EXCLUDE table gone (sha256/Bytes are
ordinary audited entries now).
- selftest_audit.sh: 9 adversarial cases against the production gate
(attributed/indented/private/instance, namespace collision, smuggled
axiom, deleted decl, unmanifested Proofs/ and gen/ modules) + positive
control — all defeated (GPT release condition 2).
- M1: recursive orphan-olean guard (caught a stray dev artifact on its
first run), gen/ dead-file check, corpus-wide single-axiom pin.
- L1/NEW-2: acceptIncl_sound drops the redundant hm (derived from
hacc.1); cone unchanged.
- M2/M3: STATEMENT-MAP counts 230,271/230,016; non-vacuity guard
wording narrowed to what the guards actually certify.
- README layer table: stale L4/pin-store rows fixed (missed by both
round-2 reviewers AND the round-2 revision — found in self-review).
- KNOWN-GAPS 12 (audit-gate lineage + residual limits), 13 (round-2 kit
target not self-contained); gap 2 count fixed.
- RESPONSE-TO-REVIEWERS.md: round-3 disposition of every finding.
Kit round 3 additionally ships the complete stdlib-only import closure
of pacta.transparency (content-addressed vs pacta 3d81d53), the
clean-extraction fidelity transcript (exit 0, 230,271+230,016, zero
mismatches), the ATTESTATION GREEN check.sh transcript, and the
self-test transcript.
The live LTL remains untouched (12 leaves, root bcd15f9d…);
attestation stays blocked pending ePrint decision + author review +
explicit operator order.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:32:18 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# selftest_audit.sh — adversarial self-test of the audit gate.
|
|
|
|
|
#
|
|
|
|
|
# Review round 2 (GPT H1 / Claude NEW-1) demonstrated that the old
|
|
|
|
|
# source-regex coverage gate was evadable. This script proves the
|
|
|
|
|
# replacement is not, by ATTACKING THE SHIPPING LOGIC: every case copies
|
|
|
|
|
# the corpus to a scratch tree, injects a declaration the old gate could
|
|
|
|
|
# not see, recompiles through lean-guard, and asserts that the exact
|
|
|
|
|
# inventory_gate.sh used by check.sh Phase 3b FAILS with the expected
|
|
|
|
|
# diagnosis. The two unmanifested-module cases run the full check.sh.
|
|
|
|
|
#
|
|
|
|
|
# Cases (release condition 2 of the round-2 GPT review, plus two):
|
|
|
|
|
# 0 positive control: pristine tree must PASS (guards against a gate
|
|
|
|
|
# that fails everything — a vacuous self-test)
|
|
|
|
|
# 1 attributed theorem @[simp] theorem …
|
|
|
|
|
# 2 indented theorem (leading whitespace)
|
|
|
|
|
# 3 private theorem private theorem …
|
|
|
|
|
# 4 instance declaration instance … : Nonempty Nat
|
|
|
|
|
# 5 nested namespace collision LTLAcc.Hidden.MTH vs audited LTLAcc.MTH
|
|
|
|
|
# 6 smuggled axiom axiom rogue : True
|
|
|
|
|
# 7 deleted declaration STALE direction of the diff
|
|
|
|
|
# 8 unmanifested Proofs/ module full check.sh must die: DEAD FILE
|
|
|
|
|
# 9 unmanifested gen/ module full check.sh must die: DEAD FILE (gen)
|
|
|
|
|
#
|
|
|
|
|
# Run AFTER a green check.sh (needs compiled .oleans in the tree).
|
|
|
|
|
# All Lean work goes through lean-guard (memory-capped, single-flight).
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
set -euo pipefail
|
Review round 4: F1* absorbed (lied-size boundary), acceptCons_sound, kit reproducibility
Round-3 verdicts: GPT-5.6 conditionally approves (blockers closed, one
portability finding); the Claude reviewer's Socratic addendum produced
F1*, the strongest finding of the series — deployed verify_consistency
and mechanized ConsRec are NOT extensionally equal. Reproduced exactly
(witness verify_consistency(1,3,R2,R3,P(2→3))=True vs ConsRec reject;
3,405 divergences n<60; strictly one-sided; power-of-two seeding
mechanism confirmed in source).
- KNOWN-GAPS gap 14: witness, mechanism, one-sidedness, and the
pinned-pair side condition under which Theorem 3 transfers to the
deployed verifier (pacta's pin-store flow supplies it by
construction). No pacta code change; deployed behavior matches
upstream RFC 9162 implementations.
- fidelity: lied-size family — 73,573 boundary cases, 3,867 expected
divergences PINNED, one-sided direction asserted per case. Banner
rescoped: agreement over pinned families, not extensional equality.
- Theorem3.lean: acceptCons_sound (F2) — soundness over the named
acceptCons predicate, n₀=0 discharged from the non-prefix premise,
size bound derived from acceptance via new consRec_some_le. Cones
read from #print axioms; CONES/AxiomCheck/allowlist updated
(218 → 222 constants, diff = the two theorems + two generated
auxiliaries).
- F3/GPT§7: verification/lean-toolchain pin + run_bare.sh (reviewer's
standalone runner, plain public lean — verified green: 61 cones, 222
constants, gate green) + AENEAS_ENV override in check.sh and
selftest_audit.sh.
- F4: awk field-equality replaces regex-with-dots in Phase 3b.
- F5: git-tracked .pyc removed (worse than reported — it was in the
repo, not just the kit); __pycache__ gitignored; round-4 kit ships a
corpus MANIFEST.sha256 + pinned commit (also GPT's governance
condition).
check.sh exit 0, ATTESTATION GREEN; selftest exit 0, 9/9 + control.
Live LTL untouched (12 leaves, bcd15f9d…); attestation still gated on
ePrint decision + author review + explicit operator order.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:07:57 +00:00
|
|
|
AENEAS_ENV="${AENEAS_ENV:-$HOME/aeneas-toolchain/env.sh}"
|
|
|
|
|
[ -f "$AENEAS_ENV" ] || { echo "FATAL: Aeneas environment not found: $AENEAS_ENV"; exit 1; }
|
|
|
|
|
source "$AENEAS_ENV"
|
Review round 3: environment-derived audit surface, self-contained kit
Round-2 external reviews (GPT-5.6 + second Claude) converged on the
coverage gate being evadable (H1/NEW-1); GPT additionally proved the
kit's fidelity target could not run (H2) and the namespace-collision
attack that defeats any source-regex fix. This round adopts GPT's
required correction in full:
- Proofs/Inventory.lean: declaration inventory read from the compiled
Lean environment — every constant of every corpus module, fully
qualified, unfiltered (compiler auxiliaries and _private mangles
pinned too), with kind and axiom cone; own cone walker cross-checked
in-process against core collectAxioms (hard error on divergence).
- verification/inventory-allowlist.txt: all 218 constants pinned.
- inventory_gate.sh: fail-closed diff both directions (UNCLASSIFIED /
STALE), INV-COUNT truncation guard, exactly-one-axiom invariant.
- check.sh Phase 3b rewritten around the gate + manifest⇔inventory
drift checks + CONES⇔inventory cone cross-check (two independent
computations must agree). EXCLUDE table gone (sha256/Bytes are
ordinary audited entries now).
- selftest_audit.sh: 9 adversarial cases against the production gate
(attributed/indented/private/instance, namespace collision, smuggled
axiom, deleted decl, unmanifested Proofs/ and gen/ modules) + positive
control — all defeated (GPT release condition 2).
- M1: recursive orphan-olean guard (caught a stray dev artifact on its
first run), gen/ dead-file check, corpus-wide single-axiom pin.
- L1/NEW-2: acceptIncl_sound drops the redundant hm (derived from
hacc.1); cone unchanged.
- M2/M3: STATEMENT-MAP counts 230,271/230,016; non-vacuity guard
wording narrowed to what the guards actually certify.
- README layer table: stale L4/pin-store rows fixed (missed by both
round-2 reviewers AND the round-2 revision — found in self-review).
- KNOWN-GAPS 12 (audit-gate lineage + residual limits), 13 (round-2 kit
target not self-contained); gap 2 count fixed.
- RESPONSE-TO-REVIEWERS.md: round-3 disposition of every finding.
Kit round 3 additionally ships the complete stdlib-only import closure
of pacta.transparency (content-addressed vs pacta 3d81d53), the
clean-extraction fidelity transcript (exit 0, 230,271+230,016, zero
mismatches), the ATTESTATION GREEN check.sh transcript, and the
self-test transcript.
The live LTL remains untouched (12 leaves, root bcd15f9d…);
attestation stays blocked pending ePrint decision + author review +
explicit operator order.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:32:18 +00:00
|
|
|
SRC="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
AENEAS_LEAN="$AENEAS_HOME/backends/lean"
|
|
|
|
|
CORES="${LEAN_MAX_CORES:-0-3}"
|
|
|
|
|
|
|
|
|
|
WORK=$(mktemp -d /tmp/acc-selftest-XXXX)
|
|
|
|
|
trap 'echo "(scratch tree kept for inspection: $WORK)"' ERR
|
|
|
|
|
echo "=== audit-gate self-test (scratch: $WORK) ==="
|
|
|
|
|
cp -a "$SRC" "$WORK/verification"
|
|
|
|
|
T="$WORK/verification"
|
|
|
|
|
cp "$T/Proofs/PinStore.lean" "$T/PinStore.pristine"
|
|
|
|
|
|
|
|
|
|
# Recompile the injected leaf module + the inventory, then run the gate.
|
|
|
|
|
# Returns the gate's exit code; gate output goes to $T/gate.out.
|
|
|
|
|
run_gate() {
|
|
|
|
|
cd "$AENEAS_LEAN"
|
|
|
|
|
lake env bash -c "
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
cd '$T' && export LEAN_PATH=\"\$LEAN_PATH:$T/gen:$T\"
|
|
|
|
|
LEAN_TIMEOUT=600 LEAN_MAX_CORES=$CORES '$T/lean-guard' Proofs/PinStore.lean >/dev/null 2>&1
|
|
|
|
|
LEAN_TIMEOUT=600 LEAN_MAX_CORES=$CORES '$T/lean-guard' Proofs/Inventory.lean
|
|
|
|
|
" > "$T/inv.out" 2>&1 || { echo " (inventory compile failed — see $T/inv.out)"; return 99; }
|
|
|
|
|
"$T/inventory_gate.sh" "$T/inv.out" "$T/inventory-allowlist.txt" > "$T/gate.out" 2>&1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expect_fail() { # $1=case label $2=grep pattern expected in gate output
|
|
|
|
|
local rc=0; run_gate || rc=$?
|
|
|
|
|
if [ "$rc" = 0 ]; then
|
|
|
|
|
echo " ✗ $1: gate PASSED but must fail"; echo "SELF-TEST FAILED"; exit 1
|
|
|
|
|
elif [ "$rc" = 99 ]; then
|
|
|
|
|
echo " ✗ $1: injected code did not compile (case is vacuous)"; exit 1
|
|
|
|
|
elif ! grep -q "$2" "$T/gate.out"; then
|
|
|
|
|
echo " ✗ $1: gate failed but without expected diagnosis '$2':"
|
|
|
|
|
sed 's/^/ /' "$T/gate.out"; exit 1
|
|
|
|
|
fi
|
|
|
|
|
echo " ✓ $1: gate fails with $(grep -c "$2" "$T/gate.out") '$2' line(s)"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
restore() { cp "$T/PinStore.pristine" "$T/Proofs/PinStore.lean"; }
|
|
|
|
|
|
|
|
|
|
# 0 — positive control
|
|
|
|
|
if run_gate; then echo " ✓ case 0 control: pristine tree passes the gate"; else
|
|
|
|
|
echo " ✗ case 0 control: pristine tree FAILED the gate:"; sed 's/^/ /' "$T/gate.out"; exit 1; fi
|
|
|
|
|
|
|
|
|
|
# 1 — attributed
|
|
|
|
|
restore; printf '\n@[simp] theorem smuggled_attr : 1 = 1 := rfl\n' >> "$T/Proofs/PinStore.lean"
|
|
|
|
|
expect_fail "case 1 attributed theorem" "UNCLASSIFIED.*smuggled_attr"
|
|
|
|
|
|
|
|
|
|
# 2 — indented
|
|
|
|
|
restore; printf '\n theorem smuggled_indent : 3 = 3 := rfl\n' >> "$T/Proofs/PinStore.lean"
|
|
|
|
|
expect_fail "case 2 indented theorem" "UNCLASSIFIED.*smuggled_indent"
|
|
|
|
|
|
|
|
|
|
# 3 — private
|
|
|
|
|
restore; printf '\nprivate theorem smuggled_private : 2 = 2 := rfl\n' >> "$T/Proofs/PinStore.lean"
|
|
|
|
|
expect_fail "case 3 private theorem" "UNCLASSIFIED.*_private.*smuggled_private"
|
|
|
|
|
|
|
|
|
|
# 4 — instance
|
|
|
|
|
restore; printf '\ninstance smuggledInst : Nonempty Nat := ⟨0⟩\n' >> "$T/Proofs/PinStore.lean"
|
|
|
|
|
expect_fail "case 4 instance" "UNCLASSIFIED.*smuggledInst"
|
|
|
|
|
|
|
|
|
|
# 5 — nested namespace reusing an audited basename
|
|
|
|
|
restore; printf '\nnamespace LTLAcc.Hidden\ntheorem MTH : 1 = 1 := rfl\nend LTLAcc.Hidden\n' >> "$T/Proofs/PinStore.lean"
|
|
|
|
|
expect_fail "case 5 namespace collision (LTLAcc.Hidden.MTH)" "UNCLASSIFIED.*LTLAcc\.Hidden\.MTH"
|
|
|
|
|
|
|
|
|
|
# 6 — smuggled axiom
|
|
|
|
|
restore; printf '\naxiom rogue : True\n' >> "$T/Proofs/PinStore.lean"
|
|
|
|
|
expect_fail "case 6 smuggled axiom" "AXIOM SURFACE DRIFT"
|
|
|
|
|
|
|
|
|
|
# 7 — deleted declaration (STALE direction)
|
|
|
|
|
restore
|
|
|
|
|
python3 - "$T/Proofs/PinStore.lean" <<'EOF'
|
|
|
|
|
import sys
|
|
|
|
|
p = sys.argv[1]; s = open(p).read()
|
|
|
|
|
# drop the trailing nonvacuity guard (a leaf theorem nothing imports),
|
|
|
|
|
# including its doc comment — an orphaned /-- ... -/ would not compile
|
|
|
|
|
i = s.rindex("/-- Permanent non-vacuity witness for pin_prefix_correct")
|
|
|
|
|
j = s.index("end LTLAcc", i)
|
|
|
|
|
open(p, "w").write(s[:i] + s[j:])
|
|
|
|
|
EOF
|
|
|
|
|
expect_fail "case 7 deleted declaration" "STALE.*pin_prefix_nonvacuous"
|
|
|
|
|
|
|
|
|
|
restore
|
|
|
|
|
rm -f "$T/PinStore.pristine"
|
|
|
|
|
|
|
|
|
|
# 8 — unmanifested Proofs/ module (full check.sh; dies in Phase 2)
|
|
|
|
|
printf '/- rogue -/\ntheorem rogue_thm : 1 = 1 := rfl\n' > "$T/Proofs/Rogue.lean"
|
|
|
|
|
if SKIP_FIDELITY=1 "$T/check.sh" > "$T/check8.out" 2>&1; then
|
|
|
|
|
echo " ✗ case 8: check.sh PASSED with unmanifested Proofs/Rogue.lean"; exit 1
|
|
|
|
|
fi
|
|
|
|
|
grep -q "DEAD FILE: Proofs/Rogue.lean" "$T/check8.out" || {
|
|
|
|
|
echo " ✗ case 8: check.sh failed without DEAD FILE diagnosis"; tail -5 "$T/check8.out"; exit 1; }
|
|
|
|
|
echo " ✓ case 8 unmanifested Proofs module: check.sh dies with DEAD FILE"
|
|
|
|
|
rm -f "$T/Proofs/Rogue.lean"
|
|
|
|
|
|
|
|
|
|
# 9 — unmanifested gen/ module (full check.sh; dies in Phase 2)
|
|
|
|
|
printf '/- rogue -/\ntheorem rogue_gen : 1 = 1 := rfl\n' > "$T/gen/LTLAcc/Rogue.lean"
|
|
|
|
|
if SKIP_FIDELITY=1 "$T/check.sh" > "$T/check9.out" 2>&1; then
|
|
|
|
|
echo " ✗ case 9: check.sh PASSED with unmanifested gen/LTLAcc/Rogue.lean"; exit 1
|
|
|
|
|
fi
|
|
|
|
|
grep -q "DEAD FILE (gen): gen/LTLAcc/Rogue.lean" "$T/check9.out" || {
|
|
|
|
|
echo " ✗ case 9: check.sh failed without DEAD FILE (gen) diagnosis"; tail -5 "$T/check9.out"; exit 1; }
|
|
|
|
|
echo " ✓ case 9 unmanifested gen module: check.sh dies with DEAD FILE (gen)"
|
|
|
|
|
|
|
|
|
|
rm -rf "$WORK"
|
|
|
|
|
trap - ERR
|
|
|
|
|
echo "=== SELF-TEST GREEN: 9 attack cases defeated + positive control ==="
|