ADR-048: Verify-on-Ingest — Kernel-Verify Each Proof Once, Then Trust Immutability

Field Value
Decision ID ADR-048
Initiative unsorry — Gate A soundness model / performance
Proposed By unsorry maintainers
Date 2026-06-15
Status Accepted

Context

Gate A’s implicit model has been re-verify in CI: the full kernel replay (leanchecker) and axiom audit re-run over large module sets — on every push to main (full backstop) and, until now, on every archive-block PR (which re-replayed the archived proofs). On the available 16 GB heavy Namespace runner and constrained routine Namespace runner this is the dominant cost and the source of the exit-137 OOM class: each archive package is a separate Lake project whose leanchecker loads its own full mathlib image (>8 GB resident), so re-replaying a block OOM-killed even a 16 GB runner regardless of chunk size (#764). But the same proofs validate fine when active, because their kernel replay runs once on the larger push-to-main runner and PR-time replay is incremental and small.

The key observation: a proof module only needs a kernel replay when its trusted context changes — the proof file, its goal statement, its imports/dependencies, the Lean/mathlib/lake configuration, or the Gate A checker logic. If none of those changed, re-running leanchecker on the same immutable proof re-proves nothing. Archiving a proof is a file move + metadata change; it changes none of the above.

WH(Y) Decision Statement

In the context of a Gate A that re-verifies large proof sets in CI — re-replaying archived blocks and full-replaying on every push — at a cost that OOMs memory-bound runners and forces a 16 GB profile just to re-check already-verified, immutable proofs,

facing the fact that this re-verification is redundant whenever the proof artifact and its verification context are unchanged, and that the redundancy is precisely what blocks downscaling CI to the routine namespace profile,

we decided for a verify-on-ingest model: a proof is kernel-verified exactly once, by the trusted CI pipeline, at the PR that introduces it (the normal incremental Gate A: build, statement-binding, axiom audit, leanchecker replay). After it merges it is verified for that toolchain/mathlib context. Thereafter the system trusts the immutable artifact rather than re-deriving soundness: an archive move validates provenance + byte-identity (the archived file is byte-for-byte the already-verified active proof, ADR-018) plus packaging sanity (lake build --wfail), and does not re-run leanchecker,

and explicitly NOT “trust because the proving agent says it checked” — trust is “CI verified this exact artifact and the artifact is immutable since”, never a prover’s self-attestation,

and neglected making archive validation heavier / finding it a bigger runner (#838 — a tactical unblocker that re-checks what’s already verified), because verify-on-ingest matches what archives are (frozen, already-verified blocks),

to achieve removal of the archive-OOM class entirely, archive PRs that are provenance + packaging validation (cheap, fits the routine namespace profile), and — with the rollout below — downscaling routine CI from 16 GB to the routine profile and a faster pipeline,

accepting that the residual risk shifts from Lean soundness to bookkeeping soundness: the system must reliably know an archived proof is exactly the one CI verified (guardrails below), that a context change must still force re-verification, and that a scheduled full replay remains as defense-in-depth.

Policy

  1. Active proof PRs pass normal Gate A: build, statement-binding, axiom audit, kernel replay (incremental — only the changed modules + their reverse-import closure, ADR-033).
  2. After merge, the proof is verified for that toolchain/mathlib context.
  3. Archive moves validate that the archived artifact is byte-identical to an already-verified version — proof modules (library/Unsorry/*.lean) via the archive_packages.archive_proof_provenance check (git-blob identity of each archived proof module against the base active module or the base archived copy), and goal statements via ADR-018 archive-aware immutability — plus lake build --wfail for packaging sanity. No leanchecker replay.
  4. Re-verification (kernel replay) re-runs only when the trusted context changes: the proof file, its goal statement, its imports/deps, lean-toolchain/lakefile/lake-manifest.json, or the Gate A checker (tools/gate_a/**). These are exactly the ADR-033 full-replay triggers.
  5. Full replay becomes scheduled or context-triggered, not a per-push / per-archive-PR step.

Guardrails (bookkeeping soundness)

Rollout

Soundness

Lean soundness is unchanged: every proof is still kernel-replayed by the trusted pipeline once, under the toolchain/mathlib it is verified against, and a context change re-triggers verification. What changes is that we stop re-deriving soundness for unchanged, immutable artifacts and instead carry it forward via provenance. The trust boundary is “CI verified this exact artifact + immutability,” not the prover. Defense-in-depth (scheduled full replay; context-change triggers; byte-identity enforcement) covers the bookkeeping risk.

References

Reference ID Title Type Location
REF-1 Verify-on-ingest provenance spec (Phase 1) Specification specs/SPEC-048-A-Verify-On-Ingest.md
REF-1b Incremental push + scheduled backstop + routine runner sizing (Phase 2) Specification specs/SPEC-048-B-Verify-On-Ingest-Phase2.md
REF-2 Archive-aware immutability / byte-identity Decision ADR-018-Goal-Statement-Immutability.md
REF-3 Incremental kernel replay + full-replay triggers Decision ADR-033-Incremental-Kernel-Replay.md
REF-4 Proof archive blocks Decision ADR-041-Proof-Archive-Blocks.md
REF-5 Gate A soundness enforcement (re-verify stance amended) Decision ADR-006-Gate-A-Soundness-Enforcement.md

Status History

Status Approver Date
Proposed unsorry maintainers 2026-06-15
Accepted unsorry maintainers 2026-06-15
Phase 2 implemented (SPEC-048-B) unsorry maintainers 2026-06-15