Skip to content

P-022 step 7b residual: Rust CLI for the .own and dev surfaces — cfg, summaries, explain, .own check; emit behind #257 #345

Description

@PhysShell

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:

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions