Skip to content

Commit cf581a3

Browse files
hyperpolymathclaude
andcommitted
feat(abi): register AxiomTracker + Gnn in ipkg; fix Idris2 proofs
EchidnaABI.AxiomTracker and EchidnaABI.Gnn were present on disk but absent from echidnaabi.ipkg so they were never type-checked by the Idris2 build. AxiomTracker.idr also had two proof bugs that would surface on compile: 1. anyRejectIsTrue (There branch): returning the IH directly fails because any (== Reject) (x :: xs) ≠ any (== Reject) xs in general. Fix: case-split on the head constructor so `x == Reject` reduces definitionally before applying `rewrite ih in Refl`. 2. precision / noRejectMeansAnyFalse: `rewrite rest in Refl` left an unprovable `x == Reject = False` residual for generic x; and `rewrite anyFalse in absurd prf` rewrote in Void (unchanged goal). Fix: case-split constructors in noRejectMeansAnyFalse so `False || ...` reduces definitionally; restructure precision to case on `any (== Reject) usages` so prf becomes a constructor mismatch. Also adds Eq DangerLevel and DecidableEq DangerLevel (required by `any (== Reject)` in enforcePolicySpec and `decEq` in the proof helpers). Zero believe_me; all proofs constructive (Refl, void, case-split). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5a708f5 commit cf581a3

2 files changed

Lines changed: 67 additions & 15 deletions

File tree

src/abi/EchidnaABI/AxiomTracker.idr

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,33 @@ data DangerLevel
3535
| Warning -- incomplete proof marker (sorry, Admitted); warning issued
3636
| Reject -- known unsound construct; proof REJECTED
3737

38+
public export
39+
Eq DangerLevel where
40+
Safe == Safe = True
41+
Noted == Noted = True
42+
Warning == Warning = True
43+
Reject == Reject = True
44+
_ == _ = False
45+
46+
public export
47+
DecidableEq DangerLevel where
48+
decEq Safe Safe = Yes Refl
49+
decEq Noted Noted = Yes Refl
50+
decEq Warning Warning = Yes Refl
51+
decEq Reject Reject = Yes Refl
52+
decEq Safe Noted = No (\case Refl impossible)
53+
decEq Safe Warning = No (\case Refl impossible)
54+
decEq Safe Reject = No (\case Refl impossible)
55+
decEq Noted Safe = No (\case Refl impossible)
56+
decEq Noted Warning = No (\case Refl impossible)
57+
decEq Noted Reject = No (\case Refl impossible)
58+
decEq Warning Safe = No (\case Refl impossible)
59+
decEq Warning Noted = No (\case Refl impossible)
60+
decEq Warning Reject = No (\case Refl impossible)
61+
decEq Reject Safe = No (\case Refl impossible)
62+
decEq Reject Noted = No (\case Refl impossible)
63+
decEq Reject Warning = No (\case Refl impossible)
64+
3865
-- C-ABI wire encoding (u8 discriminant, matches Rust PartialOrd order)
3966
public export
4067
dangerToU8 : DangerLevel -> Bits8
@@ -114,16 +141,25 @@ soundness
114141
-> Elem Reject usages
115142
-> enforcePolicySpec usages = PolicyRejected
116143
soundness usages prf =
117-
-- 'any (== Reject) usages' is True whenever Reject is an element
118144
rewrite anyRejectIsTrue usages prf in Refl
119145
where
146+
-- Prove any (== Reject) us = True given Reject ∈ us.
147+
-- We case-split on the head constructor so that 'x == Reject' reduces
148+
-- definitionally before applying the induction hypothesis via rewrite.
120149
anyRejectIsTrue
121150
: (us : List DangerLevel)
122151
-> Elem Reject us
123152
-> any (== Reject) us = True
124-
anyRejectIsTrue (Reject :: _) Here = Refl
125-
anyRejectIsTrue (_ :: rest) (There p) = anyRejectIsTrue rest p
126-
anyRejectIsTrue [] p = absurd p
153+
anyRejectIsTrue [] p = absurd p
154+
anyRejectIsTrue (Reject :: _) Here = Refl
155+
anyRejectIsTrue (Safe :: xs) (There p) =
156+
let ih := anyRejectIsTrue xs p in rewrite ih in Refl
157+
anyRejectIsTrue (Noted :: xs) (There p) =
158+
let ih := anyRejectIsTrue xs p in rewrite ih in Refl
159+
anyRejectIsTrue (Warning :: xs) (There p) =
160+
let ih := anyRejectIsTrue xs p in rewrite ih in Refl
161+
anyRejectIsTrue (Reject :: xs) (There p) =
162+
let ih := anyRejectIsTrue xs p in rewrite ih in Refl
127163

