|
| 1 | +# Implementation Specifications: Container Isolation + GNN Training |
| 2 | + |
| 3 | +## SPEC 1: Container Isolation (🔴 CRITICAL - Stage 1 Blocker) |
| 4 | + |
| 5 | +### Problem |
| 6 | +All 105 prover implementations directly invoke solver binaries via `Command::new(self.binary())` without sandbox wrapping. This allows arbitrary code execution on the host system. **Required for production Stage 1 release.** |
| 7 | + |
| 8 | +### Current State |
| 9 | +- ✅ `src/rust/executor/sandbox.rs` — SandboxedExecutor fully implemented (lines 84-200+) |
| 10 | + - Supports Podman (preferred), Bubblewrap (fallback), None (dev) |
| 11 | + - Includes resource limits: memory, CPU, time, disk |
| 12 | + - Auto-detection of available sandboxing tools |
| 13 | +- ❌ **NOT WIRED**: Sandbox not called from any prover backend |
| 14 | + |
| 15 | +### Gap Analysis |
| 16 | +**File: src/rust/provers/*.rs** (all 105 implementations) |
| 17 | + |
| 18 | +Vulnerable pattern (current): |
| 19 | +```rust |
| 20 | +// athena.rs:91, cameleer.rs:111, kissat.rs:368, etc. (105 occurrences) |
| 21 | +let mut cmd = Command::new(self.binary()); |
| 22 | +cmd.arg("--version").output().await |
| 23 | +``` |
| 24 | + |
| 25 | +Safe pattern (required): |
| 26 | +```rust |
| 27 | +let config = SandboxConfig { kind: SandboxKind::Podman, ..Default::default() }; |
| 28 | +let executor = SandboxedExecutor::new(config); |
| 29 | +let output = executor.run_command( |
| 30 | + Command::new(self.binary()), |
| 31 | + &goal_input, |
| 32 | + ... |
| 33 | +)?; |
| 34 | +``` |
| 35 | + |
| 36 | +### Implementation Steps |
| 37 | + |
| 38 | +1. **Update ProverBackend trait** (`src/rust/provers/mod.rs`) |
| 39 | + - Add `sandbox_config: SandboxConfig` parameter to trait methods |
| 40 | + - Modify `fn solve(&self, goal: Goal) -> Result<Proof>` signature |
| 41 | + |
| 42 | +2. **Create SandboxExecutor wrapper** (`src/rust/executor/wrapper.rs` — NEW) |
| 43 | + - Method: `async fn run_sandboxed_command(config: SandboxConfig, cmd: Command, stdin: &str) -> Result<SandboxedOutput>` |
| 44 | + - Delegates to `SandboxedExecutor::new(config).execute(cmd, stdin)` |
| 45 | + - **Critical**: Preserve stderr for debugging; enforce resource limits strictly |
| 46 | + |
| 47 | +3. **Migrate all 105 prover backends** |
| 48 | + - Pattern: Replace all `Command::new()...spawn()` with `run_sandboxed_command(config, cmd, input)` |
| 49 | + - **Files to modify**: `src/rust/provers/{athena,cameleer,kissat,matita,...,vcl_ut}.rs` |
| 50 | + - One commit per prover family (e.g., "feat(sandbox): wrap SMT solvers (Z3, CVC5, Alt-Ergo)") to keep history clean |
| 51 | + - Integration test: each prover must still produce correct proofs post-wrapping |
| 52 | + |
| 53 | +4. **Update dispatch.rs** (`src/rust/dispatch.rs`) |
| 54 | + - Pass `SandboxConfig` from AGENTIC.a2ml entropy/risk level → sandbox mode selection |
| 55 | + - Low risk: Podman (full isolation) |
| 56 | + - Medium/High: Bubblewrap (lightweight) |
| 57 | + - Critical (never): Podman with stricter limits |
| 58 | + - Audit: log sandbox choice + resource usage per proof |
| 59 | + |
| 60 | +5. **Tests** |
| 61 | + - Unit: Each prover's sandbox wrapper produces same output as unsandboxed (regression test) |
| 62 | + - Integration: Run e2e_proof_pipeline.rs with Podman sandbox enabled |
| 63 | + - Security: Attempt file/network access from proof; verify isolation blocks it |
| 64 | + |
| 65 | +### Success Criteria |
| 66 | +- ✅ All 105 provers wrapped in SandboxedExecutor |
| 67 | +- ✅ Default sandbox mode is Podman (not None) |
| 68 | +- ✅ 638+ existing tests pass with sandbox enabled |
| 69 | +- ✅ New security integration test passes (isolation enforcement) |
| 70 | +- ✅ Panic-attack + clippy warnings clear |
| 71 | +- ✅ Audit log shows sandbox mode + resource usage per proof |
| 72 | + |
| 73 | +### Files Modified |
| 74 | +- `src/rust/provers/mod.rs` — trait update |
| 75 | +- `src/rust/executor/wrapper.rs` — new wrapper |
| 76 | +- `src/rust/provers/*.rs` — 105 implementations (batched commits) |
| 77 | +- `src/rust/dispatch.rs` — sandbox config routing |
| 78 | +- `tests/e2e_security_isolation.rs` — new security test |
| 79 | + |
| 80 | +### Timeline |
| 81 | +- High complexity but highly parallelizable (105 provers can be migrated in batches) |
| 82 | +- Estimate: 2-3 weeks for thorough migration + testing |
| 83 | +- **Critical path**: Trait update (1 day) → 3 prover families as POC (2 days) → roll out rest (5-7 days) → testing (3-5 days) |
| 84 | + |
| 85 | +--- |
| 86 | + |
| 87 | +## SPEC 2: GNN Model Training & Integration (🟡 HIGH - Stage 3 Quality Blocker) |
| 88 | + |
| 89 | +### Problem |
| 90 | +GNN scaffolds exist (`src/julia/`, `src/rust/gnn/`) but model was never trained on corpus. Currently using cosine-similarity fallback (works, but suboptimal). **Blocks Stage 3 quality gate.** |
| 91 | + |
| 92 | +### Current State |
| 93 | + |
| 94 | +**Corpus ready**: `training_data/` — 553 MB, 66,674 proofs, 179,933 tactics across 16 prover systems |
| 95 | +- `proof_states_UNIFIED_BALANCED.jsonl` (161,989 lines) |
| 96 | +- `premises_COMPLETE.jsonl` (4,789,221 lines) |
| 97 | + |
| 98 | +**Scaffolds exist but untouched**: |
| 99 | +- `src/julia/Project.toml` — Flux.jl, StatsBase.jl, MLUtils.jl declared |
| 100 | +- `src/julia/training/train.jl` — training loop (~1.4k LoC) but never invoked |
| 101 | +- `models/neural_solver.jl` — logistic regression (never trained) |
| 102 | +- `src/julia/api/gnn_endpoint.jl:40-114` — falls back to cosine similarity if model missing |
| 103 | +- `models/premise_vocab.txt` (1.31M tokens), `models/tactic_vocab.txt` (120k tokens) |
| 104 | + |
| 105 | +**Idris2 formal proofs** (ready for integration): |
| 106 | +- `src/abi/GnnProperties.idr` — 7 GNN properties proven (0 believe_me) |
| 107 | +- Requires: `MRR ≥ 0.66`, `nDCG ≥ 0.60` for claim-type="verified" |
| 108 | + |
| 109 | +### Gap Analysis |
| 110 | + |
| 111 | +**Why training never ran:** |
| 112 | +1. No documented protocol for data → model pipeline |
| 113 | +2. Unclear performance requirements (MRR/nDCG targets) |
| 114 | +3. No integration between Julia training output + Rust GNN inference |
| 115 | +4. Risk of overfitting (163k train set for 16 prover systems = ~10k examples per system) |
| 116 | + |
| 117 | +**Why fallback works but is suboptimal:** |
| 118 | +- Cosine similarity in premise space: O(n) for every query |
| 119 | +- No learned ranking: premise order = random TF-IDF scores |
| 120 | +- GNN could rank by proof-relevance semantics (same axioms, similar lemmas) |
| 121 | + |
| 122 | +### Implementation Steps |
| 123 | + |
| 124 | +1. **Data preparation** (`data_pipeline.jl` — NEW) |
| 125 | + - Load `proof_states_UNIFIED_BALANCED.jsonl` + `premises_COMPLETE.jsonl` |
| 126 | + - Normalize: split 80/10/10 (train/val/test) **stratified by prover system** (not random) |
| 127 | + - Encode: terms → token IDs using `premise_vocab.txt` |
| 128 | + - Output: three Julia DataFrames (train_df, val_df, test_df) serialized to JLD2 |
| 129 | + |
| 130 | +2. **Model architecture** (`models/gnn_model.jl` — NEW) |
| 131 | + - Graph neural network (7 node kinds, 8 edge kinds from proof graph) |
| 132 | + - Architecture: 3-layer message passing + global readout (32-dim embedding) |
| 133 | + - Flux.jl layers: GraphConv, BatchNorm, Dropout(0.2) |
| 134 | + - Loss: ranking loss (BPR or margin loss) on premise pairs |
| 135 | + - Why ranking loss: "is premise P₁ better than P₂ for this proof?" is the actual task |
| 136 | + |
| 137 | +3. **Training loop** (`src/julia/training/train_gnn.jl` — REPLACE current stub) |
| 138 | + - Hyperparameters: |
| 139 | + - Learning rate: 1e-3 (Adam) |
| 140 | + - Batch size: 32 (premise batches) |
| 141 | + - Epochs: 100 (with early stopping on val nDCG) |
| 142 | + - Dropout: 0.2 |
| 143 | + - Validation every 10 epochs: compute nDCG@10, MRR on val set |
| 144 | + - Early stopping: if val nDCG doesn't improve for 20 epochs, stop |
| 145 | + - Save best model (weights + architecture) to `models/gnn_trained_model.jld2` |
| 146 | + |
| 147 | +4. **Evaluation protocol** (`eval_gnn.jl` — NEW) |
| 148 | + - Metrics on test set: |
| 149 | + - **nDCG@10**: ideal ranking coefficient (is ground-truth premise in top-10?) |
| 150 | + - **MRR**: mean reciprocal rank (1/position of first-correct) |
| 151 | + - **Recall@5**: % of proofs where ground-truth premise appears in top-5 |
| 152 | + - Report: nDCG, MRR, Recall@5 per prover system (16 rows) |
| 153 | + - **Gate**: MRR ≥ 0.66, nDCG ≥ 0.60 for "verified" claim-type; else fallback to cosine |
| 154 | + |
| 155 | +5. **Integration** (`src/rust/gnn/model.rs` — modify) |
| 156 | + - Load trained model from `models/gnn_trained_model.jld2` at startup |
| 157 | + - Route request to Julia RPC endpoint (`api/gnn_endpoint.jl`) |
| 158 | + - Julia endpoint: |
| 159 | + ```rust |
| 160 | + POST /gnn/rank |
| 161 | + Input: { goal_id, premise_ids: [p1, p2, ...] } |
| 162 | + Output: { ranked_ids: [p_i, p_j, ...], scores: [0.92, 0.87, ...] } |
| 163 | + ``` |
| 164 | + - **Fallback**: if model missing/loading fails, use cosine similarity (no error) |
| 165 | + |
| 166 | +6. **Formal verification** (`src/abi/GnnIntegration.idr` — NEW) |
| 167 | + - Proof: If model achieves nDCG ≥ 0.60, then claim-type can be "verified" for GNN-guided proofs |
| 168 | + - Proof sketch: Lemma showing ranking guarantee (top-10 recall ≥ 0.6) |
| 169 | + - Reference existing properties in `src/abi/GnnProperties.idr` |
| 170 | + - Status: Use Parameter axiom (M11 pattern) for training convergence proof (external fact) |
| 171 | + |
| 172 | +7. **Tests** |
| 173 | + - Unit: Load trained model, rank premise set, check output shape |
| 174 | + - Integration: Submit proof with GNN-guided search; verify correct premise ranked high |
| 175 | + - Regression: All 638+ existing tests pass with new GNN model |
| 176 | + - Performance: Query latency < 100ms per premise set (benchmark w/ Criterion) |
| 177 | + |
| 178 | +### Success Criteria |
| 179 | +- ✅ Training completes on corpus (66k proofs, 4.7M premises) |
| 180 | +- ✅ Model achieves MRR ≥ 0.66, nDCG ≥ 0.60 on test set |
| 181 | +- ✅ Per-prover-system breakdown available (16 rows of metrics) |
| 182 | +- ✅ Integrated into dispatch.rs as preferred ranking strategy |
| 183 | +- ✅ Formal Idris2 proof of nDCG threshold guarantee |
| 184 | +- ✅ Fallback to cosine similarity if model unavailable |
| 185 | +- ✅ 638+ tests pass; no performance regressions |
| 186 | + |
| 187 | +### Files Modified/Created |
| 188 | +- `src/julia/training/data_pipeline.jl` — new |
| 189 | +- `src/julia/training/train_gnn.jl` — new (replaces stub) |
| 190 | +- `src/julia/training/eval_gnn.jl` — new |
| 191 | +- `models/gnn_model.jl` — new |
| 192 | +- `models/gnn_trained_model.jld2` — new (generated artifact) |
| 193 | +- `src/rust/gnn/model.rs` — load trained weights |
| 194 | +- `src/abi/GnnIntegration.idr` — new formal proof |
| 195 | +- `tests/gnn_ranking.rs` — new integration test |
| 196 | +- `benches/gnn_latency.rs` — new benchmark |
| 197 | + |
| 198 | +### Timeline |
| 199 | +- Data prep: 2-3 days (handle stragglers, outliers, normalization) |
| 200 | +- Model training & eval: 5-7 days (iterate on architecture, hyperparams) |
| 201 | +- Integration: 2-3 days (RPC endpoint, fallback, error handling) |
| 202 | +- Formal proofs: 2-3 days (Idris2, reference existing lemmas) |
| 203 | +- Testing & CI: 3-5 days |
| 204 | +- **Total**: 2-3 weeks (can be parallelized: data prep in parallel with formal proofs) |
| 205 | + |
| 206 | +### Known Risks |
| 207 | +- **Overfitting**: 16 prover systems × 10k examples/system is tight; use cross-validation |
| 208 | +- **Convergence**: GNN may not converge on ranking loss; have cosine fallback always ready |
| 209 | +- **Reproducibility**: Flux.jl + CUDA random seeds must be pinned for reproducible weights |
| 210 | +- **Maintenance**: Retrain if corpus grows significantly (> 100k proofs); document retraining protocol |
| 211 | + |
| 212 | +--- |
| 213 | + |
| 214 | +## Handoff Checklist |
| 215 | + |
| 216 | +Both specs are ready for Sonnet or Opus to execute: |
| 217 | + |
| 218 | +- [ ] Container Isolation (Spec 1) |
| 219 | + - Estimated 2-3 weeks, parallelizable (105 provers) |
| 220 | + - Critical path: trait → 3 POC → rollout → test |
| 221 | + - **Blocker for Stage 1 production release** |
| 222 | + |
| 223 | +- [ ] GNN Training (Spec 2) |
| 224 | + - Estimated 2-3 weeks, parallelizable (data/training/formal in parallel) |
| 225 | + - Rich integration: Julia ML + Rust dispatch + Idris2 proofs |
| 226 | + - **Blocker for Stage 3 quality gate** |
| 227 | + |
| 228 | +### Recommended Execution Order |
| 229 | +1. **Container Isolation first** (Stage 1 gate is critical) |
| 230 | +2. **GNN in parallel** (no dependencies on isolation; independent workstream) |
| 231 | +3. Merge both when ready; re-test full 638-test suite |
| 232 | + |
| 233 | +### Success Definition |
| 234 | +- Container: All 105 provers sandboxed, 638+ tests green, security test passing |
| 235 | +- GNN: Model trained (MRR ≥ 0.66), integrated, formal proof verified, benchmarks < 100ms |
0 commit comments