|
| 1 | +<!-- SPDX-License-Identifier: PMPL-1.0-or-later --> |
| 2 | +# Echidna Phase 3 — Real-Algebraic + Modal/Hybrid Backends — Opus Handoff Prompt |
| 3 | + |
| 4 | +**Context**: Phase 3 of the ECHIDNA expansion is the |
| 5 | +**architecturally trickiest** of the three remaining phases. Unlike |
| 6 | +Phases 2B and 4, where the backends are mostly subprocess CAS shells |
| 7 | +or DL/probabilistic solvers with familiar shapes, Phase 3 brings in |
| 8 | +*differential dynamic logic*, *cylindrical algebraic decomposition*, |
| 9 | +*Reduce-CAS shims*, *connection-method intuitionistic / modal*, and |
| 10 | +*tableau generators for arbitrary modal logics*. Per the |
| 11 | +**ECHIDNA-EXPANSION-TOMORROW-2026-04-28** plan: "Don't farm these |
| 12 | +out. Pick a 2-3 hour midweek slot and ask Opus to do them solo. Each |
| 13 | +is ~250 LOC but the semantics matter." |
| 14 | + |
| 15 | +This document is **that brief**. It locks the design for all 7 |
| 16 | +remaining Phase 3 backends, names the worked example already landed |
| 17 | +(`keymaerax.rs`), and gives the next Opus session a step-by-step path |
| 18 | +to closing the phase. |
| 19 | + |
| 20 | +**Master plan**: `~/Desktop/ECHIDNA-EXPANSION-TOMORROW-2026-04-28.md` |
| 21 | +**Reference doc**: `docs/ROADMAP.md` row "Every important solver" |
| 22 | +**Status as of 2026-04-27**: KeYmaera X **landed** in |
| 23 | +`src/rust/provers/keymaerax.rs` (8/8 tests passing); 6 backends |
| 24 | +remain. |
| 25 | + |
| 26 | +## Status table |
| 27 | + |
| 28 | +| Backend | Status | LOC | Notes | |
| 29 | +|---|---|---|---| |
| 30 | +| **KeYmaera X** (dDL / hybrid systems) | ✅ landed 2026-04-27 | 313 | Worked example for this brief | |
| 31 | +| **QEPCAD-B** (cylindrical algebraic decomposition) | 🟡 open | ~250 | CAD input format | |
| 32 | +| **Redlog** (Reduce-CAS frontend, real algebra) | 🟡 open | ~250 | Reduce shim + real-quantifier elim | |
| 33 | +| **MleanCoP** (connection-method intuitionistic) | 🟡 open | ~250 | Prolog-based, esoteric output | |
| 34 | +| **ileanCoP** (intuitionistic leanCoP variant) | 🟡 open | ~250 | Shares parser with MleanCoP | |
| 35 | +| **nanoCoP** (lean-er connection prover) | 🟡 open | ~250 | Same family as above | |
| 36 | +| **MetTeL2** (tableau generator for modal logics) | 🟡 open | ~250 | JVM, configuration-heavy | |
| 37 | + |
| 38 | +Total expected: ~1500 LOC + tests for the 6 remaining. |
| 39 | + |
| 40 | +## Architectural patterns (per-backend semantics) |
| 41 | + |
| 42 | +### KeYmaera X (✅ already done — worked example) |
| 43 | + |
| 44 | +- **Input format**: `.kyx` archive entries with hybrid programs. |
| 45 | +- **Output**: stdout/stderr scan for `proved` / `closed` / `qed` (closed) |
| 46 | + vs `open subgoals` / `failed` / `untrusted` (open or refuted). |
| 47 | +- **Trust tier**: 2 (delegates QE to external CAS — Mathematica/Z3/Polya). |
| 48 | +- **FFI u8**: 128. |
| 49 | +- **Reference**: `src/rust/provers/keymaerax.rs`. Use this as the |
| 50 | + template for the remaining six. |
| 51 | + |
| 52 | +### QEPCAD-B (CAD) |
| 53 | + |
| 54 | +- **Input**: prefixed-formula syntax for real-quantifier elimination |
| 55 | + problems. Header with variable order, then `(quantifier x)[ formula ]`. |
| 56 | +- **CLI invocation**: stdin-driven REPL; backends typically pipe |
| 57 | + the QEPCAD input followed by `finish` to flush. |
| 58 | +- **Output**: prints either `Equivalent quantifier-free formula: |
| 59 | + <expr>` (success) or error / `unable to compute`. Consider |
| 60 | + formula `True` / `False` as `verify_proof` outcome. |
| 61 | +- **Trust tier**: 2 (CAD result is sound but the implementation is |
| 62 | + large; no external proof certificate). |
| 63 | +- **Hazard**: very long runtimes for formulae with > 4 variables; |
| 64 | + honour the timeout aggressively. |
| 65 | + |
| 66 | +### Redlog (REDUCE-CAS frontend) |
| 67 | + |
| 68 | +- **Input**: REDUCE-CAS surface syntax — `rlqe formula;` for QE, |
| 69 | + `rlcad` for CAD-based decision, etc. Statements terminated with |
| 70 | + `;` or `$` (silent). |
| 71 | +- **CLI invocation**: spawn `redcsl` (CSL build of Reduce) or |
| 72 | + `redpsl`, feed the Redlog `load_package redlog;` then the goal, |
| 73 | + read stdout. |
| 74 | +- **Output**: scan for `true`, `false`, or a residual quantifier-free |
| 75 | + formula. `true` → `verify_proof` returns `Ok(true)`; everything |
| 76 | + else either `Ok(false)` or `Err`. |
| 77 | +- **Trust tier**: 2 (parallel to QEPCAD). |
| 78 | +- **Hazard**: Reduce sometimes prints leading whitespace and |
| 79 | + `END OF FILE` markers. Strip carefully. |
| 80 | + |
| 81 | +### MleanCoP / ileanCoP / nanoCoP (connection-method) |
| 82 | + |
| 83 | +These three share a parser and CLI shape; implement them as **three |
| 84 | +backend files** but factor common helpers into a private |
| 85 | +`connection_method.rs` module if the duplication exceeds ~30 LOC. |
| 86 | + |
| 87 | +- **Input**: TPTP CNF/FOF (MleanCoP) or intuitionistic / modal |
| 88 | + variants (ileanCoP, nanoCoP). |
| 89 | +- **CLI invocation**: Prolog-driven; typical call is |
| 90 | + `swipl -g "leancop:prove('input.p')" -t halt`. Variants differ in |
| 91 | + the `prove/1` predicate name. |
| 92 | +- **Output**: lines like `% SZS status Theorem` / |
| 93 | + `% SZS status CounterSatisfiable` / `% SZS status GaveUp` |
| 94 | + (when the upstream uses TPTP-style SZS) OR plain |
| 95 | + `+ matrix proof found` (when in native mode). |
| 96 | +- **Trust tier**: 2 for MleanCoP (classical), 3 for ileanCoP |
| 97 | + (intuitionistic kernel is small — connection method) and nanoCoP |
| 98 | + (similarly small kernel). |
| 99 | +- **Hazard**: Prolog stack overflow on goals with deep unification — |
| 100 | + bound the heap with `--stack-limit=256m`. |
| 101 | + |
| 102 | +### MetTeL2 (tableau generator) |
| 103 | + |
| 104 | +- **Input**: a tableau **specification** (logic axioms in MetTeL's |
| 105 | + syntax) plus a goal formula. Different from a one-shot prover — |
| 106 | + MetTeL2 generates a tableau prover *for* the supplied logic, then |
| 107 | + uses it. We only support the "use the bundled standard logics" |
| 108 | + path for v1 (S4, K, KT, KD45, ...). |
| 109 | +- **CLI invocation**: `java -jar mettel2.jar -s <logic>.spec |
| 110 | + -i <input>.mt` (typical). Wrap with `-version` probe. |
| 111 | +- **Output**: `Provable.` / `Not provable.` / `Unknown.` (tableau |
| 112 | + open). |
| 113 | +- **Trust tier**: 2 — the generated tableau prover is correct by |
| 114 | + construction, but we don't formally check the spec. |
| 115 | +- **Hazard**: JVM cold-start latency. Cache the JVM via |
| 116 | + `--keep-alive` if benchmark numbers ask for it later. |
| 117 | + |
| 118 | +## What "landed" looks like (using KeYmaera X as the template) |
| 119 | + |
| 120 | +For each backend, you produce: |
| 121 | + |
| 122 | +1. **Backend file** at `src/rust/provers/<name>.rs`. Mirror |
| 123 | + `keymaerax.rs`: |
| 124 | + - Crate-level `//!` doc explaining *why this backend exists* and |
| 125 | + the *input format / output parsing*. |
| 126 | + - Module-level `#![allow(dead_code)]` (warranted: tests are |
| 127 | + gated by `#[cfg(test)]`). |
| 128 | + - `pub struct <Name>Backend { config: ProverConfig }`. |
| 129 | + - `impl <Name>Backend { new, to_<format>, parse_result }`. |
| 130 | + - `#[async_trait] impl ProverBackend for <Name>Backend` — |
| 131 | + `kind`, `version`, `parse_file`, `parse_string`, `apply_tactic` |
| 132 | + (return `anyhow::anyhow!(... not supported ...)` if |
| 133 | + non-interactive), `verify_proof`, `export`, |
| 134 | + `suggest_tactics` (return `Ok(vec![])` for v1), |
| 135 | + `search_theorems` (return `Ok(vec![])`), `config`, `set_config`. |
| 136 | + - `#[cfg(test)] mod tests` with at minimum: `_kind`, |
| 137 | + `_to_<format>_basic`, `_parse_result_proved`, |
| 138 | + `_parse_result_failed`, `_parse_result_inconclusive`. |
| 139 | + |
| 140 | +2. **mod.rs wiring** — add the backend at all 10 integration points. |
| 141 | + Use `keymaerax` as the search anchor for each: |
| 142 | + |
| 143 | + 1. `pub mod <name>;` at the top of `mod.rs` (~line 70). |
| 144 | + 2. `<Name>` variant in the `ProverKind` enum. |
| 145 | + 3. Match arm in the name parser (`"<name>" | "<alias>" => |
| 146 | + Ok(ProverKind::<Name>)`). |
| 147 | + 4. `ProverKind::<Name>,` in the `all()` list (~line 700). |
| 148 | + 5. `ProverKind::<Name> => <complexity>,` in the complexity table |
| 149 | + (~line 800). |
| 150 | + 6. `ProverKind::<Name> => <trust>,` in the trust tier table |
| 151 | + (~line 960). |
| 152 | + 7. `ProverKind::<Name> => <ml_score>,` in the ML guidance table |
| 153 | + (~line 1090). |
| 154 | + 8. `ProverKind::<Name> => "<binary>",` in the binary-name table |
| 155 | + (~line 1224). |
| 156 | + 9. `ProverKind::<Name> => |
| 157 | + Ok(Box::new(<name>::<Name>Backend::new(config))),` in the |
| 158 | + factory dispatch (~line 1614). |
| 159 | + 10. **FFI: u8 mapping at `src/rust/ffi/mod.rs`** — add |
| 160 | + `<n> => Some(ProverKind::<Name>),` to `kind_from_u8` and |
| 161 | + `ProverKind::<Name> => <n>,` to `kind_to_u8`. KeYmaera X took |
| 162 | + slot 128; the remaining six get 129..134 in the order: |
| 163 | + QEPCAD-B (129), Redlog (130), MleanCoP (131), |
| 164 | + ileanCoP (132), nanoCoP (133), MetTeL2 (134). MetiTarski is |
| 165 | + already at its existing slot — do not renumber existing |
| 166 | + entries. |
| 167 | + |
| 168 | +3. **Tests** — at minimum 8 unit tests per backend (the count |
| 169 | + `keymaerax.rs` ships). Pattern after the `keymaerax::tests` |
| 170 | + block. |
| 171 | + |
| 172 | +4. **Acceptance gate**: `cargo build --lib` clean (no new warnings |
| 173 | + beyond the pre-existing four), `cargo test --lib <name>` |
| 174 | + passes 100%, `cargo test --lib` overall green. |
| 175 | + |
| 176 | +## Sequencing recommendation |
| 177 | + |
| 178 | +The six remaining backends differ wildly in semantics. Suggested |
| 179 | +order — **easiest first** (sets up the dispatch wiring once, then |
| 180 | +the harder ones reuse it): |
| 181 | + |
| 182 | +1. **QEPCAD-B** — clean CAD input, well-documented stdout markers. |
| 183 | +2. **Redlog** — same shape as QEPCAD but uses Reduce CSL; reuse |
| 184 | + subprocess pattern. |
| 185 | +3. **MetTeL2** — JVM call, simple stdout parse. |
| 186 | +4. **MleanCoP** — Prolog binding, TPTP I/O. |
| 187 | +5. **ileanCoP** — share helpers with MleanCoP; intuitionistic |
| 188 | + nuances in `parse_result`. |
| 189 | +6. **nanoCoP** — share helpers with the leanCoP family; lighter |
| 190 | + tactic surface. |
| 191 | + |
| 192 | +Budget ~30 minutes per backend file + ~15 minutes for the 10 |
| 193 | +integration points + ~15 minutes for tests + verification = 1 hour |
| 194 | +per backend. Whole phase: 6 hours. Don't try to compress; semantics |
| 195 | +matter. |
| 196 | + |
| 197 | +## Per-backend FFI u8 reservations |
| 198 | + |
| 199 | +Already fixed in mod.rs/ffi.rs. Repeat here so a future Opus session |
| 200 | +doesn't have to re-derive them: |
| 201 | + |
| 202 | +| Backend | FFI u8 | |
| 203 | +|---|---| |
| 204 | +| KeYmaera X | 128 | |
| 205 | +| QEPCAD-B | 129 | |
| 206 | +| Redlog | 130 | |
| 207 | +| MleanCoP | 131 | |
| 208 | +| ileanCoP | 132 | |
| 209 | +| nanoCoP | 133 | |
| 210 | +| MetTeL2 | 134 | |
| 211 | + |
| 212 | +## Acceptance criteria for the *phase* |
| 213 | + |
| 214 | +1. All 6 remaining backend files exist under `src/rust/provers/`. |
| 215 | +2. `cargo build --lib` clean (0 errors, no new warnings). |
| 216 | +3. `cargo test --lib` passes (every existing test green plus |
| 217 | + ≥ 6 × 8 = 48 new backend tests). |
| 218 | +4. `provers/mod.rs` `all()` list contains all 7 Phase 3 backends. |
| 219 | +5. Each backend's `verify_proof` calls the upstream binary with the |
| 220 | + correct flag — gated such that **missing binary returns a |
| 221 | + structured error**, not a panic. (KeYmaera X already does this |
| 222 | + via `Command::spawn().context(...)`.) |
| 223 | +6. `docs/ROADMAP.md` row "Every important solver" updated with the |
| 224 | + new ProverKind count (currently 128 → 134 after Phase 3 closes). |
| 225 | +7. `PROOF-NEEDS.md` and `.machine_readable/6a2/STATE.a2ml` |
| 226 | + reflect the closure of Phase 3. |
| 227 | + |
| 228 | +## Non-goals |
| 229 | + |
| 230 | +- **No upstream binary provisioning.** The CI containers don't have |
| 231 | + KeYmaera X, QEPCAD-B, Redlog, leanCoP family, or MetTeL2 |
| 232 | + installed; integration tests gated by `#[cfg(feature = |
| 233 | + "<name>-real")]` are out of scope here. Document the gap. |
| 234 | +- **No Bellerophon / leanCoP tactic-language dispatch.** All |
| 235 | + Phase 3 backends route through `verify_proof` only; interactive |
| 236 | + `apply_tactic` returns the "not supported" error per the |
| 237 | + KeYmaera X pattern. |
| 238 | +- **No GNN integration for these backends.** The GNN ranking layer |
| 239 | + (§4.4 of ECHIDNA-NOTES) is a separate phase; Phase 3 |
| 240 | + `suggest_tactics` returns `Ok(vec![])`. |
| 241 | +- **No formal proofs of `verify_proof` correctness for Phase 3.** |
| 242 | + These are dispatch-only backends; their outputs feed the |
| 243 | + `compute_trust_level` pipeline at most. Trust tier 2 is the |
| 244 | + ceiling without certificate output. |
| 245 | + |
| 246 | +## Test fixtures (where they should live) |
| 247 | + |
| 248 | +For each backend, add a tiny `tests/fixtures/<name>/` directory |
| 249 | +containing 2-3 `.kyx` / `.qep` / `.red` / `.p` / `.mt` example |
| 250 | +problems lifted from the upstream documentation. These are *not* |
| 251 | +loaded in the unit tests (which use string literals); they exist |
| 252 | +for hand-running `echidna prove` against a local install during |
| 253 | +development. |
| 254 | + |
| 255 | +## What you (Opus, future session) should NOT do |
| 256 | + |
| 257 | +- **Do not implement Bellerophon / leanCoP tactic compilation** |
| 258 | + inside the backend file. That is a separate ~1500 LOC project |
| 259 | + per the ECHIDNA-NOTES §4.1 design. |
| 260 | +- **Do not add upstream-binary auto-detection logic** to mod.rs. |
| 261 | + Keep the binary-name table simple; deployment-time |
| 262 | + configuration handles binary location. |
| 263 | +- **Do not modify the trust pipeline** (`src/rust/dispatch.rs`, |
| 264 | + `verification/`) to "support" the new backends. They plug in |
| 265 | + through the existing `ProverBackend` trait; no pipeline edit |
| 266 | + should be necessary. |
| 267 | +- **Do not break MetiTarski**. It's already shipped (Phase 1B, |
| 268 | + cbd9449); it stays at trust tier 2 / FFI slot whatever it has. |
| 269 | + Don't reshuffle existing FFI numbers. |
| 270 | + |
| 271 | +## How to verify phase completion (5-minute sanity check) |
| 272 | + |
| 273 | +```bash |
| 274 | +cd /var/mnt/eclipse/repos/echidna |
| 275 | +cargo build --lib # 0 errors |
| 276 | +cargo test --lib qepcad redlog mleancop ileancop nanocop mettel2 keymaerax \ |
| 277 | + # all green |
| 278 | +grep -c "ProverKind::" src/rust/provers/mod.rs # should rise by 6 |
| 279 | +git log --oneline | head -10 # 6 fresh feat() commits |
| 280 | +``` |
| 281 | + |
| 282 | +## Tracking |
| 283 | + |
| 284 | +- This brief: `docs/handover/PHASE-3-PROMPT.md` |
| 285 | +- Master plan: `~/Desktop/ECHIDNA-EXPANSION-TOMORROW-2026-04-28.md` |
| 286 | +- Worked example: `src/rust/provers/keymaerax.rs` (committed |
| 287 | + 2026-04-27) |
| 288 | +- Companion handoff: `docs/handover/SUGGEST-CLI-PROMPT.md` |
| 289 | + (the §4.1/4.3 mechanical-baseline brief from the same session) |
| 290 | + |
| 291 | +When Phase 3 closes, update this file's status table to all-green, |
| 292 | +move the doc to `docs/handover/archived/`, and link the closing |
| 293 | +commits. |
0 commit comments