Add Lean disaster recovery specification and trace validation - #8241
Draft
Amaury Chamayou (achamayou) wants to merge 16 commits into
Draft
Add Lean disaster recovery specification and trace validation#8241Amaury Chamayou (achamayou) wants to merge 16 commits into
Amaury Chamayou (achamayou) wants to merge 16 commits into
Conversation
Add an executable Lean port of the Stateright model, a C++-aligned canonical model with refinement and temporal proofs, and a versioned implementation trace validator.\n\nAdd exhaustive bounded Rust/Lean graph comparison and CI coverage while retaining Stateright as the migration oracle.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Add commit-aware recovery protocol instrumentation, causal log merging, and terminal trace checks for quorum, failover, and multiple-timeout SNP scenarios. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Defer trace-enabled joiner restart until the committed receive and join effect have been emitted, while preserving the default immediate path. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Defer trace-enabled retry work until its local protocol phase is globally visible, preventing sends from preceding their committed transition event. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Summarize kernel-checked safety, progress, and refinement results, distinguish bounded executable properties, and link successful Lean and SNP validation runs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Replace generalized under-observation search with deterministic replay of complete instrumented traces, enforce exact retry send batches and terminal effects, and split format parsing from replay. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Copilot started reviewing on behalf of
Amaury Chamayou (achamayou)
August 30, 2026 18:57
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Adds Lean disaster-recovery specifications, bounded Stateright equivalence checks, and validation of committed C++ recovery traces.
Changes:
- Adds legacy and canonical Lean models with refinement and progress proofs.
- Adds deterministic trace instrumentation, extraction, replay, and fixtures.
- Integrates equivalence and SNP trace validation into CI.
Custom instructions used
.github/copilot-instructions.md.github/instructions/reviewing.instructions.md.github/skills/testing/SKILL.md
Reviewed changes
Copilot reviewed 41 out of 41 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
tla/disaster-recovery/src/main.rs |
Adds graph-export CLI command. |
tla/disaster-recovery/src/export.rs |
Implements canonical graph export. |
tla/disaster-recovery/Readme.md |
Documents graph export. |
tests/infra/recovery_trace.py |
Extracts, orders, and validates traces. |
tests/infra/recovery_trace_test.py |
Tests trace processing. |
tests/e2e_operations.py |
Validates recovery scenarios. |
src/node/rpc/self_healing_open_handlers.h |
Records committed receive and timeout events. |
src/node/recovery_decision_protocol.h |
Declares tracing state and APIs. |
src/node/recovery_decision_protocol.cpp |
Implements trace emission and causal IDs. |
lean/disaster-recovery/TraceTests.lean |
Tests strict replay validation. |
lean/disaster-recovery/TraceMain.lean |
Adds trace-validator CLI. |
lean/disaster-recovery/TRACE_FORMAT_V1.md |
Defines the trace contract. |
lean/disaster-recovery/Tests.lean |
Tests legacy model semantics. |
lean/disaster-recovery/README.md |
Documents models, proofs, and migration. |
lean/disaster-recovery/Main.lean |
Adds model checker/exporter CLI. |
lean/disaster-recovery/lean-toolchain |
Pins Lean 4.28.0. |
lean/disaster-recovery/lakefile.toml |
Defines Lean targets. |
lean/disaster-recovery/lake-manifest.json |
Locks Lean dependencies. |
lean/disaster-recovery/fixtures/rejected.ndjson |
Adds invalid-state fixture. |
lean/disaster-recovery/fixtures/rejected-cause.ndjson |
Adds invalid-causality fixture. |
lean/disaster-recovery/fixtures/accepted.ndjson |
Adds quorum fixture. |
lean/disaster-recovery/fixtures/accepted-multinode.ndjson |
Adds multinode fixture. |
lean/disaster-recovery/fixtures/accepted-failover.ndjson |
Adds failover fixture. |
lean/disaster-recovery/DisasterRecovery/Protocol/Trace/Replay.lean |
Implements deterministic trace replay. |
lean/disaster-recovery/DisasterRecovery/Protocol/Trace/Format.lean |
Parses trace records. |
lean/disaster-recovery/DisasterRecovery/Protocol/Trace.lean |
Exports trace modules. |
lean/disaster-recovery/DisasterRecovery/Protocol/Temporal.lean |
Proves temporal properties. |
lean/disaster-recovery/DisasterRecovery/Protocol/Refinement.lean |
Proves phase refinement. |
lean/disaster-recovery/DisasterRecovery/Protocol/Model.lean |
Defines canonical C++-aligned model. |
lean/disaster-recovery/DisasterRecovery/Model.lean |
Ports the legacy model. |
lean/disaster-recovery/DisasterRecovery/Checker.lean |
Enumerates and checks graphs. |
lean/disaster-recovery/DisasterRecovery.lean |
Exports Lean modules. |
lean/disaster-recovery/compare.py |
Compares Rust and Lean graphs. |
lean/disaster-recovery/CanonicalTests.lean |
Tests canonical semantics. |
lean/disaster-recovery/.gitignore |
Excludes Lake artifacts. |
include/ccf/service/tables/self_healing_open.h |
Defines trace events and table. |
CMakeLists.txt |
Adds trace build option. |
.github/workflows/README.md |
Documents Lean CI. |
.github/workflows/lean-shallow.yml |
Adds Lean verification workflow. |
.github/workflows/ci.yml |
Enables SNP trace validation. |
.github/workflows/ci-verification.yml |
Adds exhaustive scheduled checks. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Validate commands before enumeration, trigger CI for merger tests, bind gossip payloads to causal sends, and enforce atomic ordered retry batches. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Remove checked-in and synthetic recovery traces so Lean replay is exercised exclusively with NDJSON captured from the C++ implementation and retained as CI artifacts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Model retries, in-flight delivery, timeouts, and terminal effects around the canonical protocol. Prove provenance, location consistency, action locality, append-only histories, and well-formedness for all reachable global states. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Prove unbounded quorum-opener uniqueness from vote provenance and strict-majority intersection. Prove maximum selection and committed-prefix preservation under explicit full-gossip and durability premises. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Define global executions and action-oriented fairness, prove phase-by-phase progress to a completed opener, and prove all active nodes eventually terminate under an explicit broadcast-before-completion ordering premise. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Compile every project target with warnings as errors and scan every DisasterRecovery declaration transitively for sorryAx. Run the scan explicitly in Lean shallow CI. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
Request host restart from post-commit hooks instead of from the JOINING transaction. Preserve trace-event-before-restart ordering in trace-enabled builds. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 46d56f8a-935f-4551-abe3-84bd4f951865
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
Equivalence evidence
The bounded comparison checks the initial state, every reachable normalized state, every labeled edge in both directions, and all nine predicate valuations:
This is exhaustive bounded evidence, not an unbounded theorem about Rust. The separate canonical Lean model intentionally follows C++ where the legacy model differs.
Reproduce the bounded comparison
From a fresh Ubuntu checkout:
Representative timings on the WSL development machine, with dependencies already cached:
lake buildpython3 compare.py --nodes 1 2python3 compare.py --nodes 1 2 3The complete Lean shallow CI job, including toolchain installation, Mathlib cache restoration, builds, model checks, and the
n=1,2comparison, takes roughly 3 minutes 20 seconds. Timings vary by machine and cache state.Global proof foundation
Protocol/Global.lean wraps the canonical transition function with active nodes, in-flight messages, immutable send history, and terminal effects. Protocol/Invariants.lean proves, without axioms or placeholders, that every reachable global state preserves configured node keys and internal locations, unique configured active nodes, retry-derived message provenance, configured gossip TxIDs, in-flight/send-history consistency, active senders, action locality, and monotonic opening/restart/completion histories.
Protocol/Quorum.lean proves duplicate-free counted votes, prior-send evidence for every counted vote, immutable vote targets, complete opening evidence, strict-majority intersection at n / 2 + 1, and the unbounded quorum_opener_unique theorem over arbitrary reachable histories.
Protocol/Committed.lean defines lexicographic TxID prefix order, proves maximumGossip is a member and upper bound of every collected gossip, and proves committed-prefix preservation. The premise is intentionally explicit: DurableCommit requires a configured recovered ledger covering the commit, while FullGossipSelection requires a real sent vote whose frozen snapshot contains exactly the configured recovered TxIDs. Quorum opening alone does not imply full gossip because voting may follow a gossip timeout.
Protocol/GlobalTemporal.lean defines infinite Global.next executions and action-oriented retry, delivery, and timeout fairness. It proves every active node leaves Gossiping, some target opens, every Opening node completes via a well-founded timeout-lane measure, and fair_some_opener_completes. global_progress additionally proves every active node eventually terminates. Its separate BroadcastBeforeCompletion premise states only that IAmOpen messages were sent before completion; follower outcomes are derived from a reachable announcement-resolution invariant, actual delivery actions, and transition replay. This premise is deliberately not mislabeled as weak fairness because weak fairness cannot order two actions enabled only for a finite interval. quorum_path_progress excludes failover completions, derives unique completion via quorum_opener_unique, and proves every other active participant restarts. The theorems remain conditional and do not construct a concrete fair scheduler.
Production restart ordering hardened
The trace instrumentation exposed a production-significant ordering gap: the default build previously wrote the host restart ringbuffer message from inside the transaction entering JOINING, before that transaction was known to have committed. A later transaction failure could therefore restart the process without a committed JOINING decision.
This PR now closes that gap. The immediate restart was removed and both build configurations call a shared restart_after_commit helper only from global commit hooks. Default builds trigger it when the committed recovery state becomes JOINING. Trace-enabled builds continue to trigger it from the committed trace-event hook, after emitting join_restart, preserving the stronger log-before-restart ordering required for truthful replay. Aborted or rolled-back JOINING transactions cannot request restart in either build.
This is a commit-before-restart guarantee, not an exactly-once guarantee: repeated committed JOINING writes may request the idempotent host restart again before the first restart takes effect.
Real implementation trace validation
No recovery-decision-protocol traces are checked into the repository, and no Python-generated protocol traces are fed to Lean. Every NDJSON trace checked in CI is captured from the running C++ implementation.
The flow is:
-DCCF_RECOVERY_TRACE=ON.trace-validatorexecutable.CCF_LEAN_TRACE_VALIDATORpointing to that executable.RDP_TRACErecords for starts, exact retry send batches, accepted receives, committed timeouts, and one-shot effects.tests/infra/recovery_trace.pyextracts records from every node log and topologically orders them using per-node sequence pluscaused_byedges.*.recovery.ndjsonbefore invoking Lean.Trace/Replay.leanreplays every event through the canonicalProtocol/Model.leantransition relation and rejects mismatched phases, causes, gossip payloads, sends, batches, effects, or termination.On an SNP machine, the same path can be run with:
The generated traces are under
build/workspace/**/*.recovery.ndjson. Any captured trace can be replayed directly:The SNP suite exercises quorum, failover, and multiple-timeout recovery on both Milan and Genoa runners.
Validation
node_frontend_testcargo checkandcargo buildpython3 compare.py --nodes 1 2 3Transition plan
Stateright and its existing CI job remain in place while the Lean replacement accumulates evidence. Once the bounded equivalence and SNP implementation-trace jobs have a stable green history, the temporary migration scaffolding can be removed.
Remove after transition
Approximately 2,822 lines are temporary, including generated lock/docs:
tla/disaster-recovery/:src/model.rsand the Stateright property/checker CLI insrc/main.rssrc/export.rsCargo.toml,Cargo.lock, and the Stateright READMEDisasterRecovery/Model.leanDisasterRecovery/Checker.leanMain.leanandTests.leancompare.pyProtocol/Refinement.lean, after extracting any generally useful standalone quorum lemmasKeep long term
Protocol/Model.lean: the canonical specification aligned with production C++Protocol/Temporal.lean: canonical safety and fairness-aware progress proofsProtocol/Global.leanandProtocol/Invariants.lean: distributed semantics and reachable-state foundationsProtocol/Quorum.leanandProtocol/Committed.lean: global safety and prefix-preservation proofsProtocol/GlobalTemporal.lean: global fairness and termination proofsCanonicalTests.lean: bounded canonical checks, trimmed of legacy-specific assertionsProtocol/Trace/Format.leanProtocol/Trace/Replay.leanTraceMain.leanandTRACE_FORMAT_V1.mdCCF_RECOVERY_TRACEtests/infra/recovery_trace.pyand e2e wiring for quorum, failover, and multiple-timeout tracesAxiomChecks.lean, and the Lean/SNP CI checks, with equivalence-only steps removedThe long-term path is therefore direct:
The legacy Lean mirror, BFS checker, Rust exporter, comparator, and refinement bridge exist only to establish confidence during cutover.