Skip to content

Commit 1d09ef6

Browse files
hyperpolymathclaude
andcommitted
proof: items 7+8 — design pass + first concrete agreement witnesses
Refactors VerifierAccepts / SourceAccepts from single-constructor opaque predicates (statement-level only per A13) into inductive types with two cases each: * VAStructural / SAStructural — backed by a FunctionsAccepted witness that mirrors the spec's L7+L10 acceptance, no trust-injection. * VADifferential / SADifferential — external evidence (fixture name + id) pinned to a row in tests/cross_compat.rs or the source-side harness. Searching for these constructors enumerates every trust-injection site. SpecAccepts is refactored to wrap the same FunctionsAccepted predicate, so spec / verifier / source-checker share one canonical structural witness at the L7+L10 layer. Adds the first concrete, total, no-believe_me agreement value in the codebase between the three acceptance predicates: * functionsAcceptedImpliesSpec / specImpliesFunctionsAccepted * specImpliesVerifierStructural / specImpliesSourceStructural * verifierImpliesSpecStructural / sourceImpliesSpecStructural (return Maybe — VADifferential / SADifferential case stays Nothing because it is the multi-week differential-trust obligation) * StructuralAgreement record + concrete structuralAgreement value closing the structural sublattice of items 7 and 8 Plus concrete inhabitants showing the predicates aren't vacuously empty: emptyFunctionsAccepted, emptyModuleSpecAccepts, emptyModuleVerifierAccepts, emptyModuleSourceAccepts. The full VerifierSpecAgreement / SourceVerifierAgreement records are unchanged — they remain obligations for the differential case (no structural witness available from a fixture string + id alone without external trust). This commit advances those records from "statement-only" to "structural sublattice fully proven; differential case explicitly stratified out". Idris2 0.8.0 build green: 22/22 modules. Zero new believe_me, assert_total, postulate, sorry, assert_smaller. %default total preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b585068 commit 1d09ef6

1 file changed

Lines changed: 216 additions & 34 deletions

File tree

src/abi/TypedWasm/ABI/VerifierSpec.idr

Lines changed: 216 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -129,69 +129,103 @@ data IntentsLinearAcceptable : List OwnershipIntent -> Type where
129129
IntentsLinearAcceptable rest
130130
-> IntentsLinearAcceptable (BorrowsExclusive s :: rest)
131131

132-
||| `SpecAccepts m` — the Idris2 spec accepts a module summary. Built
133-
||| from `IntentsLinearAcceptable` on every function's intent list.
134-
||| Stated as a `data` with one constructor so future extensions
135-
||| (L13 cross-module checks, L14 session-state) can be added as new
136-
||| constructors without breaking existing witnesses.
132+
||| Per-function structural acceptance lifted across the function list,
133+
||| matching how the verifier walks the module's export section. Used
134+
||| by `SpecAccepts`, `VerifierAccepts`, and `SourceAccepts` so all
135+
||| three agree on the structural witness shape at L7+L10.
136+
public export
137+
data FunctionsAccepted : List FunctionSummary -> Type where
138+
FANil : FunctionsAccepted []
139+
FACons : IntentsLinearAcceptable f.intents
140+
-> FunctionsAccepted rest
141+
-> FunctionsAccepted (f :: rest)
142+
143+
||| `SpecAccepts m` — the Idris2 spec accepts a module summary. Wraps
144+
||| `FunctionsAccepted` on the function list so spec / verifier /
145+
||| source-checker share a single canonical structural witness at the
146+
||| L7+L10 layer. Future extensions (L13 cross-module checks, L14
147+
||| session-state) add new constructors without breaking existing
148+
||| structural witnesses.
137149
public export
138150
data SpecAccepts : ModuleSummary -> Type where
139151
MkSpecAccepts :
140-
(perFunction : (f : FunctionSummary)
141-
-> Elem f m.functions
142-
-> IntentsLinearAcceptable f.intents)
152+
FunctionsAccepted m.functions
143153
-> SpecAccepts m
144154

