feat(abi): prove hash-chain integrity, version ordering & lineage acyclicity (Idris2)#173
Merged
Merged
Conversation
…clicity Completes the ROADMAP Phase-1 "Idris2 ABI proofs" line. #168 proved sidecar isolation (Octad.idr); this adds the remaining three, each a constructive, total, hole-free module that `idris2 --build` (provable.yml's idris2-proofs job) machine-checks — zero believe_me / postulate / assert_total / holes. - HashChain.idr — provenance hash chain. Integrity by construction (`ProvChain` only extends onto its actual current tip); the runtime `replay` verifier provably accepts a correctly-linked entry (`replayOne`) and rejects a forged predecessor (`replayReject`); no link can pose as genesis (`linkHashNeverGenesis`); end-to-end intact/tampered controls. - Version.idr — temporal versions. Strict monotonicity by construction (`History` indexed by a strict lower bound => no skew, no duplicate versions); `asOf` point-in-time query + `ascending` check with concrete pos/neg controls. - Lineage.idr — lineage DAG. Every derivation strictly increases the topological index, so any path does too (`lineageIncreases`) => acyclicity (`noCycle`, `noSelfLoop`); LTE order lemmas proved from constructors (no stdlib axioms). Verified locally with idris2 0.8.0 (the estate's pinned toolchain, via echidna/scripts/install-proof-toolchains.sh): full package builds clean, and an adversarial pass confirms the type-checker REJECTS false claims (Origin at a non-zero tip, a tampered chain replaying as valid, a self-loop edge). ROADMAP Phase-1 proof line ticked; live-DB enforcement of these invariants remains TODO (a separate, larger systems change). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdqVWGSSv36Ph8ZWvizGMp
10 tasks
hyperpolymath
added a commit
that referenced
this pull request
Jun 27, 2026
…#174) ## Summary Clears the one pre-existing red on the estate's `governance / Language / package anti-pattern policy` check. `scripts/abi-ffi-gate.py` was the repo's **last Python file** (RSR-H4 bans Python — Julia for scripts/data, Rust for systems), and the whole-tree scanner flags it. Rather than suppress the scanner, this **ports the gate to Julia** and removes the `.py`, so the check goes green *and* the ABI↔FFI conformance gate keeps doing its job. (Follow-up to #173 — this is the cleanup of the lone pre-existing failure carried on #172/#173.) ## Changes - **`scripts/abi-ffi-gate.jl`** (new) — a 1:1 Julia port of the former Python gate (Julia PCRE maps directly onto Python `re`). Same three checks, same messages, same exit codes: 1. the Zig FFI carries no unrendered `{{...}}` template tokens; 2. every `%foreign "C:<name>"` symbol in the ABI `.idr` sources is `export fn`-ed by the Zig FFI; 3. the Idris `resultToInt` map and the Zig `Result = enum(c_int)` agree on **names and values** (`Error`/`err` unified). - **`.github/workflows/abi-ffi-gate.yml`** — the `ABI ↔ FFI structural conformance` job now installs the pinned **Julia 1.11.5** release tarball (mirroring the adjacent `zig-build` job's pinned-tarball pattern) and runs `julia scripts/abi-ffi-gate.jl`. - **`scripts/abi-ffi-gate.py`** (removed) — the last Python file in the repo. ## RSR Quality Checklist ### Required - [x] Tests pass — gate runs clean locally (julia 1.11.5); `cargo`/Zig/Idris surfaces untouched - [x] Code is formatted — idiomatic Julia - [x] Linter is clean - [x] **No banned language patterns — this PR *removes* the last Python file** (RSR-H4) - [x] No `unsafe` blocks without `// SAFETY:` — n/a - [x] No banned functions - [x] SPDX license header present on `abi-ffi-gate.jl` (MPL-2.0) - [x] No secrets, credentials, or `.env` files ### As Applicable - [x] ABI/FFI changes validated — the conformance gate's behaviour is preserved (verified below) - [ ] State files / CHANGELOG — no released-version change ## Testing Verified locally with **julia 1.11.5** against this tree: ``` # OK path is byte-identical to the Python it replaces: $ diff <(python3 abi-ffi-gate.py) <(julia abi-ffi-gate.jl) → identical ABI-FFI GATE: OK (verisimiser) — 24 ABI functions exported, 8 result codes match (exit 0) ``` Adversarial (the gate must **fail** on drift) — each returns exit 1 with the right diagnosis: | Injected drift | Result | |---|---| | `null_pointer = 4` → `9` in the Zig enum | ❌ "Result-code map differs (name or value)" | | remove an `export fn` | ❌ "1 ABI function(s) not exported by the Zig FFI" | | add a `{{PROJECT_NAME}}` token to the Zig | ❌ "Zig FFI has unrendered template tokens" | So the port is behaviour-equivalent **and** non-vacuous. ## Screenshots n/a (script + workflow) --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01JdqVWGSSv36Ph8ZWvizGMp)_ Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #167 / #168. The ROADMAP Phase-1 line "Idris2 ABI proofs: sidecar isolation, hash-chain integrity, version ordering, lineage acyclicity" was one box with four parts. #168 delivered sidecar isolation (
Octad.idr). This PR delivers the remaining three as machine-checked Idris2 theorems, so the whole line can be ticked.Each module is constructive,
%default total, and hole-free —idris2 --build(theidris2-proofsjob inprovable.yml) type-checks all 8 ABI modules with zerobelieve_me/postulate/assert_total/ holes. Verified locally with idris2 0.8.0, the estate's pinned toolchain (provisioned viaechidna/scripts/install-proof-toolchains.sh's recipe).Changes
src/interface/abi/Verisimiser/ABI/HashChain.idr— provenance hash chain (the Provenance octad dimension).ProvChainis integrity by construction: it can only be extended onto the chain's actual current tip, so a value is a proof every link is intact.replayverifier provably accepts a correctly-linked entry (replayOne) and rejects a forged predecessor (replayReject).linkHashNeverGenesis: no link hash can pose as the genesis record;contentChangesTip: the tip binds its payload. End-to-end intact/tampered controls.src/interface/abi/Verisimiser/ABI/Version.idr— temporal versioning (the Temporal octad dimension).Historyis indexed by a strict lower bound ⇒ strict monotonicity by construction (no version skew, no duplicate versions).asOfpoint-in-time query +ascendingvalidity check, each pinned by concrete positive and negative controls.src/interface/abi/Verisimiser/ABI/Lineage.idr— lineage DAG (the Lineage octad dimension, ADR-0005).lineageIncreases) ⇒ acyclicity:noCycle,noSelfLoop.LTEorder lemmas are proved from constructors (no stdlib axioms).verisimiser-abi.ipkg— registers the three new modules (so CI type-checks them).ROADMAP.adoc— ticks the Phase-1 Idris2-ABI-proofs line; notes the*.idrare abstract models of the invariants and that live-DB enforcement of them remains TODO.RSR Quality Checklist
Required
idris2 --build verisimiser-abi.ipkgbuilds all 8 modules clean (idris2 0.8.0);cargo/Zig surface untouchedOctad.idr/Proofs.idridiomunsafeblocks without// SAFETY:— n/abelieve_me/postulate/assert_total/assert_smaller/ holes (grep-verified).envfilesAs Applicable
src/interface/abi/type-checks; no FFI surface change (these are proof-only modules)ROADMAP.adocPhase-1 proof lineTesting
Non-vacuity (adversarial): a throwaway module asserting false statements is rejected by the type-checker —
Origin : ProvChain 5→ Mismatch 0/5; a tampered chain claimed valid → Mismatch Just 18 / Nothing;DerivedFrom 4 4→ Mismatch 0/4. So the theorems have teeth, mirroring #168's adversarial controls.Screenshots
n/a (Idris2 source + proofs)
🤖 Generated with Claude Code
Generated by Claude Code