mirror of
https://github.com/saymrwulf/ltl-accumulator-verified.git
synced 2026-09-08 20:40:35 +00:00
No theorem was wrong; every fix is spec-surface, audit-mechanism, docs, or harness coverage. Changes: LEAN (Claude F1, GPT M4): - acceptIncl: the consumer's inclusion accept (m<n ∧ Root=some r) is now a named object, not just a theorem hypothesis. Root alone accepts out-of-range m; acceptIncl pins the guard. - acceptIncl_complete / acceptIncl_sound: route Thm 1/2 through it. - extractCons_correct_paper: Thm 3 at the paper's exact quantifiers (n₀≤n₁, no separate 0<n₀; n₀=0 discharged since D₀=[]=take 0). SCRIPT (GPT H1/H2, Claude F3): - Phase 3b: fail-closed audit-surface COVERAGE — every named decl under Proofs/ and gen/ must be in CONES or a documented EXCLUDE (sha256, Bytes); anonymous gen instances count-pinned; every CONES key must be queried by AxiomCheck (no pin-but-never-check). Tested: an unclassified theorem now makes the button exit 1. - H2: distinct markers — LEAN GREEN always, ATTESTATION GREEN only when fidelity actually ran; SKIP/absent-pacta no longer emit the strong marker. Attestation gate keys on ATTESTATION GREEN. - Phase 0: orphan-olean guard (every Proofs/*.olean needs a sibling .lean); deleted 6 orphans; untracked all *.olean/.lake from git and gitignored them (root cause of the F3 tarball leak). HARNESS (Claude F1, GPT M3): - added out-of-range families (m≥n, m>n, n₀>n₁, n₀=0); re-pinned counts 230,271 / 230,016 (match the reviewer's independent RFC difftest exactly); narrowed 'exhaustive' wording to the tested domain. DOCS: README stale rows fixed (freeze banner no longer contradicts table); KNOWN-GAPS gap 3 reworded (general Lemma 2 = specializations), +gaps 9 (cost), 10 (pin init), 11 (acceptIncl resolved); STATEMENT-MAP +acceptIncl rows, +Lemma-2-general note, +constant-vs-property clarification for §10(i). Button: EXIT 0, coverage complete, ATTESTATION GREEN, 230,271/230,016. 56 pinned cones over an ENFORCED surface. LTL untouched (12, bcd15f9d). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
215 lines
10 KiB
Bash
Executable file
215 lines
10 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# check.sh — THE button (accumulator corpus). Same discipline as the
|
|
# *-ed25519-verified repos: compiles every shipped .lean through lean-guard
|
|
# and axiom-audits every certificate against its DOCUMENTED exact cone,
|
|
# both directions.
|
|
#
|
|
# Phases: 0 resource/integrity · 1 stub+axiom-smuggling audit ·
|
|
# 2 compile manifest · 3 boundary-exact axiom audit
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
set -euo pipefail
|
|
source ~/aeneas-toolchain/env.sh
|
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
AENEAS_LEAN="$AENEAS_HOME/backends/lean"
|
|
TIMEOUT="${LEAN_TIMEOUT:-600}"
|
|
export LEAN_MEM_MB="${LEAN_MEM_MB:-4096}"
|
|
CORES="${LEAN_MAX_CORES:-0-3}"
|
|
|
|
GEN_MODULES=( LTLAcc/HashExternal )
|
|
PROOFS=( Basic Completeness Extract Descent Consistency Binding3 Refactor Theorem3 PinStore )
|
|
|
|
# Certificates and their exact expected cones (observed via #print axioms,
|
|
# never guessed; any drift in EITHER direction is a failure).
|
|
# AUDIT SURFACE: every theorem/def under Proofs/ (52) + the two load-bearing
|
|
# gen/ instances (Inhabited/DecidableEq Hash). Excluded by nature: the
|
|
# sanctioned axiom itself (sha256 IS the boundary) and `abbrev Bytes`
|
|
# (a bare type alias, no cone content).
|
|
declare -A CONES=(
|
|
[LTLAcc.domsep]=""
|
|
[LTLAcc.kbelow_pos]="propext, Quot.sound"
|
|
[LTLAcc.kbelow_lt]="propext, Quot.sound"
|
|
[LTLAcc.le_two_kbelow]="propext, Quot.sound"
|
|
[LTLAcc.kbelow_pow2]="propext, Quot.sound"
|
|
[LTLAcc.MTH]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.Root]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.ConsRec]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.Path]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.incl_complete]="propext, Classical.choice, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.hnode_preimage_inj]="propext"
|
|
[LTLAcc.IsCollision]="LTLAcc.sha256"
|
|
[LTLAcc.extractIncl]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.extractIncl_correct]="propext, Classical.choice, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.extractIncl_nonvacuous]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.extractMTH]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.extractMTH_correct]="propext, Classical.choice, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.extractMTH_nonvacuous]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.kbelow_prefix_eq]="propext, Quot.sound"
|
|
[LTLAcc.take_take_le]="propext, Quot.sound"
|
|
[LTLAcc.take_drop_prefix]="propext, Classical.choice, Quot.sound"
|
|
[LTLAcc.extractConsNode]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.take_all]="propext"
|
|
[LTLAcc.consRecBinding]="propext, Classical.choice, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.consRec_base_false_eq]="propext, Classical.choice, Quot.sound"
|
|
[LTLAcc.consRec_base_true_eq]="propext"
|
|
[LTLAcc.extractCons]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.extractCons_correct]="propext, Classical.choice, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.extractCons_nonvacuous]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.pinAccept_monotone]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.pin_prefix_correct]="propext, Classical.choice, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.fork_distinct]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.pin_prefix_nonvacuous]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.MTH_single]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.MTH_split]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.Root_left]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.Root_one]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.Root_one_cons]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.Root_right]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.acceptCons]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.exists_singleton_of_length_one]="propext, Classical.choice, Quot.sound"
|
|
[LTLAcc.getD_drop]="propext, Quot.sound"
|
|
[LTLAcc.getD_take]="propext, Quot.sound"
|
|
[LTLAcc.hleaf]="LTLAcc.sha256"
|
|
[LTLAcc.hnode]="LTLAcc.sha256"
|
|
[LTLAcc.kbelow]="propext, Quot.sound"
|
|
[LTLAcc.kbelow_eq_of_pow2_between]="propext, Quot.sound"
|
|
[LTLAcc.pinAccept]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.pinExtract]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.pow2_exp_unique]="propext, Quot.sound"
|
|
[LTLAcc.take_append_drop]=""
|
|
[LTLAcc.eq_dropLast_append_of_getLast?]="propext"
|
|
[LTLAcc.instInhabitedHash]="propext"
|
|
[LTLAcc.instDecidableEqHash]=""
|
|
[LTLAcc.Hash]=""
|
|
[LTLAcc.acceptIncl]="propext, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.acceptIncl_complete]="propext, Classical.choice, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.acceptIncl_sound]="propext, Classical.choice, LTLAcc.sha256, Quot.sound"
|
|
[LTLAcc.extractCons_correct_paper]="propext, Classical.choice, LTLAcc.sha256, Quot.sound"
|
|
)
|
|
|
|
# Sanctioned exclusions from the cone audit (documented, not silent):
|
|
# sha256 = THE boundary axiom (it IS the assumption)
|
|
# Bytes = bare type alias (abbrev), no cone content
|
|
declare -A EXCLUDE=( [sha256]=1 [Bytes]=1 )
|
|
|
|
free -m | awk '/Mem:/{if($7<2048){print "FATAL: <2GB RAM available — refusing to compile"; exit 1}}'
|
|
echo "=== Phase 0: source integrity ==="
|
|
for f in "$HERE"/gen/LTLAcc/*.lean "$HERE"/Proofs/*.lean; do
|
|
[ -f "$f" ] || continue
|
|
if ! grep -qE '^(/-|import |namespace |theorem |def |noncomputable |open |set_option |--|abbrev )' "$f"; then
|
|
echo "CORRUPTED: $f is not Lean source. Restore: git checkout HEAD -- $f"; exit 1
|
|
fi
|
|
done
|
|
echo " all sources valid"
|
|
for o in "$HERE"/Proofs/*.olean; do
|
|
[ -f "$o" ] || continue
|
|
[ -f "${o%.olean}.lean" ] || { echo "ORPHAN OLEAN: $o has no sibling .lean (stale artifact)"; exit 1; }
|
|
done
|
|
|
|
echo "=== Phase 1: stub + axiom-smuggling audit ==="
|
|
if grep -rn 'by trivial' "$HERE"/Proofs/*.lean 2>/dev/null; then
|
|
echo "STUB DETECTED"; exit 1; fi
|
|
if grep -rn ' : True :=' "$HERE"/Proofs/*.lean 2>/dev/null; then
|
|
echo "STUB DETECTED: True-target theorem"; exit 1; fi
|
|
if grep -rnE '^(private |protected |noncomputable )*axiom ' "$HERE"/Proofs/*.lean 2>/dev/null; then
|
|
echo "AXIOM SMUGGLING DETECTED: axiom under Proofs/ — gen/ is the only sanctioned site."; exit 1
|
|
fi
|
|
echo " clean"
|
|
|
|
echo "=== Phase 2: compile ==="
|
|
LOG=$(mktemp /tmp/acc-check-XXXX.log)
|
|
cd "$AENEAS_LEAN"
|
|
lake env bash -c "
|
|
set -euo pipefail
|
|
cd '$HERE/gen' && export LEAN_PATH=\"\$LEAN_PATH:\$PWD:$HERE\"
|
|
compile() {
|
|
echo \" · \$1\"
|
|
LEAN_TIMEOUT=$TIMEOUT LEAN_MAX_CORES=$CORES '$HERE/lean-guard' \"\${1}.lean\" 2>&1 | tee -a '$LOG' || { echo \"FAIL: \$1\"; exit 1; }
|
|
}
|
|
for m in ${GEN_MODULES[*]}; do compile \"\$m\"; done
|
|
cd '$HERE'
|
|
for m in ${PROOFS[*]}; do
|
|
[ -f \"Proofs/\$m.lean\" ] || { echo \"MISSING: Proofs/\$m.lean\"; exit 1; }
|
|
compile \"Proofs/\$m\"
|
|
done
|
|
for f in Proofs/*.lean; do
|
|
b=\$(basename \"\$f\" .lean)
|
|
[ \"\$b\" = AxiomCheck ] && continue
|
|
case \" ${PROOFS[*]} \" in (*\" \$b \"*) ;; (*) echo \"DEAD FILE: \$f\"; exit 1;; esac
|
|
done
|
|
"
|
|
if grep -q "uses 'sorry'" "$LOG"; then echo "STUB: sorry detected"; exit 1; fi
|
|
rm -f "$LOG"
|
|
|
|
echo "=== Phase 3: boundary-exact axiom audit ==="
|
|
AUD=$(mktemp /tmp/acc-audit-XXXX.log)
|
|
cd "$AENEAS_LEAN"
|
|
lake env bash -c "
|
|
cd '$HERE' && export LEAN_PATH=\"\$LEAN_PATH:$HERE/gen:$HERE\"
|
|
LEAN_TIMEOUT=300 LEAN_MAX_CORES=$CORES '$HERE/lean-guard' Proofs/AxiomCheck.lean
|
|
" > "$AUD" 2>&1 || { cat "$AUD"; exit 1; }
|
|
FAIL=0
|
|
for cert in "${!CONES[@]}"; do
|
|
want="${CONES[$cert]}"
|
|
if [ -z "$want" ]; then
|
|
exp="'$cert' does not depend on any axioms"
|
|
else
|
|
exp="'$cert' depends on axioms: [$want]"
|
|
fi
|
|
if ! grep -qF "$exp" "$AUD"; then
|
|
echo " CONE DRIFT: $cert"
|
|
echo " expected: $exp"
|
|
echo " observed: $(grep -F "'$cert'" "$AUD" || echo '(missing)')"
|
|
FAIL=1
|
|
else
|
|
echo " ✓ $cert [$want]"
|
|
fi
|
|
done
|
|
rm -f "$AUD"
|
|
|
|
# -- Phase 3b: audit-surface COVERAGE (fail-closed; review H1) --------------
|
|
echo "=== Phase 3b: audit-surface coverage ==="
|
|
COVFAIL=0
|
|
DECLS=$(grep -hoE "^(theorem|noncomputable def|def|abbrev) [A-Za-z0-9_?]+" \
|
|
"$HERE"/Proofs/*.lean "$HERE"/gen/LTLAcc/*.lean | awk '{print $NF}' | sort -u)
|
|
for d in $DECLS; do
|
|
if [ -n "${CONES[LTLAcc.$d]+x}" ] || [ -n "${EXCLUDE[$d]+x}" ]; then :; else
|
|
echo " UNCLASSIFIED DECLARATION: $d (not in CONES, not a sanctioned exclusion)"; COVFAIL=1
|
|
fi
|
|
done
|
|
# anonymous instances live only in gen/ (a controlled file); pin their count
|
|
GENINST=$(grep -cE "^instance" "$HERE"/gen/LTLAcc/*.lean)
|
|
CONEINST=$(printf '%s\n' "${!CONES[@]}" | grep -cE "LTLAcc\.inst")
|
|
if [ "$GENINST" != "$CONEINST" ]; then
|
|
echo " INSTANCE COUNT DRIFT: gen has $GENINST instances, CONES pins $CONEINST"; COVFAIL=1
|
|
fi
|
|
# every pinned cert must actually be queried by AxiomCheck (no pin-but-never-check)
|
|
for cert in "${!CONES[@]}"; do
|
|
grep -qF "#print axioms $cert" "$HERE/Proofs/AxiomCheck.lean" || {
|
|
echo " PINNED BUT NOT QUERIED: $cert (in CONES, absent from AxiomCheck.lean)"; COVFAIL=1; }
|
|
done
|
|
[ "$COVFAIL" = 0 ] && echo " coverage complete: every declaration classified (audited or sanctioned-excluded)"
|
|
[ "$COVFAIL" = 0 ] || { echo "COVERAGE FAILED"; FAIL=1; }
|
|
[ "$FAIL" = 0 ] || exit 1
|
|
# -- Phase 4: definition fidelity (Lean defs vs deployed pacta verifiers) --
|
|
echo "=== Phase 4: definition fidelity ==="
|
|
PACTA_SRC="${PACTA_SRC:-$HERE/../../proof-aware-crypto-tooling-agent/src}"
|
|
FIDELITY_RAN=0
|
|
if [ "${SKIP_FIDELITY:-0}" = "1" ]; then
|
|
echo " skipped (SKIP_FIDELITY=1)"
|
|
elif [ -d "$PACTA_SRC/pacta" ]; then
|
|
PACTA_SRC="$PACTA_SRC" python3 "$HERE/fidelity/run_fidelity.py" || { echo "FIDELITY FAILED"; exit 1; }
|
|
FIDELITY_RAN=1
|
|
else
|
|
echo " SKIPPED: pacta repo not found at $PACTA_SRC (set PACTA_SRC to run)"
|
|
fi
|
|
|
|
# Fail-closed markers (review H2): the Lean corpus is green either way, but
|
|
# only the strong marker — required by the attestation gate — is emitted
|
|
# when fidelity actually ran. Never conflate the two.
|
|
echo "=== LEAN GREEN ==="
|
|
if [ "$FIDELITY_RAN" = 1 ]; then
|
|
echo "=== ATTESTATION GREEN (Lean + fidelity) ==="
|
|
else
|
|
echo "=== FIDELITY NOT RUN — NOT attestation-ready (run with pacta present) ==="
|
|
fi
|