|
1 | 1 | # P-022 — Rust core migration: bird's-eye architecture |
2 | 2 |
|
3 | | -Status: **draft / exploratory** (design only — no Rust code committed yet; the |
4 | | -Python core stays the reference implementation and the oracle until parity holds). |
5 | | -Revised per the post-merge review in |
| 3 | +Status: **in execution** (strangler-fig underway; the Python core stays the |
| 4 | +reference implementation and the oracle until the explicit cutover). The design |
| 5 | +rationale below is historical and unchanged; the live sequencing is the #250 |
| 6 | +child-issue DAG. Revised per the post-merge review in |
6 | 7 | [`docs/notes/p022-review-notes.md`](../notes/p022-review-notes.md). |
7 | 8 |
|
| 9 | +### Implementation status (reconciled after #214/#249 — see #250/#251) |
| 10 | + |
| 11 | +**Implemented** (workspace members in `rust/Cargo.toml`, parity-gated by |
| 12 | +`scripts/oracle_exact.py` and the shared fixtures in `tests/fixtures/`): |
| 13 | + |
| 14 | +- `own-ir` — OwnIR serde + schema round-trip (step 1); |
| 15 | +- `own-syntax` — parser, error-text parity (step 2); |
| 16 | +- `own-cfg` — lowering + the canonical CFG-JSON seam, replaying |
| 17 | + `tests/fixtures/cfg_parity.json` (steps 0/3; the seam the strategy below |
| 18 | + said "still needs building" **is built** — `python -m ownlang cfg --format |
| 19 | + json` + the `--write`-regenerated parity fixtures); |
| 20 | +- `own-diagnostics` — the data-only diagnostics layer; |
| 21 | +- `own-analysis` — the worklist solver + ownership/lifetime/buffer/effect/DI |
| 22 | + analyses (step 4; the **analysis-heart milestone**, completed in #214 / |
| 23 | + PR #249, replaying `diag_parity.json` and the DI/effect fact-parity |
| 24 | + fixtures). |
| 25 | + |
| 26 | +**Next steps — each owned by exactly one child issue under #250:** |
| 27 | + |
| 28 | +| Step | Deliverable | Issue | |
| 29 | +|---|---|---| |
| 30 | +| 5a | diagnostic messages + ordered Evidence parity | #255 | |
| 31 | +| 5b | `.ownreport.json` + SARIF projection, canonical parity | #256 | |
| 32 | +| 5c | `own-codegen` (analysis-independent sibling) | #257 | |
| 33 | +| 6a | OwnIR **bridge semantics formalized** before the port | #258 (deliverable written — `spec/Bridge.md` + `spec/BridgeBehaviorMatrix.md` **land with PR #297**, in independent review; not on `main` yet) | |
| 34 | +| 6b | Rust `own-bridge`, layered OwnIR parity | #259 | |
| 35 | +| 7a | dual-engine shadow mode + zero-diff reproduction artifacts | #260 (supported by #269 — normalized `AnalysisTrace` + first-divergence minimizer) | |
| 36 | +| 7b | Rust `own-cli`: command/output/exit-code parity | #261 | |
| 37 | +| 8 | Rust-default **cutover**, rollback gate, Python distribution removal | #262 | |
| 38 | + |
| 39 | +The Datalog/Ascent rule layer stays strictly **post-cutover** (strategy step 8 |
| 40 | +below) and deliberately has no issue yet. Throughout: Python remains |
| 41 | +authoritative until #262's cutover gate passes; a feature-freeze on |
| 42 | +verdict-changing inference holds until then |
| 43 | +([`interprocedural-roadmap.md`](../notes/interprocedural-roadmap.md)). |
| 44 | + |
8 | 45 | ## Why |
9 | 46 |
|
10 | 47 | The primary trigger is the **IDE extension** — Gate B of |
@@ -477,37 +514,50 @@ SARIF/JSON shapes. So: |
477 | 514 | misses. |
478 | 515 |
|
479 | 516 | **Per-layer seams.** SARIF is the verdict-layer seam and already exists. A **CFG-layer |
480 | | -seam does not** — today `python -m ownlang cfg` prints a *human* dump (`_print_cfg`), |
481 | | -not a contract. So a prerequisite of diffing CFGs is to first **add and freeze a |
482 | | -canonical `cfg --format json` export on the Python side**; mirroring the debug text |
483 | | -dump would bake a non-contract format into the ratchet. Treat "CFG JSON seam" as work |
484 | | -to build, not an existing contract. With SARIF (verdict) present and CFG-JSON added, |
485 | | -a divergence can be bisected to the crate that introduced it. |
| 517 | +seam did not** (at writing) — `python -m ownlang cfg` printed a *human* dump |
| 518 | +(`_print_cfg`), not a contract. So a prerequisite of diffing CFGs was to first **add |
| 519 | +and freeze a canonical `cfg --format json` export on the Python side**; mirroring the |
| 520 | +debug text dump would bake a non-contract format into the ratchet. With SARIF (verdict) |
| 521 | +present and CFG-JSON added, a divergence can be bisected to the crate that introduced |
| 522 | +it. *(Status per #251: built and frozen — `cfg --format json` + |
| 523 | +`tests/fixtures/cfg_parity.json`, replayed by `own-cfg`'s parity tests.)* |
486 | 524 |
|
487 | 525 | Because the oracle compares *contracts we froze and tested*, the recent evidence/SARIF |
488 | | -hardening is what makes the verdict seam cheap — the CFG seam still needs building. |
| 526 | +hardening is what made the verdict seam cheap — and the CFG seam has since been built |
| 527 | +(step 0 ✅ above). |
489 | 528 |
|
490 | 529 | ## Migration strategy (strangler-fig, bottom-up, oracle-gated) |
491 | 530 |
|
492 | | -0. **Add the missing Python seams first**: a canonical `cfg --format json` export |
493 | | - (and the exact diff harness). Without these the ratchet has nothing to compare the |
494 | | - CFG layer against. |
495 | | -1. **Stand up the workspace + `own-ir`** (serde round-trips the existing OwnIR |
| 531 | +*(Status markers reconciled per #251; the plan text is otherwise as designed.)* |
| 532 | + |
| 533 | +0. ✅ **Add the missing Python seams first**: a canonical `cfg --format json` export |
| 534 | + (and the exact diff harness, `scripts/oracle_exact.py`). Without these the ratchet |
| 535 | + has nothing to compare the CFG layer against. |
| 536 | +1. ✅ **Stand up the workspace + `own-ir`** (serde round-trips the existing OwnIR |
496 | 537 | fixtures — first parity check, at the seam). |
497 | | -2. **`own-syntax`**: port the parser; diff the AST/`cfg` dump against Python. |
498 | | -3. **`own-cfg`**: port lowering; diff the frozen CFG JSON (from step 0). |
499 | | -4. **`own-analysis`**: port the worklist + ownership first, then lifetime/effect/DI; |
| 538 | +2. ✅ **`own-syntax`**: port the parser; diff the AST/`cfg` dump against Python. |
| 539 | +3. ✅ **`own-cfg`**: port lowering; diff the frozen CFG JSON (from step 0). |
| 540 | +4. ✅ **`own-analysis`**: port the worklist + ownership first, then lifetime/effect/DI; |
500 | 541 | diff diagnostics (no evidence) → then evidence → then SARIF, layer by layer. |
501 | | -5. **`own-diagnostics` + `own-codegen`**: SARIF/report/text and C# `emit`; diff each. |
| 542 | + (#214 / PR #249 — the analysis heart; `own-diagnostics` shipped as its |
| 543 | + data-only layer.) |
| 544 | +5. **`own-diagnostics` (messages/Evidence — #255), report/SARIF (#256) + |
| 545 | + `own-codegen` (#257)**: SARIF/report/text and C# `emit`; diff each. |
502 | 546 | 6. **`own-bridge`**: port the OwnIR bridge — facts→core lowering, the MOS |
503 | 547 | interprocedural inference, verdict mapping. **Prerequisite:** the normative |
504 | 548 | write-up of the inference semantics (consume/borrow/fresh/alias/overwrite rules) |
505 | 549 | from the tech-debt register, so the port has a spec and not just |
506 | | - `test_ownir.py` examples. Diff on the OwnIR fixtures + `ownir --format sarif`. |
507 | | -7. **`own-cli`**: cut over once corpus parity is ~100%. Keep Python frozen as the |
| 550 | + `test_ownir.py` examples — written as `spec/Bridge.md` + |
| 551 | + `spec/BridgeBehaviorMatrix.md` (#258, composing `spec/Inference.md`), |
| 552 | + landing with PR #297 after independent review; implementation is #259 and |
| 553 | + starts only after that review gate. Diff on the OwnIR fixtures + |
| 554 | + `ownir --format sarif`. |
| 555 | +7. **`own-cli`**: cut over once corpus parity is ~100% (shadow mode #260 with |
| 556 | + #269's AnalysisTrace, then the CLI #261). Keep Python frozen as the |
508 | 557 | oracle/spec. |
509 | 558 | 8. **Only then** revisit the rule layer as Datalog/Ascent (ADR §8: "core moves to |
510 | | - Rust" trigger now satisfied) — natively, not as a Python detour. |
| 559 | + Rust" trigger now satisfied) — natively, not as a Python detour. The formal |
| 560 | + cutover + rollback gate + Python-distribution removal is #262. |
511 | 561 |
|
512 | 562 | Throughout, Python stays authoritative; the Rust crates light up behind the ratchet. |
513 | 563 |
|
@@ -541,6 +591,9 @@ Throughout, Python stays authoritative; the Rust crates light up behind the ratc |
541 | 591 |
|
542 | 592 | ## Placement |
543 | 593 |
|
544 | | -This document lives in `PhysShell/Own.NET/docs/proposals/`. It is design-only; the |
545 | | -first code deliverable is the workspace skeleton + `own-ir` round-trip + the oracle |
546 | | -harness, on its own branch, gated by the differential ratchet from commit one. |
| 594 | +This document lives in `PhysShell/Own.NET/docs/proposals/`. The document is |
| 595 | +design + status; the first code deliverable it called for — the workspace |
| 596 | +skeleton + `own-ir` round-trip + the oracle harness, gated by the differential |
| 597 | +ratchet from commit one — has shipped (`rust/`, `scripts/oracle_exact.py`; see |
| 598 | +the implementation-status block at the top). The monorepo layout it recommends |
| 599 | +is the one in effect. |
0 commit comments