Context: Phase 3 of the ECHIDNA expansion is the architecturally trickiest of the three remaining phases. Unlike Phases 2B and 4, where the backends are mostly subprocess CAS shells or DL/probabilistic solvers with familiar shapes, Phase 3 brings in differential dynamic logic, cylindrical algebraic decomposition, Reduce-CAS shims, connection-method intuitionistic / modal, and tableau generators for arbitrary modal logics. Per the ECHIDNA-EXPANSION-TOMORROW-2026-04-28 plan: "Don't farm these out. Pick a 2-3 hour midweek slot and ask Opus to do them solo. Each is ~250 LOC but the semantics matter."
This document is that brief. It locks the design for all 7
remaining Phase 3 backends, names the worked example already landed
(keymaerax.rs), and gives the next Opus session a step-by-step path
to closing the phase.
Master plan: ~/Desktop/ECHIDNA-EXPANSION-TOMORROW-2026-04-28.md
Reference doc: docs/ROADMAP.md row "Every important solver"
Status as of 2026-04-27: KeYmaera X landed in
src/rust/provers/keymaerax.rs (8/8 tests passing); 6 backends
remain.
| Backend | Status | LOC | Notes |
|---|---|---|---|
| KeYmaera X (dDL / hybrid systems) | ✅ landed 2026-04-27 | 313 | Worked example for this brief |
| QEPCAD-B (cylindrical algebraic decomposition) | 🟡 open | ~250 | CAD input format |
| Redlog (Reduce-CAS frontend, real algebra) | 🟡 open | ~250 | Reduce shim + real-quantifier elim |
| MleanCoP (connection-method intuitionistic) | 🟡 open | ~250 | Prolog-based, esoteric output |
| ileanCoP (intuitionistic leanCoP variant) | 🟡 open | ~250 | Shares parser with MleanCoP |
| nanoCoP (lean-er connection prover) | 🟡 open | ~250 | Same family as above |
| MetTeL2 (tableau generator for modal logics) | 🟡 open | ~250 | JVM, configuration-heavy |
Total expected: ~1500 LOC + tests for the 6 remaining.
- Input format:
.kyxarchive entries with hybrid programs. - Output: stdout/stderr scan for
proved/closed/qed(closed) vsopen subgoals/failed/untrusted(open or refuted). - Trust tier: 2 (delegates QE to external CAS — Mathematica/Z3/Polya).
- FFI u8: 128.
- Reference:
src/rust/provers/keymaerax.rs. Use this as the template for the remaining six.
- Input: prefixed-formula syntax for real-quantifier elimination
problems. Header with variable order, then
(quantifier x)[ formula ]. - CLI invocation: stdin-driven REPL; backends typically pipe
the QEPCAD input followed by
finishto flush. - Output: prints either
Equivalent quantifier-free formula: <expr>(success) or error /unable to compute. Consider formulaTrue/Falseasverify_proofoutcome. - Trust tier: 2 (CAD result is sound but the implementation is large; no external proof certificate).
- Hazard: very long runtimes for formulae with > 4 variables; honour the timeout aggressively.
- Input: REDUCE-CAS surface syntax —
rlqe formula;for QE,rlcadfor CAD-based decision, etc. Statements terminated with;or$(silent). - CLI invocation: spawn
redcsl(CSL build of Reduce) orredpsl, feed the Redlogload_package redlog;then the goal, read stdout. - Output: scan for
true,false, or a residual quantifier-free formula.true→verify_proofreturnsOk(true); everything else eitherOk(false)orErr. - Trust tier: 2 (parallel to QEPCAD).
- Hazard: Reduce sometimes prints leading whitespace and
END OF FILEmarkers. Strip carefully.
These three share a parser and CLI shape; implement them as three
backend files but factor common helpers into a private
connection_method.rs module if the duplication exceeds ~30 LOC.
- Input: TPTP CNF/FOF (MleanCoP) or intuitionistic / modal variants (ileanCoP, nanoCoP).
- CLI invocation: Prolog-driven; typical call is
swipl -g "leancop:prove('input.p')" -t halt. Variants differ in theprove/1predicate name. - Output: lines like
% SZS status Theorem/% SZS status CounterSatisfiable/% SZS status GaveUp(when the upstream uses TPTP-style SZS) OR plain+ matrix proof found(when in native mode). - Trust tier: 2 for MleanCoP (classical), 3 for ileanCoP (intuitionistic kernel is small — connection method) and nanoCoP (similarly small kernel).
- Hazard: Prolog stack overflow on goals with deep unification —
bound the heap with
--stack-limit=256m.
- Input: a tableau specification (logic axioms in MetTeL's syntax) plus a goal formula. Different from a one-shot prover — MetTeL2 generates a tableau prover for the supplied logic, then uses it. We only support the "use the bundled standard logics" path for v1 (S4, K, KT, KD45, ...).
- CLI invocation:
java -jar mettel2.jar -s <logic>.spec -i <input>.mt(typical). Wrap with-versionprobe. - Output:
Provable./Not provable./Unknown.(tableau open). - Trust tier: 2 — the generated tableau prover is correct by construction, but we don't formally check the spec.
- Hazard: JVM cold-start latency. Cache the JVM via
--keep-aliveif benchmark numbers ask for it later.
For each backend, you produce:
-
Backend file at
src/rust/provers/<name>.rs. Mirrorkeymaerax.rs:- Crate-level
//!doc explaining why this backend exists and the input format / output parsing. - Module-level
#]). pub struct <Name>Backend { config: ProverConfig }.impl <Name>Backend { new, to_<format>, parse_result }.#[async_trait] impl ProverBackend for <Name>Backend—kind,version,parse_file,parse_string,apply_tactic(returnanyhow::anyhow!(... not supported ...)if non-interactive),verify_proof,export,suggest_tactics(returnOk(vec![])for v1),search_theorems(returnOk(vec![])),config,set_config.#[cfg(test)] mod testswith at minimum:_kind,_to_<format>_basic,_parse_result_proved,_parse_result_failed,_parse_result_inconclusive.
- Crate-level
-
mod.rs wiring — add the backend at all 10 integration points. Use
keymaeraxas the search anchor for each:pub mod <name>;at the top ofmod.rs(~line 70).<Name>variant in theProverKindenum.- Match arm in the name parser (
"<name>" | "<alias>" => Ok(ProverKind::<Name>)). ProverKind::<Name>,in theall()list (~line 700).ProverKind::<Name> => <complexity>,in the complexity table (~line 800).ProverKind::<Name> => <trust>,in the trust tier table (~line 960).ProverKind::<Name> => <ml_score>,in the ML guidance table (~line 1090).ProverKind::<Name> => "<binary>",in the binary-name table (~line 1224).ProverKind::<Name> => Ok(Box::new(<name>::<Name>Backend::new(config))),in the factory dispatch (~line 1614).- FFI: u8 mapping at
src/rust/ffi/mod.rs— add<n> => Some(ProverKind::<Name>),tokind_from_u8andProverKind::<Name> => <n>,tokind_to_u8. KeYmaera X took slot 128; the remaining six get 129..134 in the order: QEPCAD-B (129), Redlog (130), MleanCoP (131), ileanCoP (132), nanoCoP (133), MetTeL2 (134). MetiTarski is already at its existing slot — do not renumber existing entries.
-
Tests — at minimum 8 unit tests per backend (the count
keymaerax.rsships). Pattern after thekeymaerax::testsblock. -
Acceptance gate:
cargo build --libclean (no new warnings beyond the pre-existing four),cargo test --lib <name>passes 100%,cargo test --liboverall green.
The six remaining backends differ wildly in semantics. Suggested order — easiest first (sets up the dispatch wiring once, then the harder ones reuse it):
- QEPCAD-B — clean CAD input, well-documented stdout markers.
- Redlog — same shape as QEPCAD but uses Reduce CSL; reuse subprocess pattern.
- MetTeL2 — JVM call, simple stdout parse.
- MleanCoP — Prolog binding, TPTP I/O.
- ileanCoP — share helpers with MleanCoP; intuitionistic
nuances in
parse_result. - nanoCoP — share helpers with the leanCoP family; lighter tactic surface.
Budget ~30 minutes per backend file + ~15 minutes for the 10 integration points + ~15 minutes for tests + verification = 1 hour per backend. Whole phase: 6 hours. Don't try to compress; semantics matter.
Already fixed in mod.rs/ffi.rs. Repeat here so a future Opus session doesn't have to re-derive them:
| Backend | FFI u8 |
|---|---|
| KeYmaera X | 128 |
| QEPCAD-B | 129 |
| Redlog | 130 |
| MleanCoP | 131 |
| ileanCoP | 132 |
| nanoCoP | 133 |
| MetTeL2 | 134 |
- All 6 remaining backend files exist under
src/rust/provers/. cargo build --libclean (0 errors, no new warnings).cargo test --libpasses (every existing test green plus ≥ 6 × 8 = 48 new backend tests).provers/mod.rsall()list contains all 7 Phase 3 backends.- Each backend's
verify_proofcalls the upstream binary with the correct flag — gated such that missing binary returns a structured error, not a panic. (KeYmaera X already does this viaCommand::spawn().context(...).) docs/ROADMAP.mdrow "Every important solver" updated with the new ProverKind count (currently 128 → 134 after Phase 3 closes).PROOF-NEEDS.mdand.machine_readable/6a2/STATE.a2mlreflect the closure of Phase 3.
- No upstream binary provisioning. The CI containers don't have
KeYmaera X, QEPCAD-B, Redlog, leanCoP family, or MetTeL2
installed; integration tests gated by
#[cfg(feature = "<name>-real")]are out of scope here. Document the gap. - No Bellerophon / leanCoP tactic-language dispatch. All
Phase 3 backends route through
verify_proofonly; interactiveapply_tacticreturns the "not supported" error per the KeYmaera X pattern. - No GNN integration for these backends. The GNN ranking layer
(§4.4 of ECHIDNA-NOTES) is a separate phase; Phase 3
suggest_tacticsreturnsOk(vec![]). - No formal proofs of
verify_proofcorrectness for Phase 3. These are dispatch-only backends; their outputs feed thecompute_trust_levelpipeline at most. Trust tier 2 is the ceiling without certificate output.
For each backend, add a tiny tests/fixtures/<name>/ directory
containing 2-3 .kyx / .qep / .red / .p / .mt example
problems lifted from the upstream documentation. These are not
loaded in the unit tests (which use string literals); they exist
for hand-running echidna prove against a local install during
development.
- Do not implement Bellerophon / leanCoP tactic compilation inside the backend file. That is a separate ~1500 LOC project per the ECHIDNA-NOTES §4.1 design.
- Do not add upstream-binary auto-detection logic to mod.rs. Keep the binary-name table simple; deployment-time configuration handles binary location.
- Do not modify the trust pipeline (
src/rust/dispatch.rs,verification/) to "support" the new backends. They plug in through the existingProverBackendtrait; no pipeline edit should be necessary. - Do not break MetiTarski. It's already shipped (Phase 1B, cbd9449); it stays at trust tier 2 / FFI slot whatever it has. Don't reshuffle existing FFI numbers.
cd /var/mnt/eclipse/repos/echidna
cargo build --lib # 0 errors
cargo test --lib qepcad redlog mleancop ileancop nanocop mettel2 keymaerax \
# all green
grep -c "ProverKind::" src/rust/provers/mod.rs # should rise by 6
git log --oneline | head -10 # 6 fresh feat() commits- This brief:
docs/handover/PHASE-3-PROMPT.md - Master plan:
~/Desktop/ECHIDNA-EXPANSION-TOMORROW-2026-04-28.md - Worked example:
src/rust/provers/keymaerax.rs(committed 2026-04-27) - Companion handoff:
docs/handover/SUGGEST-CLI-PROMPT.md(the §4.1/4.3 mechanical-baseline brief from the same session)
When Phase 3 closes, update this file's status table to all-green,
move the doc to docs/handover/archived/, and link the closing
commits.