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
Parent: #250
Recommended tier: strong or medium-strong
Blocked by: nothing for cfg, summaries, explain and .own check — the crates they wire are complete (#255, #256, #259); emit is blocked by #257
Preferably after: #261 (the production OwnIR executable), so these commands reuse its argument and exit-code skeleton instead of growing a second one
Related: #261, #257, #256
Not on the #262 path: the cutover's CLI prerequisite is #261 alone (owner decision C-5, ratified 2026-09-08)
Why this issue exists
Split out of #261 by owner decision C-3 (ratified 2026-09-08, recorded in #261). #261 is the production-oriented Rust OwnIR executable — the step 7b gate that admits #262. The remaining surfaces of python -m ownlang have a different oracle, different users and no relevance to the cutover; keeping them inside #261 would have left a permanently half-open issue carrying four stages of different age. Under C-5 they remain a migration obligation for as long as P-022 is defined as the full surface migration rather than only the production vertical — and they do not gate #262.
Scope
Every command of python -m ownlang that the production seam does not call, except the ones struck or excluded below:
cfg — human and --format json (the json form is the CFG-layer simulacrum the oracle harness already reads);
Struck, not deferred:report (.ownreport.json), by #256 and owner decision C-2 — a buffer-storage report needing the AST and the policy resolver; no cutover step needs it, and the project had already refused that shape. Do not reopen it here.
Out of scope:ownir (that is #261); config and the whole own-fix family (P-035 / S2 orchestration over dotnet, Roslyn and git — not core analysis, not P-022); engine selection of any kind (owner decision C-4: a Rust executable is one engine; python/rust/compare orchestration lives in the dev driver now and in the launcher at #262's stages, never with a silent fallback).
Shape
Per P-022's crate plan there is one entry-point binary, own-cli (owner ruling, 2026-09-08). #261 creates it with the ownir subcommand; this issue adds the residual subcommands to that same binary and does not create a second one. Argument handlers own parsing, I/O and process exit; they hold no analysis, bridge or codegen logic.
Oracle — owner decision C-1 applies unchanged, with the boundary fixed for #261
The top-level own-cli shell follows the public owen convention: own-cli alone → help on stdout, exit 2; --help/-h → help on stdout, exit 0; --version → version on stdout, exit 0; an unknown command → one error line plus help on stderr, exit 2. The empty invocation and the unknown command are distinct cases. The help text is a cross-implementation parity surface of its own, not an invented Python contract. P-022 step 7b: the production Rust OwnIR executable (own-cli ownir) — command, output and exit-code parity behind the existing launcher #261 builds this shell; this issue only adds subcommands to it.
After a subcommand is selected, the oracle is python -m ownlang <command>as measured — semantics, stdout, stderr, artifacts, exit codes and messages of the command, its usage errors included — through Python-authored fixtures replayed in Rust with zero Python, unless that exact case is separately declared a defect by the owner. The reference's reaction to --help after a command (the argument read as a file; exit 70 for check, exit 2 for ownir) is one such declared defect and is not ported: <command> --help answers with the command's usage on stdout, exit 0, as P-022 step 7b: the production Rust OwnIR executable (own-cli ownir) — command, output and exit-code parity behind the existing launcher #261 does for ownir. The Python-first repair of the reference's --help is hygiene, not a prerequisite.
Exit-code rulings carried over from P-022 step 7b: the production Rust OwnIR executable (own-cli ownir) — command, output and exit-code parity behind the existing launcher #261's ratification: a catchable panic is exactly one actionable stderr diagnostic and exit 70 (the binary keeps panic = "unwind" with a top-level catch_unwind; a hook alone only observes); an uncatchable death stays a visible hard failure, never findings and never a clean exit, with no particular OS exit number contracted; emit exits 1 on ordinary diagnostics or a refusal before codegen (the reference refuses to generate on error diagnostics or a missing module) and 70 when a CodegenError escapes generate() or on any other unexpected exception; summaries exits 2 on an expected OwnIR refusal and 70 on an unexpected exception; SIGINT is measured on the Windows and Linux reference first — 130 is not invented as a universal contract.
Fixture contract
One Python-authored fixture per command: arguments, working-directory assumptions, input files, stdout, stderr, exit code, created artifacts. Normalize only temp-root prefixes where unavoidable; paths visible to users remain contractual. Rust steady-state replay runs with zero Python.
Required controls: help and version of the shell (shared with #261); an unknown command; per command, its usage errors as measured; a clean input → the reference's code; diagnostics found → the reference's non-zero code; unsupported or unreadable input → the reference's explicit code; Windows and Unix paths; spaces and non-ASCII in paths; deterministic output and artifact names; the panic and unexpected-failure paths above.
Guardrails
No new command semantics beyond the shell convention decided in C-1.
No diagnostic behaviour changes; no analysis, bridge or codegen logic in argument handlers.
Cargo DAG: only the edges a command needs — own-syntax, own-cfg, own-analysis, own-diagnostics for the .own path; own-ir, own-lowered, own-bridge for summaries, as an entry-point crate is allowed to; own-codegen only in the emit slice and only after P-022 step 5c: port own-codegen as an analysis-independent Rust sibling #257. No new edge anywhere else.
No public cutover, no Python removal, no engine selection, no report.
Acceptance
cfg parity green (human and json) on Windows and Linux CI, zero Python in the replay.
summaries parity green under the 2 / 70 ruling.
explain parity green over the whole code catalogue, unknown code included.
Parent: #250
Recommended tier: strong or medium-strong
Blocked by: nothing for
cfg,summaries,explainand.own check— the crates they wire are complete (#255, #256, #259);emitis blocked by #257Preferably after: #261 (the production OwnIR executable), so these commands reuse its argument and exit-code skeleton instead of growing a second one
Related: #261, #257, #256
Not on the #262 path: the cutover's CLI prerequisite is #261 alone (owner decision C-5, ratified 2026-09-08)
Why this issue exists
Split out of #261 by owner decision C-3 (ratified 2026-09-08, recorded in #261). #261 is the production-oriented Rust OwnIR executable — the step 7b gate that admits #262. The remaining surfaces of
python -m ownlanghave a different oracle, different users and no relevance to the cutover; keeping them inside #261 would have left a permanently half-open issue carrying four stages of different age. Under C-5 they remain a migration obligation for as long as P-022 is defined as the full surface migration rather than only the production vertical — and they do not gate #262.Scope
Every command of
python -m ownlangthat the production seam does not call, except the ones struck or excluded below:cfg— human and--format json(the json form is the CFG-layer simulacrum the oracle harness already reads);summaries— the MOS summary dump; its content is already byte-exact in Rust at P-022 step 6b: implement Rust own-bridge with layered OwnIR parity #259 cp3, so this is the command shell around it;explain— the what/why/fix catalogue: a port of text, not of logic; an unknown code exits with the reference's code;.own check— human and SARIF over a.ownsource file;emit— own-codegen output; blocked by P-022 step 5c: port own-codegen as an analysis-independent Rust sibling #257 and lands last.Struck, not deferred:
report(.ownreport.json), by #256 and owner decision C-2 — a buffer-storage report needing the AST and the policy resolver; no cutover step needs it, and the project had already refused that shape. Do not reopen it here.Out of scope:
ownir(that is #261);configand the wholeown-fixfamily (P-035 / S2 orchestration over dotnet, Roslyn and git — not core analysis, not P-022); engine selection of any kind (owner decision C-4: a Rust executable is one engine; python/rust/compare orchestration lives in the dev driver now and in the launcher at #262's stages, never with a silent fallback).Shape
Per P-022's crate plan there is one entry-point binary,
own-cli(owner ruling, 2026-09-08). #261 creates it with theownirsubcommand; this issue adds the residual subcommands to that same binary and does not create a second one. Argument handlers own parsing, I/O and process exit; they hold no analysis, bridge or codegen logic.Oracle — owner decision C-1 applies unchanged, with the boundary fixed for #261
own-clishell follows the publicowenconvention:own-clialone → help on stdout, exit 2;--help/-h→ help on stdout, exit 0;--version→ version on stdout, exit 0; an unknown command → one error line plus help on stderr, exit 2. The empty invocation and the unknown command are distinct cases. The help text is a cross-implementation parity surface of its own, not an invented Python contract. P-022 step 7b: the production Rust OwnIR executable (own-cli ownir) — command, output and exit-code parity behind the existing launcher #261 builds this shell; this issue only adds subcommands to it.python -m ownlang <command>as measured — semantics, stdout, stderr, artifacts, exit codes and messages of the command, its usage errors included — through Python-authored fixtures replayed in Rust with zero Python, unless that exact case is separately declared a defect by the owner. The reference's reaction to--helpafter a command (the argument read as a file; exit 70 forcheck, exit 2 forownir) is one such declared defect and is not ported:<command> --helpanswers with the command's usage on stdout, exit 0, as P-022 step 7b: the production Rust OwnIR executable (own-cli ownir) — command, output and exit-code parity behind the existing launcher #261 does forownir. The Python-first repair of the reference's--helpis hygiene, not a prerequisite.own-cli ownir) — command, output and exit-code parity behind the existing launcher #261's ratification: a catchable panic is exactly one actionable stderr diagnostic and exit 70 (the binary keepspanic = "unwind"with a top-levelcatch_unwind; a hook alone only observes); an uncatchable death stays a visible hard failure, never findings and never a clean exit, with no particular OS exit number contracted;emitexits 1 on ordinary diagnostics or a refusal before codegen (the reference refuses to generate on error diagnostics or a missing module) and 70 when aCodegenErrorescapesgenerate()or on any other unexpected exception;summariesexits 2 on an expected OwnIR refusal and 70 on an unexpected exception; SIGINT is measured on the Windows and Linux reference first — 130 is not invented as a universal contract.Fixture contract
One Python-authored fixture per command: arguments, working-directory assumptions, input files, stdout, stderr, exit code, created artifacts. Normalize only temp-root prefixes where unavoidable; paths visible to users remain contractual. Rust steady-state replay runs with zero Python.
Required controls: help and version of the shell (shared with #261); an unknown command; per command, its usage errors as measured; a clean input → the reference's code; diagnostics found → the reference's non-zero code; unsupported or unreadable input → the reference's explicit code; Windows and Unix paths; spaces and non-ASCII in paths; deterministic output and artifact names; the panic and unexpected-failure paths above.
Guardrails
own-syntax,own-cfg,own-analysis,own-diagnosticsfor the.ownpath;own-ir,own-lowered,own-bridgeforsummaries, as an entry-point crate is allowed to;own-codegenonly in theemitslice and only after P-022 step 5c: port own-codegen as an analysis-independent Rust sibling #257. No new edge anywhere else.report.Acceptance
cfgparity green (human and json) on Windows and Linux CI, zero Python in the replay.summariesparity green under the 2 / 70 ruling.explainparity green over the whole code catalogue, unknown code included..own checkparity green (human and SARIF).emitparity green under the 1 / 70 ruling — after P-022 step 5c: port own-codegen as an analysis-independent Rust sibling #257.own-cli ownir) — command, output and exit-code parity behind the existing launcher #261); every case after a command is pinned as measured.