|
| 1 | +<!-- |
| 2 | +SPDX-License-Identifier: MPL-2.0 |
| 3 | +SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
| 4 | +--> |
| 5 | + |
| 6 | +# Cross-Repo Proof Dependency DAG |
| 7 | + |
| 8 | +**Status**: Design draft — implementation not yet started. |
| 9 | +**Closes**: estate blocker **C14** from the 2026-06-03 audit |
| 10 | +("when ephapax `formal/` changes, downstream consumers don't re-verify"). |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## Problem |
| 15 | + |
| 16 | +The estate carries several producer/consumer proof relationships that |
| 17 | +the current per-repo CI shape does not honour: |
| 18 | + |
| 19 | +| Producer | Path | Known consumers | |
| 20 | +|-----------------|-------------------------------|---------------------------------------------------| |
| 21 | +| `ephapax` | `formal/PRESERVATION-*.v` | `valence-shell`, `verisimdb`, `proven` | |
| 22 | +| `echo-types` | `formal/echo/*.v` | `ephapax` (L3 obligations), `valence-shell` | |
| 23 | +| `kategoria` | `formal/*.v` | `verisimdb`, downstream HP type-checker repos | |
| 24 | +| `tropical-resource-typing` | `formal/*.v` | `verisimdb` (Q1 foundation) | |
| 25 | +| `vcl-ut` | `formal/*.v` | `verisimdb` (P3, V2 foundation pack) | |
| 26 | + |
| 27 | +Today, when a producer's proofs change, no automatic signal walks the |
| 28 | +DAG to re-verify downstream. Transitive correctness drift goes |
| 29 | +unnoticed until a manual reviewer notices, or until a consumer's own CI |
| 30 | +happens to re-run against the new producer SHA. That can be weeks. |
| 31 | + |
| 32 | +This is **C14** in the 2026-06-03 audit's punch list. |
| 33 | + |
| 34 | +--- |
| 35 | + |
| 36 | +## Scope (this doc) vs out-of-scope |
| 37 | + |
| 38 | +In-scope: |
| 39 | +- Where the DAG lives. |
| 40 | +- Edge format (TOML / A2ML). |
| 41 | +- Edge-discovery rules (manual + automatic). |
| 42 | +- Trigger semantics (which webhook fires which downstream jobs). |
| 43 | +- Failure semantics (degraded mode when a consumer can't be reached). |
| 44 | + |
| 45 | +Out-of-scope: |
| 46 | +- Term-level proof translation between Coq ↔ Lean ↔ Agda — that is |
| 47 | + cross-prover canonicalisation, blocked on `verisimdb#3`. |
| 48 | +- Per-theorem dependency tracking (i.e. "only re-verify proofs whose |
| 49 | + imports actually touched the producer hunk"). v1 is repo-grain only; |
| 50 | + per-theorem precision is a follow-up once the v1 plumbing is real. |
| 51 | + |
| 52 | +--- |
| 53 | + |
| 54 | +## Where the DAG lives |
| 55 | + |
| 56 | +**Recommendation: edges declared per-consumer; aggregation in echidna.** |
| 57 | + |
| 58 | +```text |
| 59 | + consumer repo echidna (estate dispatcher) |
| 60 | + ───────────── ───────────────────────────── |
| 61 | + .machine_readable/ ┌─────────────────┐ |
| 62 | + proof-deps.a2ml ───── ingest ───────► │ proof_dag.toml │ |
| 63 | + │ (synthesised │ |
| 64 | + consumer repo │ per-fetch) │ |
| 65 | + ───────────── │ │ |
| 66 | + .machine_readable/ └─────────┬───────┘ |
| 67 | + proof-deps.a2ml ───── ingest ───────► │ |
| 68 | + ▼ |
| 69 | + ┌─────────────────┐ |
| 70 | + │ hypatia │ |
| 71 | + │ (push fixes / │ |
| 72 | + │ enqueue jobs) │ |
| 73 | + └─────────┬───────┘ |
| 74 | + │ |
| 75 | + ▼ |
| 76 | + ┌─────────────────┐ |
| 77 | + │ echidnabot │ |
| 78 | + │ (per-repo CI) │ |
| 79 | + └─────────────────┘ |
| 80 | +``` |
| 81 | + |
| 82 | +Rationale: |
| 83 | + |
| 84 | +- **Edges live in the consumer**, not the producer. Producers shouldn't |
| 85 | + need to know who depends on them; the reverse direction (consumer |
| 86 | + declares what it pulls from) matches how Coq/Lean/Agda imports already |
| 87 | + work and means no producer-side coordination is needed when a new |
| 88 | + consumer onboards. |
| 89 | +- **Aggregation in echidna** because echidna is already the estate's |
| 90 | + prover-aware dispatcher (`src/rust/provers/`, prover-class scheduling, |
| 91 | + trust-bridge), and the dispatch decision needs prover metadata that |
| 92 | + echidnabot doesn't carry. |
| 93 | +- **Trigger orchestration in hypatia** because hypatia is the estate-CI |
| 94 | + intelligence layer that already coordinates `gitbot-fleet` and the |
| 95 | + "push committed fixes to remotes" contract (see the C15 PR). |
| 96 | + |
| 97 | +--- |
| 98 | + |
| 99 | +## Edge format |
| 100 | + |
| 101 | +A new file `proof-deps.a2ml` joins the existing |
| 102 | +`.machine_readable/bot_directives/` family: |
| 103 | + |
| 104 | +```toml |
| 105 | +# .machine_readable/proof-deps.a2ml |
| 106 | +schema_version = "1.0" |
| 107 | +directive_type = "proof-deps" |
| 108 | + |
| 109 | +# Each [[depends_on]] entry is one edge from this consumer to a producer. |
| 110 | +[[depends_on]] |
| 111 | +producer = "hyperpolymath/ephapax" |
| 112 | +ref = "main" # branch | tag | exact-sha |
| 113 | +paths = ["formal/PRESERVATION-*.v"] # producer-side globs |
| 114 | +local_uses = ["formal/L3-ECHO/**/*.v"] # consumer-side proofs that |
| 115 | + # transitively rely on producer |
| 116 | +rationale = "L3 echo obligations re-use ephapax's preservation_l3." |
| 117 | +# Optional gating override: |
| 118 | +fail_open = false # if true: producer red doesn't block consumer CI |
| 119 | + # (default false — producer red ⇒ consumer red). |
| 120 | + |
| 121 | +[[depends_on]] |
| 122 | +producer = "hyperpolymath/echo-types" |
| 123 | +ref = "main" |
| 124 | +paths = ["formal/echo/*.v"] |
| 125 | +local_uses = ["formal/L3-ECHO/EchoAxioms.v"] |
| 126 | +rationale = "Echo-types canonical axioms; echo-types audit recorded." |
| 127 | +``` |
| 128 | + |
| 129 | +Schema notes: |
| 130 | + |
| 131 | +- `producer` is `<owner>/<repo>` — platform-agnostic; same shape works |
| 132 | + for Codeberg / Radicle once those adapters land. |
| 133 | +- `ref` is normally `main`; per-edge SHA pins are allowed for |
| 134 | + reproducibility-critical consumers (e.g. release branches). |
| 135 | +- `paths` are producer-side globs — used both for edge invalidation |
| 136 | + ("did this hunk touch a tracked path?") and for the v2 per-theorem |
| 137 | + story. |
| 138 | +- `local_uses` documents *why* the edge exists. Mostly for humans, but |
| 139 | + hypatia surfaces it in the PR comment so reviewers know what failed. |
| 140 | +- Tolerance: unknown fields are ignored. Same forward-compat shape as |
| 141 | + the C12 manifest. |
| 142 | + |
| 143 | +--- |
| 144 | + |
| 145 | +## Discovery |
| 146 | + |
| 147 | +Two discovery paths feed the DAG: |
| 148 | + |
| 149 | +1. **Manual** — author commits `proof-deps.a2ml` declaring producers. |
| 150 | + This is the only path that v1 honours. |
| 151 | +2. **Automatic** (v2, after `verisimdb#3`) — extract producer references |
| 152 | + from Coq `Require Import`, Lean `import`, Agda `open import`. The |
| 153 | + estate's per-language imports vary too widely to do this safely in |
| 154 | + v1; a missing edge surfaces as a stale-proof failure within one |
| 155 | + producer-change cycle and the author adds it manually. |
| 156 | + |
| 157 | +The aggregator (`echidna ingest-proof-deps`) walks each repo's |
| 158 | +`proof-deps.a2ml` via the same `PlatformAdapter::get_file_contents` path |
| 159 | +echidnabot already uses for `bot_directives/echidnabot.a2ml`. Edges are |
| 160 | +cached for 24h with ETag revalidation. No edge → no behaviour change. |
| 161 | + |
| 162 | +--- |
| 163 | + |
| 164 | +## Trigger semantics |
| 165 | + |
| 166 | +```text |
| 167 | + producer push to tracked path |
| 168 | + │ |
| 169 | + ▼ |
| 170 | + webhook hits echidna ─────► consult ingested DAG, find consumers |
| 171 | + │ (forward edges by `paths` glob match) |
| 172 | + ▼ |
| 173 | + for each consumer: |
| 174 | + enqueue echidnabot job |
| 175 | + payload: { repo, ref, reason: "upstream change", |
| 176 | + upstream: { producer, sha, paths } } |
| 177 | + │ |
| 178 | + ▼ |
| 179 | + echidnabot runs that repo's proof check (per C12 manifest) |
| 180 | + │ |
| 181 | + ▼ |
| 182 | + result → comment on producer commit + open / update consumer issue |
| 183 | + "ephapax@abc1234 broke valence-shell@formal/L3-ECHO/EchoAxioms.v" |
| 184 | +``` |
| 185 | + |
| 186 | +Trigger details: |
| 187 | + |
| 188 | +- Path-grained: only producers' commits that touch a tracked `paths` |
| 189 | + glob fan out. Touching producer README, CI, or non-tracked source |
| 190 | + does not enqueue anything. |
| 191 | +- Coalesced: multiple pushes within a 60s window collapse to one |
| 192 | + downstream job per consumer. |
| 193 | +- Backpressure: hypatia's existing concurrency limits (per-repo |
| 194 | + semaphore + global cap) apply. Cascade fan-out is bounded. |
| 195 | +- Fail-open: if echidna's DAG cache is stale or unreachable, fall back |
| 196 | + to "no edges" — never block producer CI on the dispatcher being down. |
| 197 | + |
| 198 | +--- |
| 199 | + |
| 200 | +## Failure semantics |
| 201 | + |
| 202 | +| Condition | Behaviour | |
| 203 | +|--------------------------------------------|--------------------------------------------| |
| 204 | +| Consumer's `proof-deps.a2ml` is malformed | Log warning; treat as "no edges". No CI block on the consumer. | |
| 205 | +| Producer ref not reachable (deleted branch / private repo) | Edge skipped; warning surfaced in the consumer's next PR. | |
| 206 | +| Consumer CI red after upstream-triggered run | Issue opened in consumer repo, label `blocked-on:<producer>#<sha>`; `fail_open` overrides this to advisory-only. | |
| 207 | +| Producer red + `fail_open = false` | Consumer's `merge_block` (per C12 manifest) gates merges to consumer-main. | |
| 208 | +| DAG cycle (A → B → A) | Detected at ingest; cycle edge dropped + filed as `echidna#<issue>` with both repos labelled. v1 logs only; v2 hard-fails ingestion. | |
| 209 | + |
| 210 | +--- |
| 211 | + |
| 212 | +## Concrete walk-through |
| 213 | + |
| 214 | +```text |
| 215 | +2026-06-XX 10:00 ephapax main pushes commit abc1234 |
| 216 | + touching formal/PRESERVATION-L3.v |
| 217 | +
|
| 218 | + 10:00:02 echidna webhook receives push event |
| 219 | + matches DAG glob "formal/PRESERVATION-*.v" |
| 220 | + consumers: { valence-shell, verisimdb, proven } |
| 221 | +
|
| 222 | + 10:00:03 hypatia enqueues 3 echidnabot jobs: |
| 223 | + valence-shell @ main (proof-set: formal/L3-ECHO/**/*.v) |
| 224 | + verisimdb @ main (proof-set: formal/foundation-pack/**) |
| 225 | + proven @ main (proof-set: formal/echo-leak/*.v) |
| 226 | +
|
| 227 | + 10:00:08 echidnabot dispatches Coq jobs (per each repo's |
| 228 | + C12 manifest provers list) |
| 229 | +
|
| 230 | + 10:01:42 results land: |
| 231 | + valence-shell GREEN |
| 232 | + verisimdb RED (preservation_l3 fails on L2-modality lemma) |
| 233 | + proven GREEN |
| 234 | +
|
| 235 | + 10:01:44 echidna posts comment on ephapax abc1234: |
| 236 | + "Downstream impact: verisimdb red — preservation_l3 fails |
| 237 | + against L2-modality lemma at line 412." |
| 238 | + Opens issue verisimdb#NNN with full diff + the failing |
| 239 | + proof excerpt. Adds label "blocked-on:ephapax@abc1234". |
| 240 | +``` |
| 241 | + |
| 242 | +--- |
| 243 | + |
| 244 | +## Open decisions for the owner |
| 245 | + |
| 246 | +1. **Edge ref policy**: do all consumers default to `ref = "main"`, or |
| 247 | + should release-branch consumers be required to pin a SHA? Tradeoff: |
| 248 | + pinned SHAs are reproducible but go stale; `main` is current but can |
| 249 | + silently flip. |
| 250 | +2. **Issue spam vs PR comment**: when a producer's main breaks a |
| 251 | + consumer, do we open an issue per consumer (high signal, can pile |
| 252 | + up) or just comment on the producer commit (no follow-up nudge)? |
| 253 | +3. **Echo-types special case**: echo-types is the L3 canonical axioms |
| 254 | + producer for ephapax (per estate policy |
| 255 | + [[feedback_proofs_must_check_and_cross_doc_echo_types]]). Should |
| 256 | + echo-types ↔ ephapax edges be auto-injected by echidna without a |
| 257 | + `proof-deps.a2ml` declaration, or stay opt-in like everything else? |
| 258 | +4. **`fail_open` default**: producer red ⇒ consumer red is the safer |
| 259 | + default but it means an upstream's bad-day red-CI cascades. Should |
| 260 | + `fail_open = true` be the default and `fail_open = false` be the |
| 261 | + opt-in for hard-coupled consumers? |
| 262 | + |
| 263 | +--- |
| 264 | + |
| 265 | +## What this design unblocks |
| 266 | + |
| 267 | +Closing C14 makes three other audit blockers more tractable: |
| 268 | + |
| 269 | +- **C16** (GNN outcome-feedback loop) — per-edge success/failure |
| 270 | + signals are exactly the cross-repo training data the GNN currently |
| 271 | + doesn't see. |
| 272 | +- **F25** (cross-prover RDF alignment, blocked on `verisimdb#3`) — the |
| 273 | + DAG gives an empirical ground-truth set of co-evolving proof pairs, |
| 274 | + which is the input that translation work needs. |
| 275 | +- **D22** (no single green "trust pipeline verified" badge) — the DAG |
| 276 | + defines what "fully verified estate" means: every edge green. |
| 277 | + |
| 278 | +--- |
| 279 | + |
| 280 | +## Acceptance criteria for the implementation PR (out of scope here) |
| 281 | + |
| 282 | +When this design is implemented: |
| 283 | + |
| 284 | +- [ ] `echidna ingest-proof-deps` walks all repos that echidna already |
| 285 | + knows about, parses any `.machine_readable/proof-deps.a2ml`, |
| 286 | + and builds `proof_dag.toml` at the daemon level. |
| 287 | +- [ ] Producer webhooks fan out via `EdgeEnvelope` events on the |
| 288 | + existing `dispatch.rs` channel. |
| 289 | +- [ ] hypatia's enqueue path accepts `EdgeEnvelope` and routes to |
| 290 | + echidnabot exactly as it does for direct-push events. |
| 291 | +- [ ] Cycle detection runs at ingest and logs (v1) / hard-fails (v2). |
| 292 | +- [ ] Three estate-shaped fixtures: ephapax→valence-shell, |
| 293 | + echo-types→ephapax, kategoria→verisimdb. |
| 294 | +- [ ] Sad-path: unreachable producer ref, malformed `proof-deps.a2ml`, |
| 295 | + consumer mid-rebase. All degrade gracefully. |
| 296 | + |
| 297 | +--- |
| 298 | + |
| 299 | +## See also |
| 300 | + |
| 301 | +- C12 PR — per-repo manifest schema v2.0 (echidnabot). |
| 302 | +- C15 PR — hypatia push-fixes wiring (gitbot-fleet). |
| 303 | +- `docs/architecture/CORRECTNESS-ARCHITECTURE.md` — the soundness |
| 304 | + invariant the DAG must not violate. |
| 305 | +- `docs/architecture/VERISIM-ER-SCHEMA.md` — where ingested edges |
| 306 | + eventually persist for cross-prover analytics. |
0 commit comments