145155
-- ============================================================================
146-
-- Verifier acceptance (opaquepinned by the differential harness)
156+
-- Verifier acceptance (inductivestructural + differential cases)
147157
-- ============================================================================
148158
--
149-
-- The Rust verifier's acceptance set is not directly representable in
150-
-- Idris2 (it consumes raw wasm bytes via wasmparser). We model it as
151-
-- an OPAQUE predicate `VerifierAccepts m` indexed by the same
152-
-- `ModuleSummary` shape; the differential testing harness in
153-
-- `tests/cross_compat.rs` is what pins down whether the predicate
154-
-- holds for a given module.
159+
-- The Rust verifier's acceptance set has two flavours of evidence:
155160
--
156-
-- The predicate's introduction rule is intentionally non-public: the
157-
-- only legitimate way to construct a `VerifierAccepts` witness is
158-
-- through `differentialAccepted` (below), which calls out the
159-
-- harness's role. This makes the data flow auditable in the proof:
160-
-- you can search for `MkVerifierAccepts` and find exactly the places
161-
-- where the trust is being injected.
162-
163-
||| Opaque verifier-acceptance predicate. Constructible only by
164-
||| `differentialAccepted` (statement-level promise; no `Refl` body
165-
||| could ever justify it without the harness's external evidence).
161+
-- 1. STRUCTURAL — for modules whose ownership intents are visible at
162+
-- the typed surface, the verifier's L7+L10 acceptance is exactly
163+
-- the same predicate the spec uses. No trust-injection needed.
164+
-- 2. DIFFERENTIAL — for modules whose typed surface is partial (the
165+
-- Rust verifier inspects wasm bytes via wasmparser), the only
166+
-- legitimate way to assert verifier-acceptance is through a row
167+
-- in the differential testing table (`tests/cross_compat.rs`).
168+
--
169+
-- The earlier opaque design (A13) collapsed both cases into a single
170+
-- "external evidence" constructor. That made every agreement
171+
-- direction unprovable (no introspection into the witness). The
172+
-- inductive split below restores provability of the structural cases
173+
-- while keeping the differential case auditable.
174+
175+
||| Inductive verifier-acceptance predicate, indexed by `ModuleSummary`.
176+
||| Two constructors expose the trust shape:
177+
|||
178+
||| * `VAStructural` — verifier acceptance derived from the same
179+
||| structural predicate the spec uses. No external trust;
180+
||| introspectable witness.
181+
||| * `VADifferential` — verifier acceptance via the differential
182+
||| harness. External trust pinned to a fixture row. Searching
183+
||| for this constructor enumerates every trust-injection site.
166184
public export
167185
data VerifierAccepts : ModuleSummary -> Type where
168-
MkVerifierAccepts :
186+
||| Structural verifier acceptance: every exported function's
187+
||| intent list passes the L10 single-consumption / L7 aliasing check.
188+
||| This is the case the spec and the verifier agree on by definition.
189+
VAStructural :
190+
FunctionsAccepted m.functions
191+
-> VerifierAccepts m
192+
||| Differential verifier acceptance: external evidence from
193+
||| `tests/cross_compat.rs` pinning a fixture row. Constructible
194+
||| only via `differentialAccepted` so the trust boundary is
195+
||| inspectable.
196+
VADifferential :
169197
(differentialEvidence : String)
170198
-> (fixtureId : Nat)
171199
-> VerifierAccepts m
172200

173201
||| Construct a `VerifierAccepts` witness from differential-harness
174202
||| evidence (fixture name + numeric id from `tests/cross_compat.rs`).
175203
||| Naming the fixture in the witness makes the trust boundary
176-
||| inspectable: every `VerifierAccepts` use can be traced back to a
204+
||| inspectable: every `VADifferential` use can be traced back to a
177205
||| concrete row in the differential table.
178206
public export
179207
differentialAccepted : (fixtureName : String) -> (fixtureId : Nat)
180208
-> VerifierAccepts m
181-
differentialAccepted name fid = MkVerifierAccepts name fid
209+
differentialAccepted name fid = VADifferential name fid
182210

183211
-- ============================================================================
184212
-- Source-checker acceptance (item 8 surface)
185213
-- ============================================================================
186214

187215
||| Source-checker acceptance predicate. Like `VerifierAccepts`, the
188-
||| source checker's acceptance set is determined by an external
189-
||| implementation (the AffineScript front-end at present, replaced by
190-
||| an Idris2 parser when Track A lands). The predicate is opaque and
191-
||| witnessed by the source-side test harness.
216+
||| source checker has both a structural surface (typed AST) and a
217+
||| differential side (cross-checked against fixtures when the source
218+
||| AST is partial). Same two-constructor shape, same audit story.
192219
public export
193220
data SourceAccepts : ModuleSummary -> Type where
194-
MkSourceAccepts :
221+
||| Structural source-checker acceptance: lifted from
222+
||| `FunctionsAccepted` exactly as the spec sees it.
223+
SAStructural :
224+
FunctionsAccepted m.functions
225+
-> SourceAccepts m
226+
||| Differential source-checker acceptance: external evidence from a
227+
||| source-side fixture row.
228+
SADifferential :
195229
(sourceEvidence : String)
196230
-> (fixtureId : Nat)
197231
-> SourceAccepts m
@@ -200,7 +234,7 @@ data SourceAccepts : ModuleSummary -> Type where
200234
public export
201235
sourceAccepted : (fixtureName : String) -> (fixtureId : Nat)
202236
-> SourceAccepts m
203-
sourceAccepted name fid = MkSourceAccepts name fid
237+
sourceAccepted name fid = SADifferential name fid
204238

205239
-- ============================================================================
206240
-- Agreement obligations — items 7 and 8
@@ -283,3 +317,151 @@ specImpliesSource :
283317
-> SourceAccepts m
284318
specImpliesSource vsa sva m specAcc =
285319
sva.verifierImpliesSource m (vsa.verifierIsComplete m specAcc)
320+
321+
-- ============================================================================
322+
-- Structural agreement — first concrete witnesses (items 7 + 8, partial)
323+
-- ============================================================================
324+
--
325+
-- The full `VerifierSpecAgreement` / `SourceVerifierAgreement` records
326+
-- above remain obligations because their generality covers both
327+
-- structural and differential evidence: the differential case requires
328+
-- an external connection (wasm-bytes semantics or fixture-row trust)
329+
-- and is multi-week.
330+
--
331+
-- What IS provable, total, no-trust-injection, is the agreement
332+
-- restricted to the structural cases. The records and lemmas below
333+
-- give those a concrete home. Future work plugs the differential
334+
-- cases in by extending these records (e.g. by lifting fixture
335+
-- evidence into a stratified-acceptance predicate).
336+
--
337+
-- Convention: every name in this section ends in `Structural` so the
338+
-- restriction to the structural sublattice is visible at call sites.
339+
340+
||| `FunctionsAccepted` directly witnesses `SpecAccepts`. This is the
341+
||| spec's structural inhabitant: any function-list witness gives a
342+
||| spec acceptance witness for the enclosing module.
343+
public export
344+
functionsAcceptedImpliesSpec :
345+
{m : ModuleSummary}
346+
-> FunctionsAccepted m.functions
347+
-> SpecAccepts m
348+
functionsAcceptedImpliesSpec fa = MkSpecAccepts fa
349+
350+
||| Inverse direction: a spec acceptance witness contains the
351+
||| structural per-function witness.
352+
public export
353+
specImpliesFunctionsAccepted :
354+
{m : ModuleSummary}
355+
-> SpecAccepts m
356+
-> FunctionsAccepted m.functions
357+
specImpliesFunctionsAccepted (MkSpecAccepts fa) = fa
358+
359+
||| Spec → verifier (structural case). Lifts spec acceptance directly
360+
||| into `VAStructural` — no trust-injection.
361+
public export
362+
specImpliesVerifierStructural :
363+
{m : ModuleSummary}
364+
-> SpecAccepts m
365+
-> VerifierAccepts m
366+
specImpliesVerifierStructural (MkSpecAccepts fa) = VAStructural fa
367+
368+
||| Spec → source (structural case). Symmetric to the verifier
369+
||| direction; lifts spec acceptance into `SAStructural`.
370+
public export
371+
specImpliesSourceStructural :
372+
{m : ModuleSummary}
373+
-> SpecAccepts m
374+
-> SourceAccepts m
375+
specImpliesSourceStructural (MkSpecAccepts fa) = SAStructural fa
376+
377+
||| Verifier → spec (structural case only). Defined on `VAStructural`
378+
||| witnesses; the `VADifferential` case is the multi-week obligation
379+
||| and is therefore reflected as a `Maybe` here so totality is
380+
||| preserved without `believe_me`.
381+
public export
382+
verifierImpliesSpecStructural :
383+
{m : ModuleSummary}
384+
-> VerifierAccepts m
385+
-> Maybe (SpecAccepts m)
386+
verifierImpliesSpecStructural (VAStructural fa) = Just (MkSpecAccepts fa)
387+
verifierImpliesSpecStructural (VADifferential _ _) = Nothing
388+
389+
||| Source → spec (structural case only). Mirrors
390+
||| `verifierImpliesSpecStructural` for the source side.
391+
public export
392+
sourceImpliesSpecStructural :
393+
{m : ModuleSummary}
394+
-> SourceAccepts m
395+
-> Maybe (SpecAccepts m)
396+
sourceImpliesSpecStructural (SAStructural fa) = Just (MkSpecAccepts fa)
397+
sourceImpliesSpecStructural (SADifferential _ _) = Nothing
398+
399+
||| Bundle of structural-case agreement directions. Differs from
400+
||| `VerifierSpecAgreement` / `SourceVerifierAgreement` in three ways:
401+
|||
402+
||| 1. Restricted to the structural sublattice — `VADifferential`
403+
||| and `SADifferential` are NOT covered.
404+
||| 2. Provable as a total Idris2 value (`structuralAgreement` below).
405+
||| 3. Symmetric across all three predicates simultaneously.
406+
|||
407+
||| This is the first concrete agreement value in the codebase that
408+
||| relates the spec / verifier / source-checker acceptance predicates
409+
||| without invoking external evidence.
410+
public export
411+
record StructuralAgreement where
412+
constructor MkStructuralAgreement
413+
saSpecToVerifier :
414+
(m : ModuleSummary) -> SpecAccepts m -> VerifierAccepts m
415+
saSpecToSource :
416+
(m : ModuleSummary) -> SpecAccepts m -> SourceAccepts m
417+
saVerifierStructuralToSpec :
418+
(m : ModuleSummary) -> FunctionsAccepted m.functions -> SpecAccepts m
419+
saSourceStructuralToSpec :
420+
(m : ModuleSummary) -> FunctionsAccepted m.functions -> SpecAccepts m
421+
422+
||| Concrete witness for `StructuralAgreement`. Total. No
423+
||| `believe_me`, no `postulate`, no external trust. Closes the
424+
||| structural-case portion of items 7 and 8 from the post-A10 audit.
425+
public export
426+
structuralAgreement : StructuralAgreement
427+
structuralAgreement = MkStructuralAgreement
428+
(\m, sa => specImpliesVerifierStructural sa)
429+
(\m, sa => specImpliesSourceStructural sa)
430+
(\m, fa => functionsAcceptedImpliesSpec fa)
431+
(\m, fa => functionsAcceptedImpliesSpec fa)
432+
433+
-- ============================================================================
434+
-- Concrete instance proofs — empty module
435+
-- ============================================================================
436+
--
437+
-- The empty-module case is the first concrete `SpecAccepts`
438+
-- inhabitant: no functions, so the per-function obligation is
439+
-- trivially satisfied. Used by the regression test as a smoke check
440+
-- that the predicates aren't vacuously unprovable.
441+
442+
||| Structural witness for an empty function list. Closes the L7+L10
443+
||| obligations vacuously.
444+
public export
445+
emptyFunctionsAccepted : FunctionsAccepted []
446+
emptyFunctionsAccepted = FANil
447+
448+
||| The spec accepts every module whose function list is empty.
449+
||| Concrete inhabitant of `SpecAccepts`. Demonstrates the predicate
450+
||| is not vacuously empty.
451+
public export
452+
emptyModuleSpecAccepts :
453+
(n : String) -> SpecAccepts (MkModuleSummary n [])
454+
emptyModuleSpecAccepts n = MkSpecAccepts FANil
455+
456+
||| The verifier accepts every empty-module summary via the structural
457+
||| constructor (no differential evidence needed).
458+
public export
459+
emptyModuleVerifierAccepts :
460+
(n : String) -> VerifierAccepts (MkModuleSummary n [])
461+
emptyModuleVerifierAccepts n = VAStructural FANil
462+
463+
||| The source checker accepts every empty-module summary.
464+
public export
465+
emptyModuleSourceAccepts :
466+
(n : String) -> SourceAccepts (MkModuleSummary n [])
467+
emptyModuleSourceAccepts n = SAStructural FANil

0 commit comments

Comments
 (0)