Skip to content

Commit 79f7ef2

Browse files
phodalcodex
andcommitted
feat(desktop): isolate oxc in a rust service
Implement docs/specs/2026-09-07-rust-oxc-service.md with a pinned Rust OXC executable and bounded JSONL transport. Keep AgentReact admission, ABI extraction and semantic indexing in the existing kernel, inject the compiler through production build and preview routes, and partition caches by compiler factory. Desktop compilation no longer loads OXC NAPI bindings. Add timeout, cancellation and crash recovery, package the native executable outside ASAR, and require Rust for future extracted capability services. Validated 5 Rust tests, 7 native integration tests, AgentReact and server regressions, documentation links, and the packaged macOS arm64 app. Windows/Linux execution and Apple NSXPC remain unverified or out of scope. Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
1 parent 3920fd1 commit 79f7ef2

32 files changed

Lines changed: 2019 additions & 188 deletions

‎.github/workflows/harness-desktop.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ jobs:
2525
with:
2626
node-version: '24'
2727
cache: npm
28+
- uses: dtolnay/rust-toolchain@1.96.0
2829
- run: npm ci
2930
- run: npm run harness-desktop:test
3031
- run: npm run harness-desktop:pack
3132
env:
3233
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
34+
- run: npm run test:rust -w @qoder-ai/harness-desktop
35+
- run: npm run test:native -w @qoder-ai/harness-desktop
3336
- if: runner.os != 'Linux'
3437
run: npm run smoke -w @qoder-ai/harness-desktop -- --packaged
3538
- if: runner.os == 'Linux'

