diff --git a/docs/proposals/P-022-rust-core-migration.md b/docs/proposals/P-022-rust-core-migration.md index 6ac9082..837666a 100644 --- a/docs/proposals/P-022-rust-core-migration.md +++ b/docs/proposals/P-022-rust-core-migration.md @@ -1,10 +1,47 @@ # P-022 — Rust core migration: bird's-eye architecture -Status: **draft / exploratory** (design only — no Rust code committed yet; the -Python core stays the reference implementation and the oracle until parity holds). -Revised per the post-merge review in +Status: **in execution** (strangler-fig underway; the Python core stays the +reference implementation and the oracle until the explicit cutover). The design +rationale below is historical and unchanged; the live sequencing is the #250 +child-issue DAG. Revised per the post-merge review in [`docs/notes/p022-review-notes.md`](../notes/p022-review-notes.md). +### Implementation status (reconciled after #214/#249 — see #250/#251) + +**Implemented** (workspace members in `rust/Cargo.toml`, parity-gated by +`scripts/oracle_exact.py` and the shared fixtures in `tests/fixtures/`): + +- `own-ir` — OwnIR serde + schema round-trip (step 1); +- `own-syntax` — parser, error-text parity (step 2); +- `own-cfg` — lowering + the canonical CFG-JSON seam, replaying + `tests/fixtures/cfg_parity.json` (steps 0/3; the seam the strategy below + said "still needs building" **is built** — `python -m ownlang cfg --format + json` + the `--write`-regenerated parity fixtures); +- `own-diagnostics` — the data-only diagnostics layer; +- `own-analysis` — the worklist solver + ownership/lifetime/buffer/effect/DI + analyses (step 4; the **analysis-heart milestone**, completed in #214 / + PR #249, replaying `diag_parity.json` and the DI/effect fact-parity + fixtures). + +**Next steps — each owned by exactly one child issue under #250:** + +| Step | Deliverable | Issue | +|---|---|---| +| 5a | diagnostic messages + ordered Evidence parity | #255 | +| 5b | `.ownreport.json` + SARIF projection, canonical parity | #256 | +| 5c | `own-codegen` (analysis-independent sibling) | #257 | +| 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) | +| 6b | Rust `own-bridge`, layered OwnIR parity | #259 | +| 7a | dual-engine shadow mode + zero-diff reproduction artifacts | #260 (supported by #269 — normalized `AnalysisTrace` + first-divergence minimizer) | +| 7b | Rust `own-cli`: command/output/exit-code parity | #261 | +| 8 | Rust-default **cutover**, rollback gate, Python distribution removal | #262 | + +The Datalog/Ascent rule layer stays strictly **post-cutover** (strategy step 8 +below) and deliberately has no issue yet. Throughout: Python remains +authoritative until #262's cutover gate passes; a feature-freeze on +verdict-changing inference holds until then +([`interprocedural-roadmap.md`](../notes/interprocedural-roadmap.md)). + ## Why The primary trigger is the **IDE extension** — Gate B of @@ -477,37 +514,50 @@ SARIF/JSON shapes. So: misses. **Per-layer seams.** SARIF is the verdict-layer seam and already exists. A **CFG-layer -seam does not** — today `python -m ownlang cfg` prints a *human* dump (`_print_cfg`), -not a contract. So a prerequisite of diffing CFGs is to first **add and freeze a -canonical `cfg --format json` export on the Python side**; mirroring the debug text -dump would bake a non-contract format into the ratchet. Treat "CFG JSON seam" as work -to build, not an existing contract. With SARIF (verdict) present and CFG-JSON added, -a divergence can be bisected to the crate that introduced it. +seam did not** (at writing) — `python -m ownlang cfg` printed a *human* dump +(`_print_cfg`), not a contract. So a prerequisite of diffing CFGs was to first **add +and freeze a canonical `cfg --format json` export on the Python side**; mirroring the +debug text dump would bake a non-contract format into the ratchet. With SARIF (verdict) +present and CFG-JSON added, a divergence can be bisected to the crate that introduced +it. *(Status per #251: built and frozen — `cfg --format json` + +`tests/fixtures/cfg_parity.json`, replayed by `own-cfg`'s parity tests.)* Because the oracle compares *contracts we froze and tested*, the recent evidence/SARIF -hardening is what makes the verdict seam cheap — the CFG seam still needs building. +hardening is what made the verdict seam cheap — and the CFG seam has since been built +(step 0 ✅ above). ## Migration strategy (strangler-fig, bottom-up, oracle-gated) -0. **Add the missing Python seams first**: a canonical `cfg --format json` export - (and the exact diff harness). Without these the ratchet has nothing to compare the - CFG layer against. -1. **Stand up the workspace + `own-ir`** (serde round-trips the existing OwnIR +*(Status markers reconciled per #251; the plan text is otherwise as designed.)* + +0. ✅ **Add the missing Python seams first**: a canonical `cfg --format json` export + (and the exact diff harness, `scripts/oracle_exact.py`). Without these the ratchet + has nothing to compare the CFG layer against. +1. ✅ **Stand up the workspace + `own-ir`** (serde round-trips the existing OwnIR fixtures — first parity check, at the seam). -2. **`own-syntax`**: port the parser; diff the AST/`cfg` dump against Python. -3. **`own-cfg`**: port lowering; diff the frozen CFG JSON (from step 0). -4. **`own-analysis`**: port the worklist + ownership first, then lifetime/effect/DI; +2. ✅ **`own-syntax`**: port the parser; diff the AST/`cfg` dump against Python. +3. ✅ **`own-cfg`**: port lowering; diff the frozen CFG JSON (from step 0). +4. ✅ **`own-analysis`**: port the worklist + ownership first, then lifetime/effect/DI; diff diagnostics (no evidence) → then evidence → then SARIF, layer by layer. -5. **`own-diagnostics` + `own-codegen`**: SARIF/report/text and C# `emit`; diff each. + (#214 / PR #249 — the analysis heart; `own-diagnostics` shipped as its + data-only layer.) +5. **`own-diagnostics` (messages/Evidence — #255), report/SARIF (#256) + + `own-codegen` (#257)**: SARIF/report/text and C# `emit`; diff each. 6. **`own-bridge`**: port the OwnIR bridge — facts→core lowering, the MOS interprocedural inference, verdict mapping. **Prerequisite:** the normative write-up of the inference semantics (consume/borrow/fresh/alias/overwrite rules) from the tech-debt register, so the port has a spec and not just - `test_ownir.py` examples. Diff on the OwnIR fixtures + `ownir --format sarif`. -7. **`own-cli`**: cut over once corpus parity is ~100%. Keep Python frozen as the + `test_ownir.py` examples — written as `spec/Bridge.md` + + `spec/BridgeBehaviorMatrix.md` (#258, composing `spec/Inference.md`), + landing with PR #297 after independent review; implementation is #259 and + starts only after that review gate. Diff on the OwnIR fixtures + + `ownir --format sarif`. +7. **`own-cli`**: cut over once corpus parity is ~100% (shadow mode #260 with + #269's AnalysisTrace, then the CLI #261). Keep Python frozen as the oracle/spec. 8. **Only then** revisit the rule layer as Datalog/Ascent (ADR §8: "core moves to - Rust" trigger now satisfied) — natively, not as a Python detour. + Rust" trigger now satisfied) — natively, not as a Python detour. The formal + cutover + rollback gate + Python-distribution removal is #262. Throughout, Python stays authoritative; the Rust crates light up behind the ratchet. @@ -541,6 +591,9 @@ Throughout, Python stays authoritative; the Rust crates light up behind the ratc ## Placement -This document lives in `PhysShell/Own.NET/docs/proposals/`. It is design-only; the -first code deliverable is the workspace skeleton + `own-ir` round-trip + the oracle -harness, on its own branch, gated by the differential ratchet from commit one. +This document lives in `PhysShell/Own.NET/docs/proposals/`. The document is +design + status; the first code deliverable it called for — the workspace +skeleton + `own-ir` round-trip + the oracle harness, gated by the differential +ratchet from commit one — has shipped (`rust/`, `scripts/oracle_exact.py`; see +the implementation-status block at the top). The monorepo layout it recommends +is the one in effect. diff --git a/docs/proposals/README.md b/docs/proposals/README.md index 1a15d9d..e936b80 100644 --- a/docs/proposals/README.md +++ b/docs/proposals/README.md @@ -41,7 +41,7 @@ proposal is marked `done` with a pointer. | [P-017](P-017-multi-stack-frontends.md) | Multi-stack frontends (OwnTS / OwnJVM: OwnJava + OwnKotlin) | draft | | [P-020](P-020-ownts-react-effects.md) | OwnTS React effects profile (`Own.React`) — the effect-storm angle | draft | | [P-021](P-021-async-audit-pack.md) | Async audit pack (`Own.Async`) | draft | -| [P-022](P-022-rust-core-migration.md) | Rust core migration: crate DAG, patterns, prior art, differential oracle (Python = golden) | draft / exploratory | +| [P-022](P-022-rust-core-migration.md) | Rust core migration: crate DAG, patterns, prior art, differential oracle (Python = golden) | in execution — steps 0–4 built (`own-ir`/`own-syntax`/`own-cfg`/`own-diagnostics`/`own-analysis`, #214/#249); remaining steps = the #250 child-issue DAG (#255–#262); Python authoritative until cutover | | [P-023](P-023-architecture-guard.md) | Architecture guard (`Own.Arch`): rules.yaml intent model + dependency-graph gate + baseline ratchet | draft | | [P-024](P-024-security-audit-profile.md) | Security audit profile (external tools + SARIF adapters; rejects own scanner engine) | draft | | [P-025](P-025-obligation-protocols.md) | Obligation protocols (`Own.Protocols`): barrier-sensitive project invariants (OBL001–005) | first slice built (core + bridge + fixtures; extractor pending) |