|
| 1 | +# OX-01 — Owen extension substrate with Typed Builder as extension #1: report |
| 2 | + |
| 3 | +**VERDICT: `GO_OWEN_EXTENSION_ALPHA`.** |
| 4 | + |
| 5 | +| | commit | |
| 6 | +|---|---| |
| 7 | +| base (`main`, TB-MVP-01 merged, #391) | `88cb8cc31c49d730290613208947102d0737cad2` | |
| 8 | +| preregistration + Snipper audit | `49f5493` ([`owen-extension-alpha-preregistration.md`](owen-extension-alpha-preregistration.md)) | |
| 9 | +| implementation + Amendment 1 (before any official run) | `6e65bfb` | |
| 10 | +| gate fix (Windows: paths on two drives; gate mechanics only) | `89bdb86` | |
| 11 | +| notes, E2 spike | `48c71e6` | |
| 12 | +| this report | the head of the PR | |
| 13 | + |
| 14 | +## What a user does now |
| 15 | + |
| 16 | +``` |
| 17 | +dotnet new web -n OrderBackend |
| 18 | +dotnet add package Owen.TypedBuilder --version 0.1.0 |
| 19 | +dotnet build |
| 20 | +``` |
| 21 | + |
| 22 | +1. **Generation.** The typed API is generated on every compilation by a Roslyn incremental generator: `DraftOrder.Submit`, `OrderProtocol.WithDraft`, `Order.Create().Customer(…).Build()`. |
| 23 | +2. **Compiler errors.** An illegal transition is a compiler error: `CS1061 'DraftOrder' does not contain a definition for 'Approve'`. |
| 24 | +3. **Owen findings on build.** A stale state token is an Owen finding, reported by the build in the canonical MSBuild form and so in Visual Studio's Error List: |
| 25 | + `…/C8_stale_draft.cs(18): warning OWN002: …` |
| 26 | + It is a `warning` by default and an `error` with `OwenSeverity=error`. |
| 27 | +4. **The project names its extensions** in `obj/owen/extensions.json`. |
| 28 | + |
| 29 | +None of it needs Python, a Rust toolchain, a global `owen`, a checkout, an environment |
| 30 | +variable, or a PATH lookup of an Owen binary. |
| 31 | + |
| 32 | +## Architecture as built |
| 33 | + |
| 34 | +``` |
| 35 | +Owen.TypedBuilder (package, extension #1) |
| 36 | + ├─ analyzers/: Owen.TypedBuilder.Generator — Roslyn incremental generator |
| 37 | + │ (shares TypedBuilderCore.cs with the CLI generator: one model, one renderer) |
| 38 | + ├─ build*/: owen-extension.json + <OwenExtensionDescriptor> item |
| 39 | + └─ depends on ─────────────────────────────┐ |
| 40 | + ▼ |
| 41 | +Owen.Build (package, the GENERIC host: names no extension) |
| 42 | + ├─ build*/Owen.Build.targets: after the build → `owen build-check` |
| 43 | + └─ tools/net8.0/any/: the Owen.Cli program + bundled extractor + rust-core/<platform>/own-cli |
| 44 | + │ |
| 45 | + ▼ |
| 46 | + the authoritative Rust core (`own-cli ownir --format msbuild`) |
| 47 | +``` |
| 48 | + |
| 49 | +- **No `Owen.Toolchain` or `Owen.CoreAssets`.** `Owen.Build` is the one host package. It exists because a tool package cannot be a `PackageReference` (NU1212, observed) and because every extension must share one engine. |
| 50 | +- **The platform inventory** lives in one `frontend/roslyn/OwenRustCore.props`, imported by both `Owen.Cli` and `Owen.Build`. |
| 51 | +- **The host entry point** is a new `owen build-check` subcommand of the same program. It reuses `owen check`'s extractor and engine runner; it neither copies them nor renders findings. |
| 52 | +- **The contract:** [`spec/OwenExtension.md`](../../spec/OwenExtension.md) and its JSON schema. |
| 53 | +- **Generator delivery: A**, a Roslyn incremental generator, chosen on measurement (preregistration K-3). B was absent from the design-time build that Visual Studio's IntelliSense uses. |
| 54 | + |
| 55 | +## Official runs |
| 56 | + |
| 57 | +| run | where | result | |
| 58 | +|---|---|---| |
| 59 | +| `scripts/owen_extension_gate.py` from a fresh `git worktree` of `6e65bfb` (no `bin/`/`obj/`) | local Linux x64 | **55/55 PASS** | |
| 60 | +| the same gate on `6e65bfb` | server CI `owen-extension`, ubuntu-latest | **55/55 PASS** | |
| 61 | +| the same gate on `6e65bfb` | server CI `owen-extension`, windows-latest | **FAIL before any check.** The gate's own outside-checkout test called `os.path.commonpath` on a `D:` checkout and a `C:` temp directory and raised. Fixed in `89bdb86`, gate mechanics only. | |
| 62 | +| the same gate on `48c71e6` | server CI `owen-extension`, ubuntu-latest | **55/55 PASS** | |
| 63 | +| the same gate on `48c71e6` | server CI `owen-extension`, windows-latest | **55/55 PASS**: the `win-x64` core, absolute `C:\…\C8_stale_draft.cs(18)` origins, Exec through `cmd` | |
| 64 | +| server CI run 37149198078 (`48c71e6`) | all jobs | **36/36 success**, including `owen CLI (gate A)` on both platforms, `state protocols` on both, lint, tests ×3, rust, formal kernel | |
| 65 | + |
| 66 | +## Counts (Linux official run) |
| 67 | + |
| 68 | +| | | |
| 69 | +|---|---| |
| 70 | +| packages | 2 shipped (`Owen.Build`, `Owen.TypedBuilder`) + 1 synthetic test extension | |
| 71 | +| isolation | consumer PATH = the `dotnet` dir + `sh` only; `python`/`python3`/`py`/`cargo`/`rustc`/`owen` resolve to nothing; fresh `HOME`/`NUGET_PACKAGES`; `Owen.*` mapped to the candidate feed only; the consumer outside the checkout; no checkout path in any build output | |
| 72 | +| A | build ok; the generated `Order.Protocol.g.cs` = `#nullable enable\n` + the committed golden (6880 bytes); the manifest lists `Owen.TypedBuilder 0.1.0` with its 4 capabilities; the clean sample has no Owen diagnostic | |
| 73 | +| B | `draft.Approve(…)` → CS1061 `'Approve'` | |
| 74 | +| C | `OwenSeverity=error` → `…/C8_stale_draft.cs(18): error OWN002: …`, build fails | |
| 75 | +| D | the origin is the **absolute** path of the staged file, line 18; the default severity gives the same line as `warning OWN002`, and the build succeeds | |
| 76 | +| F | **30/30** corpus cases through the package: compiler-stage CS codes, extractor refusals as `OWENB010`, core refusals as `OWENB011`, verdicts `OWN002`/`OWN005`/`OWN013`, positives clean, the P5/P8 `proven_call` present, K1 `OWN001`, K2 clean | |
| 77 | +| G | the TB-MVP acceptance runner against the package-built consumer: **44/44**, transcript **byte-identical** to `samples/OrderBackend/evidence/acceptance.txt` | |
| 78 | +| H | `Owen.TestExtension` beside `Owen.TypedBuilder`: the manifest lists both, and the project is analysed once (1 `OWN002`) | |
| 79 | +| M1a | the declared descriptor deleted → `OWENB002`, no `OWN002` | |
| 80 | +| M1b | the whole declaration deleted → `OWENB001` ("no Owen extension is active"), no `OWN002` | |
| 81 | +| M2 | unknown capability `teleportation` → `OWENB004` | |
| 82 | +| M3 | the bundled `own-cli` deleted → `OWENB005` (never a clean build) | |
| 83 | +| M4 | `requires.ownir: 3` → `OWENB003`; `requires.host: 9.0.0` → `OWENB003` | |
| 84 | +| M5 | the host target emptied → no `OWN002`, so the C check catches the absence | |
| 85 | +| M6 | consumer with no checkout → E holds | |
| 86 | +| M7 | the host sources (`Owen.Build/**`, `BuildCheckCommand.cs`, `OwenRustCore.props`) contain `TypedBuilder` 0 times | |
| 87 | + |
| 88 | +## Existing guarantees kept |
| 89 | + |
| 90 | +On the implementation commit: |
| 91 | +- `typed_builder_gate.py --rust`: 8/20/2 corpus, 44 checks ×2, transcript `ba447304cdf957ee`, 0 failures. The CLI generator now runs on the shared core and still writes the golden byte for byte. |
| 92 | +- `protocol_gate.py --rust`: 0 failures, 29 documents byte-identical on both CLIs. |
| 93 | +- `heap_effects_gate.py`: unchanged; the extractor is untouched. |
| 94 | +- `ruff` and `mypy` clean. |
| 95 | + |
| 96 | +**`tests/run_tests.py` locally:** |
| 97 | +- `fresh-record-valid` fails only on a dirty tree, by design. |
| 98 | +- Two stage-1 controls (`divergence-is-5`, `exec-failure-is-5`) fail **on the owen surface only**, and only once a launcher is built locally. In the base run they were skipped ("no built launcher"). A launcher built from the **base** commit `88cb8cc3` fails them identically, so this is pre-existing and not caused by this change. The server CI is authoritative for it. |
| 99 | + |
| 100 | +**Foundations.** |
| 101 | +`git diff --stat 88cb8cc3..HEAD -- ownlang rust spec/OwnIR.md spec/Bridge.md spec/ownir.schema.json frontend/roslyn/OwnSharp.Extractor docs/evidence/calibration scripts/perf_baseline.py` |
| 102 | +is **empty**. No OwnIR, ownership, state-protocol, H0/H1, T0, P-022 harness, `OWN` code or core |
| 103 | +change. |
| 104 | + |
| 105 | +**`Owen.Cli` payload** (Amendment 1.2), measured against a pack of the base commit with the |
| 106 | +same staged core: |
| 107 | +- identical **file list**, 109 entries; |
| 108 | +- every Roslyn assembly, `deps.json`/`runtimeconfig.json`, vendored `.py` and the Rust core **byte-identical**; |
| 109 | +- only `ownsharp.dll`/`.pdb` (gains `build-check`), `ownsharp-extract.dll`/`.pdb` (commit-stamped build metadata; source unchanged) and the nuspec `repository commit` differ. |
| 110 | + |
| 111 | +Gate A (`owen-cli` install → `owen check` → `OWN001`) runs in server CI on both platforms. |
| 112 | + |
| 113 | +## Snipper salvage (P1) |
| 114 | + |
| 115 | +[`owen-extension-snipper-salvage.md`](owen-extension-snipper-salvage.md), read-only at `43b395a`, MIT. **For this slice:** |
| 116 | +- **REJECT** its binary discovery (setting → bundled → PATH with silent fall-through), the long-lived VS process lifecycle, and the Roslyn sidecar. |
| 117 | +- **ADAPT** pack-time bundling with a hard fail, already `OwenRustCore.props`, and one-shot child discipline. MSBuild's Exec kills the child tree on cancellation; `owen` drains both streams and maps exit codes. |
| 118 | +- **Later only:** StreamJsonRpc framing (COPY), mocked-VS and two-pipe fake-server tests, the thin VS Code client shape. |
| 119 | + |
| 120 | +## Diagnostics model (P8) |
| 121 | + |
| 122 | +[`owen-diagnostics-model.md`](owen-diagnostics-model.md). The model already exists: |
| 123 | +`own_bridge::Finding` → `render_finding` (human/github/msbuild) / `build_sarif`. No new layer |
| 124 | +was built. The host only absolutises the origin path. |
| 125 | + |
| 126 | +**Recorded gaps, not changed:** |
| 127 | +- the msbuild line has no column although `Finding` has one; |
| 128 | +- the state-protocol wording and line (#393). |
| 129 | + |
| 130 | +## IDE (P9/P10) |
| 131 | + |
| 132 | +[`owen-extension-ide-feasibility.md`](owen-extension-ide-feasibility.md): |
| 133 | +- **E1 (generic VSIX): NOT EXECUTED.** There is no Windows or Visual Studio here. It is specified, with its two blockers: extractor overlay input for unsaved buffers, and a resident process. |
| 134 | +- **E2 (native Roslyn analyzer): REJECT.** Measured on Linux: |
| 135 | + - shadow copy defeats default native resolution; |
| 136 | + - only an explicit path through reflection-reached `NativeLibrary` works, and only on .NET-hosted compilers; |
| 137 | + - the extractor cannot run inside an analyzer. |
| 138 | +- **Invariant:** one `Owen.VisualStudio`, one Owen server, extension ≠ IDE plugin. |
| 139 | + |
| 140 | +## Findings worth carrying forward |
| 141 | + |
| 142 | +1. **Top-level statements.** A region in top-level statements is not modelled by the extractor. The TB-MVP acceptance runner has one, and the host refuses it with `OWENB010` instead of skipping it, which is correct fail-closed behaviour. The gate builds that runner with `OwenEnabled=false`. |
| 143 | +2. **`buildTransitive` reach.** It makes every project that references an Owen-enabled project Owen-enabled too. That is intended (the same analysis everywhere) and documented. A project opts out with `OwenEnabled=false`. |
| 144 | +3. **A system shell.** MSBuild's `Exec` needs a system shell (`sh` on Unix, `cmd` on Windows). That is not a toolchain dependency, but the isolation harness must provide it. |
| 145 | +4. **Windows console encoding.** On Windows the core's em dash in a refusal text reaches the build log as `-`: `build-check` writes through the console encoding. The codes, locations and gate checks are unaffected. The fix is to emit UTF-8 explicitly (`Console.OutputEncoding` plus Exec `StdOutEncoding`); it is left for a follow-up, not changed after the official run. |
| 146 | + |
| 147 | +## Verdict |
| 148 | + |
| 149 | +**`GO_OWEN_EXTENSION_ALPHA`**, on `48c71e6`. Every condition registered for GO holds: |
| 150 | + |
| 151 | +| registered condition | evidence | |
| 152 | +|---|---| |
| 153 | +| `PackageReference Owen.TypedBuilder` → generated API | A on Linux and Windows: generated text = `#nullable enable` + golden; manifest written | |
| 154 | +| → CS diagnostics | B, plus the 10 compiler-stage corpus cases | |
| 155 | +| → OWN diagnostics on build | C/D, plus the core-stage corpus cases, as canonical, absolute, navigable MSBuild lines | |
| 156 | +| → no external toolchain or setup | E: a stripped PATH, fresh HOME/NuGet, outside the checkout, no checkout path in any output, on both platforms | |
| 157 | +| → generic extension discovery proven | H (two extensions, one analysis), M7 (the host names no extension), M1a/M1b/M2/M3/M4/M5 (fail loud) | |
| 158 | +| existing TB-MVP guarantees | F (30/30 through the package), G (44/44, transcript byte-identical), `typed_builder_gate` unchanged | |
| 159 | +| foundations unchanged | the foundation diff is empty; `protocol_gate`/`heap_effects_gate`/`cargo` green | |
| 160 | +| Windows + Linux server CI | 36/36 | |
| 161 | + |
| 162 | +**After GO: STOP.** None of the following is started: |
| 163 | +- a production VSIX, LSP or VS Code extension; |
| 164 | +- the Memory or TypeDisciplines extensions; |
| 165 | +- BCL summaries (#394), typed write targets (#395), affine tokens (#392), diagnostic wording (#393), concurrency (#396); |
| 166 | +- a second aggregate; |
| 167 | +- a repo-wide rename. |
| 168 | + |
| 169 | +**For publishing:** nothing was pushed to nuget.org. The license blocker that gates `Owen.Cli` gates these two packages too. |
0 commit comments