‎docs/ARCHITECTURE.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ the target journey, contract, evidence, governance, and DX-measurement system.
1313
dispatches to capability-owned commands with argv arrays and must not own
1414
product logic, schemas, fixtures, or host adapters.
1515
- Compose through public surfaces; do not import another capability's private helpers, fixtures, or provider modules.
16+
- Extracted desktop capability services (including OXC and future ACP services)
17+
are Rust executables. The existing Studio Node runtime remains a host; keep
18+
capability contracts explicit and transports cross-platform.
1619
- Keep product judgment in canonical owners such as `skills/`, `scripts/`, `hooks/`, `models/`, `schemas/` (a target owner, not yet created), `templates/`, and `references/`; host shells stay thin.
1720
- Use business-named boundaries, not generic umbrellas such as `scripts/core/`.
1821
- Runtime behavior needs a contract plus validation evidence: fixtures, tests, smoke commands, or parser-safe output. CLI facades need help, unknown-command, and delegated-output coverage.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Rust OXC Service
2+
3+
## Traceability
4+
- Spec ID: rust-oxc-service
5+
- Status: Implemented
6+
- Request: split OXC into an XPC service; all extracted capability services must be written in Rust.
7+
- AI involvement: Codex implementation and local validation.
8+
9+
## Intent
10+
Move desktop OXC native parsing and transformation into a supervised Rust
11+
executable. Preserve AgentReact profile checks, ABI extraction, semantic index,
12+
diagnostics and browser CLI compatibility.
13+
14+
## Acceptance Scenarios
15+
- AC-1: Desktop uses the Rust service for every OXC parse/transform, with no OXC NAPI library loaded in the Studio process. A typed compiler factory passes through production artifact routes; the browser CLI retains its worker adapter.
16+
- AC-2: A versioned, strict, bounded JSONL stdio protocol carries request ids and parse/transform results. Invalid requests, unknown fields, oversized sources/frames and unsupported protocol versions fail closed. Native code receives source text, not filesystem authority.
17+
- AC-3: Deadline, crash, malformed output and close settle pending calls. Timeout/cancellation kills the affected process; a later compile starts a fresh process. Queues and buffers are bounded; no automatic replay of the failed request.
18+
- AC-4: Valid TSX (including non-ASCII text), refused profile examples, ABI/semantic index and source maps match the existing kernel. Profile refusal happens before requesting transformation. Compiler identity and outer artifact caches distinguish transports.
19+
- AC-5: Cargo locks OXC to 0.147.0; target-native Rust binaries ship outside ASAR. Native tests and the packaged macOS app prove real service calls and process separation. Windows/Linux build and smoke are wired into CI; unrun platforms remain unverified.
20+
21+
## Design and Capability Matrix
22+
| Capability | Decision |
23+
| --- | --- |
24+
| Parse and transform | Rust service using pinned OXC crates |
25+
| Profile, ABI, semantic index | Existing JS semantic kernel, using a private backend port |
26+
| AST transfer | Bounded ESTree JSON inside the kernel/backend boundary only; never exposed to business callers or renderer. UTF-8 spans converted to UTF-16 for parity. |
27+
| Cancellation | Kill dedicated compiler process; synchronous native work cannot consume cancel messages while executing |
28+
| XPC transport | Cross-platform child process + framed JSONL stdio; native macOS NSXPC is not claimed |
29+
| Other future capability services | Rust executables; Studio remains the existing Node host runtime |
30+
31+
## Non-goals
32+
No ACP extraction, semantic-rule rewrite, generic RPC registry, UI changes,
33+
macOS-only NSXPC bridge, signing, publication or auto-update changes.
34+
35+
## Plan and Tasks
36+
1. Separate the native backend from semantic compilation without changing the
37+
existing default adapter. Keep constants available without importing NAPI.
38+
2. Add Rust parse/transform service and a supervised Node transport adapter.
39+
3. Inject compiler factory from desktop through Studio production routes;
40+
partition caches by factory and retain current OxcCompilerPort output.
41+
4. Package the Rust binary as an external resource; add Cargo/native/protocol
42+
parity checks and extend desktop smoke with actual Rust proof.
43+
44+
## Test and Review Evidence
45+
Local macOS arm64 evidence on 2026-09-07:
46+
47+
| Acceptance | Receipt |
48+
| --- | --- |
49+
| AC-1 | Packaged-app smoke records separate main, Studio and Rust PIDs; Studio process.report sharedObjects contains no OXC NAPI. Native HTTP test proves both build and preview routes call the injected compiler. |
50+
| AC-2 | Five Rust tests cover UTF-16 spans, TSX transform/source map, unknown methods/fields/versions, source limits, oversized/truncated frames and parse errors. Cargo.lock pins native dependencies. |
51+
| AC-3 | Native adapter tests exercise a hung child, exit code zero, malformed JSON, oversized stdout, cancellation, use after close and recovery with a real Rust process. Desktop tracks active compilers and closes them before stopping Studio. |
52+
| AC-4 | Seven native integration tests include parity with NAPI over valid and refused inputs, Unicode, generated code and parsed source maps; profile refusal makes zero transform calls. Artifact cache regression proves different factories cannot share cached builds. |
53+
| AC-5 | Full desktop build/stage/pack and packaged macOS arm64 smoke pass. Binary ships outside ASAR. Cargo tests, native integration tests and packaged smoke are wired into the three-platform CI matrix. |
54+
| Regression | AgentReact/artifact/auth selection: 182 passing tests before adding the cache case. Final artifact/project/server selection: 70 passing tests including that new case. These overlap and are not additive. Six desktop lifecycle tests and eight docs-link tests also pass. |
55+
56+
Commands: `npm run test:rust -w @qoder-ai/harness-desktop`,
57+
`npm run test:native -w @qoder-ai/harness-desktop`,
58+
`npm exec -w @qoder-ai/harness-studio -- vitest run test/agent-react test/artifact-compile-runtime.test.ts test/desktop-authorization.test.ts`,
59+
`npm exec -w @qoder-ai/harness-studio -- vitest run test/artifact-compile-runtime.test.ts test/project-server.test.ts test/server.test.ts`,
60+
`CSC_IDENTITY_AUTO_DISCOVERY=false npm run harness-desktop:pack`,
61+
`npm run smoke -w @qoder-ai/harness-desktop -- --packaged`.
62+
63+
Generated native and screenshot/JSON receipts live under the desktop package's
64+
ignored `dist/`. Windows/Linux jobs have not run here; no signed release or
65+
native Apple NSXPC acceptance is claimed. Existing Studio npm dependencies retain
66+
NAPI for browser/CLI compatibility, but the desktop does not load or fall back
67+
to those modules. AST serialization adds bounded transport overhead; no speedup
68+
is claimed. Source-map object content matches; JSON property ordering is not a
69+
semantic equality requirement.
70+
71+
Review Readiness Check: user request and spec confirmed; no Story supplied.
72+
The Electron baseline is committed as 3920fd1. This Rust slice is reviewed separately from the baseline: native service/lockfile, semantic-backend seam, supervised
73+
adapter, factory routing/cache isolation, desktop packaging/smoke, architecture
74+
rule and tests. No ACP implementation or UI change is included. No generated
75+
runtime artifacts are staged; no publication is performed.

‎package-lock.json‎

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/harness-desktop/README.md‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
# Harness Studio Desktop
22

3-
Electron distribution of Harness Studio. Build from the repository root:
3+
Electron distribution of Harness Studio. Install Rust 1.96.0 with rustup for
4+
the native OXC service, then build from the repository root:
45

56
```sh
7+
rustup toolchain install 1.96.0 --profile minimal
68
npm ci
79
npm run harness-desktop:build
810
npm run harness-desktop:dev
911
```
1012

