You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Master roadmap after the completion of P-022 step 4 in #214 / PR #249.
Reconciled at 0738d29 (PR #325), in the same move as the P-022 and index reconciliation — per this issue's own status-drift rule, the surfaces change together. Previous reconciliations: 984de7d (PR #324) and fdcb222 (PR #322). Statuses are checkpoint-level: each step names its completed checkpoints, its remaining acceptance, its normative blocker (what its acceptance actually requires) and this roadmap's preferred sequencing (what order is cheapest). Conflating the last two is what made the P-022 table drift twice.
How to keep parity work from drifting again is now written down: P-022 § Parity-work discipline. It is the single home for those rules — deliberately not copied elsewhere, because two copies of one law drift.
Two independent outcomes must now be delivered:
Owen Alpha is actually published and verified from clean external consumers.
Rust participates in the real C# → OwnIR → verdict path, first in shadow mode and then through an explicit cutover gate.
The release track must not wait for the full Rust migration. The Rust track must not use the release as permission to weaken parity.
OwnIR defensive limits (feat(ownir): bound source coordinates and nesting depth (Python-first) #326, Python-first, on main): signed-64 source coordinates and a 32-level nesting limit, normative in spec/OwnIR.md §4.2 and carried by spec/ownir.schema.json. The reference used to accept coordinates and depths no other consumer could represent — not generosity, but an accident of CPython's unbounded integers and deep stack leaking into the contract. Closed in the contract, not by widening Rust.
Explicitly not missing, because it was struck rather than deferred: a Rust .ownreport.json. See the #256 entry below.
Non-negotiable migration rules
Python remains the oracle until a separate cutover decision.
A Rust/Python divergence is a Rust bug unless behavior changes in a separate Python-first PR.
Migration PRs do not add diagnostic rules, change severity, broaden Roslyn heuristics, or weaken fixtures.
Every layer owns a frozen Python-authored parity surface; steady-state Rust tests run with zero Python.
Production crate edges remain CI-enforced.
own-codegen remains independent of own-analysis and own-diagnostics.
own-bridge feeds facts and maps verdicts; it must not duplicate analysis algorithms.
Owen Alpha publication does not depend on Rust-default cutover.
Parity work follows P-022 § Parity-work discipline — oracle over reviewer prose, mutation over plausible tests, no fail-fast during mutation campaigns, insertion-stable generated goldens.
.ownreport.json — described as carrying "schema/version, tool and run metadata, diagnostics and ordered Evidence"; measured, it is {module, buffers[]}, a buffer storage report where diagnostics contribute only four boolean checks. A faithful port needs ast_nodes + buffers.resolve, which the same issue's guardrail forbids own-diagnostics from reaching — and the project already refused that shape («НЕ перегружать .ownreport.json», AGENTS.execution-surfaces.md; "build_report untouched" in docs/tasks/evidence-coverage.md). Struck, not deferred: no cutover step needs a Rust buffer report.
"GitHub upload of Rust-generated SARIF" — the Rust workspace has no binary target and P-022 step 5b: port the SARIF projection with canonical parity #256 forbids CLI migration, so nothing can write a log. Replaced by asserting the structural rules an ingest enforces over the Rust-produced value, on top of byte-identity with the Python log CI already uploads.
1 — typed OwnIR validation: complete via PR feat(ownir): make the strict door accept the same language as the reference #325 — no known strict-door divergence. Three censuses. The first froze 77 controls and read 0/0/0 — then review found seven divergences the ledger could not express, because the same author wrote the ledger and the port and one gap in reading BR-D1 produced a matching gap in each. The second, derived from load() and obligations.py line by line, is 193 controls and opened a further 58 permissive documents and 9 category mismatches; 47 of the 58 were the obligation acceptance grammar, which load() calls and therefore owns. Closing them was architectural: a sequential raw-document validator reproducing BR-D1's per-section interleaving, with serde demoted to typed constructor and a guard asserting nothing escapes into it. The third admitted the two divergence families the second had measured and deliberately excluded, once feat(ownir): bound source coordinates and nesting depth (Python-first) #326 closed them Python-first — opening 7 more permissive documents and 8 more category mismatches. The defect under the mismatches is the one worth keeping: the ledger had been reading its category off the reference's diagnostic rather than off the mechanism, because _check_column raises one message for a bool, a string, a float, an out-of-range integer and a zero alike. Taxonomy is seven categories on two axes — Shape is "no representable primitive or container form", Location is "a representable coordinate violating its domain rule", WellFormedness covers records that are typed and vocabulary-legal and still cannot mean anything. Final: 216 controls, 35/181, 0/0/0, nothing excluded; 48 mutations across the three rounds, all caught.
2 — fact lowering: complete, 27/27 byte-exact.
3 — interprocedural MOS: complete for the stage-1 scalar-metadata domain, 35 goldens byte-exact. Container-valued metadata explicitly outside the domain.
4 — analysis wiring: not started; the crate declares "no diagnostics, no analysis".
The defensive limits that used to head this queue landed in #326, and their position was load-bearing rather than tidy: they changed what the reference accepts, so they had to land Python-first and cp1 had to be re-measured against them rather than merged beside them.
Production implementation remains blocked until the runtime marker/helper/escape-hatch contract is finalized. Do not duplicate call-site use-after-dispose rules.
Recommended agent allocation
Strong agent
#259 cp4 -> cp5
#255, #256, #258 and #259 cp1–cp3 are complete and drop out of this chain. #259 is already in flight — do not restart it from the top.
Separate strong or medium-strong agent
#257
Keep codegen isolated from analysis.
Medium agent
#252 verification packet
#253/#254 release evidence and external checks
A step is never described by a single Implemented/Missing bit. Any status edit to this issue or to docs/proposals/P-022-rust-core-migration.md must state, per step: completed checkpoints, remaining acceptance, normative blocker, and preferred sequencing. Both surfaces are updated in the same change — a reconciliation that touches only one of them replaces a stale pair with a contradictory pair. The proposals index row counts as a third surface for the same fact.
A child issue's own body is a fourth surface when its acceptance turns out to be wrong. #256 is the worked example: its requirements described a .ownreport.json the project does not have and had already refused to build, so the correction belongs in the issue, in this roadmap, in P-022 and in the index — together, or not at all.
Parity checkpoints carry one more surface, and it is not a status surface: the frozen ledger. A checkpoint whose acceptance is "two implementations agree" is proved by an artifact that can share the implementation's blind spots, and a green matrix over an incomplete ledger is indistinguishable from a green matrix over a complete one. #259 cp1 is the worked example, three times over — 0/0/0 over 77 controls, then 58 permissive documents and 9 category mismatches once the ledger was rebuilt from the reference instead of from the author's reading of it, then 7 more permissive documents and 8 more category mismatches once the two deliberately excluded families were admitted.
The third round added a second failure mode worth naming separately: a ledger can carry the right controls and still take its category from the wrong place. _check_column raises one message for five distinct mechanisms, and the ledger inherited one message as one category — so the classification was correct about accept/reject and wrong about why, for a year, in a file whose entire purpose is to be right about why.
The distinction matters: a ledger cannot report a normative blocker, a preferred sequencing or a remaining acceptance. It carries no project state at all. It is evidence for a claim the status surfaces make, so it is reviewed as evidence — is it derived from the reference or from the port, can it express absence, does every category have a control, and is any family excluded — and when it turns out to be incomplete, the correction is a further census with every result on the record, not a fix to the port.
For how to keep parity work honest — not just its status — see P-022 § Parity-work discipline.
Global PR acceptance packet
Every substantive child PR must state:
Scope:
Explicit non-goals:
Python source of truth:
Frozen fixture:
Fixture regeneration command:
Steady-state test command:
Production dependency changes:
Behavior changes:
Acceptance changes:
Local commands:
GitHub Actions links:
Known deferred cases:
Release smoke tests use project references/rebuilds instead of the packed .nupkg.
Python and Rust receive different OwnIR bytes in compare mode.
SARIF normalization deletes semantic fields merely to remove a diff.
Performance work begins without a baseline/profile.
A new abstraction layer has no vertical consumer.
Schema, semantics, output and packaging are mixed into one supposedly small PR.
A parity result is reported as complete while known divergence families sit outside the measured set. Excluding them is legitimate; calling the remainder "parity" is not.
A parity category is taken from where the reference raises its error rather than from the mechanism the document violated. One diagnostic covering several mechanisms is normal in a reference written for humans; inheriting it as one category makes the taxonomy decorative.
Note on (1) versus a corrected acceptance: striking a requirement because the tree proves it describes something that does not exist is not weakening it. The distinction is evidence — #256's strikes each carry a measurement, and the surfaces that stated the old acceptance were all corrected in the same move.
Milestone completion
This roadmap reaches its next major milestone when all are true:
Owen Alpha
installable from nuget.org on a clean machine;
owen check works on Windows and Linux;
immutable GitHub Action tag works from an external repository;
Status
Master roadmap after the completion of P-022 step 4 in #214 / PR #249.
Two independent outcomes must now be delivered:
The release track must not wait for the full Rust migration. The Rust track must not use the release as permission to weaken parity.
Current baseline
Completed:
Owen.Cli,owen, Owen Action/SARIF identity (feat(owen): public facade rebrand for the CLI, Action, and SARIF identity #246).own-ir,own-syntax,own-cfg,own-diagnostics,own-analysis,own-lowered,own-bridge.render/render_prettytext, the emission ordering contract (a stable sort on(line, code)), and a self-policing ledger over all 47TITLEScodes with missing/orphan/stale guards.own_diagnostics::sarifportsdiag_sarif.py+ the twoevidence.pybuilders as typed structs; 16 cases / 21 results replayed with zero Python, canonical comparison stripping zero volatile fields (empty list proven by census + determinism check), six mutations each caught under--no-fail-fast.spec/Bridge.md+spec/BridgeBehaviorMatrix.mdnormative and onmainvia PR spec(bridge): #258 — executable own-bridge contract (Bridge.md + behavior matrix) #297.rust_replaycases byte-exact.*.summaries.jsongoldens byte-identical, over the declared scalar-metadata parity domain.main): signed-64 source coordinates and a 32-level nesting limit, normative inspec/OwnIR.md§4.2 and carried byspec/ownir.schema.json. The reference used to accept coordinates and depths no other consumer could represent — not generosity, but an accident of CPython's unbounded integers and deep stack leaking into the contract. Closed in the contract, not by widening Rust.Still missing:
Owen.Cliand Action releases;own-codegen(P-022 step 5c: port own-codegen as an analysis-independent Rust sibling #257);own-bridgeremaining acceptance: analysis wiring (cp4, not started), full fact-to-verdict parity (cp5, unblocked but not done);Explicitly not missing, because it was struck rather than deferred: a Rust
.ownreport.json. See the #256 entry below.Non-negotiable migration rules
own-codegenremains independent ofown-analysisandown-diagnostics.own-bridgefeeds facts and maps verdicts; it must not duplicate analysis algorithms.Child issues and execution order
A. Owen Alpha release
Owen.Clialpha. Blocked by Owen Alpha gate: choose license, configure protected release environments, and approve version #252.Release DAG:
The release track is independent of the Rust-default cutover.
B. P-022 Rust production vertical
Each entry states its normative blocker first; preferred ordering is marked as such and is advisory.
.ownflow-diagnostic SARIF projection, byte-identical under a canonical comparison that normalizes only object key order and leaves every ordered array in place. Three of the issue's original requirements were struck as unbuildable as written, each verified by running the reference:.ownreport.json— described as carrying "schema/version, tool and run metadata, diagnostics and ordered Evidence"; measured, it is{module, buffers[]}, a buffer storage report where diagnostics contribute only four booleanchecks. A faithful port needsast_nodes+buffers.resolve, which the same issue's guardrail forbidsown-diagnosticsfrom reaching — and the project already refused that shape («НЕ перегружать.ownreport.json»,AGENTS.execution-surfaces.md; "build_reportuntouched" indocs/tasks/evidence-coverage.md). Struck, not deferred: no cutover step needs a Rust buffer report.DI004/DI005control — produced byownir.build_sarif, the OwnIR path excluded by the same issue's "no OwnIR bridge". Lands with P-022 step 6b: implement Rust own-bridge with layered OwnIR parity #259.own-codegen. Ready, independent of the analysis path; parallelizable.spec/Bridge.md+spec/BridgeBehaviorMatrix.mdmerged via PR spec(bridge): #258 — executable own-bridge contract (Bridge.md + behavior matrix) #297, onmain.own-bridge. In progress. Normative blocker P-022 step 6a: formalize OwnIR bridge semantics before the Rust port #258 satisfied; landed ahead of the preferred "after P-022 step 5a: port diagnostic messages and ordered Evidence with Python parity #255/P-022 step 5b: port the SARIF projection with canonical parity #256" ordering, which was advisory. Checkpoints:load()andobligations.pyline by line, is 193 controls and opened a further 58 permissive documents and 9 category mismatches; 47 of the 58 were the obligation acceptance grammar, whichload()calls and therefore owns. Closing them was architectural: a sequential raw-document validator reproducing BR-D1's per-section interleaving, withserdedemoted to typed constructor and a guard asserting nothing escapes into it. The third admitted the two divergence families the second had measured and deliberately excluded, once feat(ownir): bound source coordinates and nesting depth (Python-first) #326 closed them Python-first — opening 7 more permissive documents and 8 more category mismatches. The defect under the mismatches is the one worth keeping: the ledger had been reading its category off the reference's diagnostic rather than off the mechanism, because_check_columnraises one message for a bool, a string, a float, an out-of-range integer and a zero alike. Taxonomy is seven categories on two axes —Shapeis "no representable primitive or container form",Locationis "a representable coordinate violating its domain rule",WellFormednesscovers records that are typed and vocabulary-legal and still cannot mean anything. Final: 216 controls, 35/181, 0/0/0, nothing excluded; 48 mutations across the three rounds, all caught.own-clicommand/output/exit-code parity. Blocked by P-022 step 6b: implement Rust own-bridge with layered OwnIR parity #259; integrates with P-022 step 7a: add dual-engine shadow mode and zero-diff reproduction artifacts #260. The former "preferably after P-022 step 5b: port the SARIF projection with canonical parity #256" is now satisfied.Rust DAG (unchanged — normative dependencies only):
Preferred queue:#259 cp4 → cp5 → #260/#269.
The defensive limits that used to head this queue landed in #326, and their position was load-bearing rather than tidy: they changed what the reference accepts, so they had to land Python-first and cp1 had to be re-measured against them rather than merged beside them.
C. IDE/incremental path
.ownRust LSP MVP with broken-code tolerance and cancellation. P-022 step 5a: port diagnostic messages and ordered Evidence with Python parity #255 and P-022 step 5b: port the SARIF projection with canonical parity #256 satisfied; informed by IDE foundation: establish cold/warm/incremental latency and memory baselines #263.IDE DAG:
D. P-034
Production implementation remains blocked until the runtime marker/helper/escape-hatch contract is finalized. Do not duplicate call-site use-after-dispose rules.
Recommended agent allocation
Strong agent
#255,#256,#258and#259cp1–cp3 are complete and drop out of this chain.#259is already in flight — do not restart it from the top.Separate strong or medium-strong agent
Keep codegen isolated from analysis.
Medium agent
Local/corpus-capable agent
Status-drift rule
A step is never described by a single Implemented/Missing bit. Any status edit to this issue or to
docs/proposals/P-022-rust-core-migration.mdmust state, per step: completed checkpoints, remaining acceptance, normative blocker, and preferred sequencing. Both surfaces are updated in the same change — a reconciliation that touches only one of them replaces a stale pair with a contradictory pair. The proposals index row counts as a third surface for the same fact.A child issue's own body is a fourth surface when its acceptance turns out to be wrong. #256 is the worked example: its requirements described a
.ownreport.jsonthe project does not have and had already refused to build, so the correction belongs in the issue, in this roadmap, in P-022 and in the index — together, or not at all.Acceptance-evidence surface (parity checkpoints only)
Parity checkpoints carry one more surface, and it is not a status surface: the frozen ledger. A checkpoint whose acceptance is "two implementations agree" is proved by an artifact that can share the implementation's blind spots, and a green matrix over an incomplete ledger is indistinguishable from a green matrix over a complete one. #259 cp1 is the worked example, three times over — 0/0/0 over 77 controls, then 58 permissive documents and 9 category mismatches once the ledger was rebuilt from the reference instead of from the author's reading of it, then 7 more permissive documents and 8 more category mismatches once the two deliberately excluded families were admitted.
The third round added a second failure mode worth naming separately: a ledger can carry the right controls and still take its category from the wrong place.
_check_columnraises one message for five distinct mechanisms, and the ledger inherited one message as one category — so the classification was correct about accept/reject and wrong about why, for a year, in a file whose entire purpose is to be right about why.The distinction matters: a ledger cannot report a normative blocker, a preferred sequencing or a remaining acceptance. It carries no project state at all. It is evidence for a claim the status surfaces make, so it is reviewed as evidence — is it derived from the reference or from the port, can it express absence, does every category have a control, and is any family excluded — and when it turns out to be incomplete, the correction is a further census with every result on the record, not a fix to the port.
For how to keep parity work honest — not just its status — see P-022 § Parity-work discipline.
Global PR acceptance packet
Every substantive child PR must state:
Migration PRs additionally report:
The final value must be
0.Stop conditions
Stop and redesign the child scope if any of these occur:
own-bridgereimplements ownership/lifetime/effect/DI algorithms.own-codegenstarts consuming analysis verdicts..nupkg.Note on (1) versus a corrected acceptance: striking a requirement because the tree proves it describes something that does not exist is not weakening it. The distinction is evidence — #256's strikes each carry a measurement, and the surfaces that stated the old acceptance were all corrected in the same move.
Milestone completion
This roadmap reaches its next major milestone when all are true:
Owen Alpha
owen checkworks on Windows and Linux;Rust vertical
IDE foundation
.ownLSP proves cancellation and stale-result correctness;