128164
-- ────────────────────────────────────────────────────────────────────────────
129165
-- Invariant 2 (Precision): no Reject-in → not PolicyRejected-out
@@ -136,22 +172,36 @@ precision
136172
-> ((e : DangerLevel) -> Elem e usages -> Not (e = Reject))
137173
-> Not (enforcePolicySpec usages = PolicyRejected)
138174
precision usages noReject prf =
139-
-- If enforcePolicySpec returns PolicyRejected, then 'any (== Reject) usages'
140-
-- must have been True, so there is a Reject in the list — contradicting noReject.
141-
let anyFalse = noRejectMeansAnyFalse usages noReject
142-
in rewrite anyFalse in absurd prf
175+
-- Derive any (== Reject) usages = False from the no-Reject hypothesis.
176+
-- Then case-split on all three 'any' booleans so enforcePolicySpec usages
177+
-- reduces definitionally in each branch; each non-Rejected result gives a
178+
-- constructor mismatch with prf : ... = PolicyRejected.
179+
let anyFalse := noRejectMeansAnyFalse usages noReject
180+
in case any (== Reject) usages of
181+
True => absurd anyFalse -- anyFalse : True = False
182+
False =>
183+
case any (== Warning) usages of
184+
True => case prf of { Refl impossible } -- PolicyIncomplete ≠ PolicyRejected
185+
False =>
186+
case any (== Noted) usages of
187+
True => case prf of { Refl impossible } -- PolicyClassical ≠ PolicyRejected
188+
False => case prf of { Refl impossible } -- PolicyClean ≠ PolicyRejected
143189
where
190+
-- Case-split on each constructor so 'x == Reject' reduces definitionally,
191+
-- making 'False || any (== Reject) xs = any (== Reject) xs' automatic.
144192
noRejectMeansAnyFalse
145193
: (us : List DangerLevel)
146194
-> ((e : DangerLevel) -> Elem e us -> Not (e = Reject))
147195
-> any (== Reject) us = False
148196
noRejectMeansAnyFalse [] _ = Refl
149-
noRejectMeansAnyFalse (x :: xs) noR =
150-
case decEq x Reject of
151-
Yes Refl => absurd (noR Reject Here Refl)
152-
No _ =>
153-
let rest = noRejectMeansAnyFalse xs (\e p => noR e (There p))
154-
in rewrite rest in Refl
197+
noRejectMeansAnyFalse (Safe :: xs) noR =
198+
noRejectMeansAnyFalse xs (\e p => noR e (There p))
199+
noRejectMeansAnyFalse (Noted :: xs) noR =
200+
noRejectMeansAnyFalse xs (\e p => noR e (There p))
201+
noRejectMeansAnyFalse (Warning :: xs) noR =
202+
noRejectMeansAnyFalse xs (\e p => noR e (There p))
203+
noRejectMeansAnyFalse (Reject :: xs) noR =
204+
void (noR Reject Here Refl)
155205

156206
-- ════════════════════════════════════════════════════════════════════════════
157207
-- ABI Layout declarations (for Zig/Rust bridge validation)

src/abi/echidnaabi.ipkg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ sourcedir = "."
1818
depends = base
1919
, contrib
2020

21-
modules = EchidnaABI.Types
21+
modules = EchidnaABI.AxiomTracker
22+
, EchidnaABI.Gnn
23+
, EchidnaABI.Types
2224
, EchidnaABI.Layout
2325
, EchidnaABI.Foreign
2426
, EchidnaABI.Provers

0 commit comments

Comments
 (0)