1113
`npm run harness-desktop:pack` builds an unpacked application under
1214
`packages/harness-desktop/dist/installers`. Run it on the target OS/architecture
13-
so npm selects the matching oxc native packages. `npm run dist -w
15+
so Cargo produces the matching Rust executable. `npm run dist -w
1416
@qoder-ai/harness-desktop` creates the configured ZIP (macOS), NSIS (Windows),
1517
or AppImage (Linux). These are development packaging targets; signing,
1618
notarization and automatic updates are not configured.
1719

1820
The main process owns the native window, menus, single instance and directory
1921
chooser. A utility process owns a Node worker hosting the Studio HTTP server, providers,
20-
ACP and compiler workers. Renderer fetch and SSE stay unchanged. A per-launch
22+
ACP and the semantic kernel. OXC parsing and transformation run in a dedicated Rust
23+
process supervised by the Studio host. Renderer fetch and SSE stay unchanged. A per-launch
2124
HTTP credential is attached by an isolated Electron session; the renderer has
2225
no Node integration or preload API. External links need native confirmation.
2326

@@ -26,20 +29,33 @@ request/result. Startup and shutdown are bounded, directory requests are
2629
correlated, and service failure terminates the desktop app. The browser CLI
2730
continues to use its existing directory chooser and server behavior.
2831

29-
“XPC” describes the intended cross-process service boundary here. The current
30-
transport is Electron utilityProcess on all three platforms, not native macOS
31-
NSXPC. Extracting ACP and oxc into dedicated services is a later step; keep
32-
request/result/cancellation contracts with those capability owners.
32+
All extracted capability services, including OXC and future ACP services, are
33+
written in Rust. OXC uses a versioned, bounded JSONL stdio protocol. The current
34+
cross-platform process boundary does not claim native macOS NSXPC support.
35+
36+
A compiler belongs to one artifact build. Parse/transform requests carry source
37+
text and portable module names; Rust never opens those names as files. Profile,
38+
ABI and semantic-index rules stay in the JS kernel; AST transfer is private to
39+
its native backend. Deadlines, malformed replies, crashes and cancellation kill
40+
the affected Rust process. A later request can start a fresh process. There is
41+
no desktop fallback to NAPI. Compiler-factory identity partitions artifact caches.
42+
43+
The Rust binary is packaged as `Resources/native/harness-oxc-service` (with
44+
`.exe` on Windows), outside ASAR. The browser CLI still uses its existing NAPI
45+
worker, so the shared Studio package retains those dependencies.
3346

3447
Validation:
3548

3649
```sh
3750
npm run harness-desktop:test
51+
npm run test:rust -w @qoder-ai/harness-desktop
52+
npm run test:native -w @qoder-ai/harness-desktop
3853
npm run smoke -w @qoder-ai/harness-desktop
3954
```
4055

4156
The smoke command launches a real Electron window with Playwright, verifies
42-
renderer isolation, local HTTP protection and native parsing, saves a screenshot,
57+
renderer isolation, local HTTP protection, Rust parsing/transformation and absence of OXC NAPI
58+
in the Studio process, saves a screenshot,
4359
and checks service shutdown. It requires a graphical session (Xvfb on Linux).
4460
Staging resolves production dependencies through npm; a signed, reproducible
4561
release pipeline and native Windows/Linux receipts remain separate work.

‎packages/harness-desktop/package.json‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010
"node": ">=22.20.0 <25.0.0"
1111
},
1212
"scripts": {
13-
"build": "npm run build -w @qoder-ai/harness && npm run build -w @qoder-ai/harness-studio",
13+
"build": "npm run build -w @qoder-ai/harness && npm run build -w @qoder-ai/harness-studio && npm run build:rust",
1414
"start": "electron .",
1515
"test": "node --test test/*.test.mjs",
1616
"smoke": "node scripts/smoke.mjs",
1717
"stage": "node scripts/stage.mjs",
1818
"pack": "npm run build && npm run stage && electron-builder --dir",
19-
"dist": "npm run build && npm run stage && electron-builder"
19+
"dist": "npm run build && npm run stage && electron-builder",
20+
"build:rust": "node scripts/rust.mjs",
21+
"test:rust": "node scripts/rust.mjs --test",
22+
"test:native": "npm exec -w @qoder-ai/harness-studio -- vitest run --config vitest.native.config.ts"
2023
},
2124
"dependencies": {
22-
"@qoder-ai/harness-studio": "0.1.1",
23-
"oxc-parser": "0.147.0"
25+
"@qoder-ai/harness-studio": "0.1.1"
2426
},
2527
"devDependencies": {
2628
"electron": "44.2.0",
@@ -61,6 +63,15 @@
6163
],
6264
"category": "Development",
6365
"executableName": "harness-studio"
64-
}
66+
},
67+
"extraResources": [
68+
{
69+
"from": "dist/native",
70+
"to": "native",
71+
"filter": [
72+
"harness-oxc-service*"
73+
]
74+
}
75+
]
6576
}
6677
}

0 commit comments

Comments
 (0)