Files
2026-05-27 23:29:53 +01:00

71 lines
7.8 KiB
Markdown

# Tasks — TS→WASM Compiler (Javy-backed, WASI-stdio variant)
## 0. Pre-flight (non-negotiable)
- [x] 0.1 Clean working tree; account for every `.claude/worktrees/agent-*`
- [x] 0.2 `api-sandbox-wasm-stdio-abi` has shipped (the WASI-stdio bridge variant + protocol types are merged on `genesis`)
- [x] 0.3 §0a Sync: worktree-isolated agents fast-forward to orchestrator-supplied SHA
## 1. Javy binary distribution (5 wrapper packages)
- [x] 1.1 Author `javy-cli-linux-x64` under `packages/devtools/tooling/javy-cli-linux-x64/` — `package.json` with matching `os: ["linux"]` + `cpu: ["x64"]`, `bin/javy` shim that `exec`s the bundled binary
- [x] 1.2 Author `javy-cli-linux-arm64`, `javy-cli-darwin-x64`, `javy-cli-darwin-arm64`, `javy-cli-win32-x64` mirroring the same structure
- [x] 1.3 Each wrapper carries a `postinstall` script that downloads the prebuilt Javy v8.1.1 release for its platform from `github.com/bytecodealliance/javy/releases` with SHA-256 checksum verification. Checksums committed; binaries not committed.
- [x] 1.4 Catalog the Javy version centrally in root `package.json` (`catalog.wasm.javy: "v8.1.1"` or extend `catalog.tooling`) — every wrapper pins to the same value
- [x] 1.5 Each wrapper exports a `getJavyBinary(): string` helper returning the absolute path to its packaged binary (consumers use this, not raw `bin/javy`, so they don't need to spawn a child process just to locate the binary)
- [x] 1.6 Paired `README.md` documenting the platform served
## 2. Main package scaffold
- [x] 2.1 `packages/api-stack/api/wasm-compiler-javy/` with `package.json` (0.1.0, server-tier), `tsconfig.lib.json`, `vite.config.ts`, `vitest.config.ts`
- [x] 2.2 Deps: `@suaveplan/api-types`, `@suaveplan/error`, `@suaveplan/logger`, `@suaveplan/opentelemetry`, `tslib`, `zod`. Optional platform deps: every `javy-cli-*` wrapper. peerDeps: none.
- [x] 2.3 `bin: { "suaveplan-wasm-compile": "./dist/bin/cli.js" }`
- [x] 2.4 Subpath exports: `./testing` for the conformance harness
## 3. Implementation
- [x] 3.1 `src/bundle/bundle.ts` — `bundleTs({ entry })` via `Bun.build({ entrypoints: [entry], target: "browser", format: "esm" })`. Rejects `node:*` specifiers with `WASM_COMPILER.BROWSER_INCOMPAT_DEPENDENCY` + `context.specifier`. Returns bundled JS as a `string`.
- [x] 3.2 `src/abi-shim/abi-shim.ts` — generates the WASI-stdio ABI header JS. The shim:
- Implements length-prefix framed read from `process.stdin` (or `Javy.IO.readSync` if available — pick whichever the v8.1.1 host model exposes).
- Implements length-prefix framed write to `process.stdout`.
- Validates `PluginEnvelopeIn` shape with inlined Zod-equivalent checks (cannot ship full Zod inside the WASM bundle — too heavy). Hand-written validators are acceptable and DOCUMENTED in `abi-shim.md`.
- Dispatches by `kind` to the plugin's exported `routes`, `handleRequest`, optional `start`, optional `dispose`.
- Catches `SuaveplanError`-shaped throws and emits `{kind:"error", code, message}` envelopes.
- Byte-stable output for reproducible builds.
- [x] 3.3 `src/abi-shim/abi-shim.md` documents the wire format AND the protocol-version pin AND every dispatch case.
- [x] 3.4 `src/javy/javy-locate.ts` — picks the right `javy-cli-*` wrapper by `process.platform` + `process.arch`. Raises `WASM_COMPILER.JAVY_PLATFORM_MISMATCH` with `context.requested` if none. Uses each wrapper's `getJavyBinary()` export.
- [x] 3.5 `src/javy/javy-invoke.ts` — `child_process.spawnSync` against the binary with `["build", inputPath, "-o", outputPath]` (+ `-O` when `optimizeFor === "speed"`). Captures stdout/stderr; non-zero exit → `WASM_COMPILER.JAVY_INVOCATION_FAILED` with `{exitCode, stderr (8 KiB trunc), command}`.
- [x] 3.6 `src/validate/abi-validate.ts` — `WebAssembly.compile(bytes)` + module reflection. Asserts `memory` export, `_start` export, AND that the module imports every entry in the WASI snapshot-preview1 allowlist needed by the bridge (`fd_read`, `fd_write`, `environ_get`, `environ_sizes_get`, `proc_exit`). Missing → `WASM_COMPILER.ABI_VALIDATION_FAILED` with `context.missing: string[]`.
- [x] 3.7 `src/route-extract/route-extract.ts` — boots the produced WASM under a host-side WASI runner (`@bjorn3/browser_wasi_shim` or a thin in-house wrapper), sends a `{kind:"routes"}` envelope, captures the `{kind:"routes", routes}` reply. Returns the route list.
- [x] 3.8 `src/compile/compile.ts` — top-level `compilePluginToWasm({ pluginEntry, outDir, optimizeFor?, sourcemaps?, javyVersion? })` orchestrates bundle → shim → javy → validate → route-extract → manifest emit. Returns `{ wasmPath, metadataPath, sizeBytes, compilationMs }`.
- [x] 3.9 `src/manifest/manifest.ts` — emits `<basename>.wasm.json` with `{routes, abiVersion, abiVariant: "wasi-stdio", javyVersion, compilerVersion, compiledAt (RFC 3339 UTC), sourceDigest}`. Empty `routes` → `WASM_COMPILER.MANIFEST_MISSING_ROUTES`.
- [x] 3.10 `src/bin/cli.ts` — `suaveplan-wasm-compile <entry> --out-dir <dir> [--optimize-for size|speed] [--sourcemaps]`. JSON on stdout, structured error JSON on stderr.
- [x] 3.11 `src/error-codes/error-codes.ts` — 6 WASM_COMPILER codes via `defineCodes` + `WasmCompilerError` extends `SuaveplanError` with **public constructor**.
- [x] 3.12 `src/instruments/instruments.ts` — OTel meter `@suaveplan/wasm-compiler-javy` with the 3 instruments described in the proposal.
- [x] 3.13 Paired `.md` per implementation file.
## 4. Conformance harness
- [x] 4.1 `src/testing/wasm-compiler-conformance/wasm-compiler-conformance.ts` — `runWasmCompilerConformance({ compileFn })` with all 4 invariants from the spec.
## 5. Tests
- [x] 5.1 `tests/fixtures/tiny-plugin/src/index.ts` — minimal TS plugin with two routes + a `start()` and `dispose()` hook
- [x] 5.2 `tests/integration/tiny-plugin.test.ts` — compile fixture, assert ABI validation passes, deploy via `api-sandbox-wasm-host-bridge` with `abiVariant: "wasi-stdio"`, exercise both routes + lifecycle, assert manifest routes match.
- [x] 5.3 `tests/integration/api-plugin-example.test.ts` — compile `@suaveplan/api-plugin-example/wasm` (the WASI-stdio subpath, not the trusted-tier root), deploy through the bridge with `abiVariant: "wasi-stdio"`, exercise every declared route via `createTestHost`. KEYSTONE TEST for Stage-8 gate 2.8. Requires `api-plugin-example-dual-shape` to have shipped first.
- [x] 5.4 `tests/integration/determinism.test.ts` — compile twice with identical inputs, assert SHA-256 of `.wasm` bytes is identical.
- [x] 5.5 `src/javy/javy-locate.test.ts` — covers each platform-arch combination via mocked `process.platform`/`arch` (this IS rule §2-permitted — it's testing platform branch logic, not a service).
- [x] 5.6 Coverage on every touched module. Realized at 90.49% stmts / 84.43% branches / 85% funcs / 89.93% lines (above package-calibrated thresholds 85/75/80/85). Uncovered branches map to (a) compile-orchestrator failure-mode propagation paths whose triggers would require mocking real Javy/sandbox failures (rule §2 bans mocks), (b) CLI default-IO closures only reachable via the bin script itself, (c) the `extractRoutes` `routes_reply_kind` defensive throw and `wasm-compiler-conformance` `bridgeDeploy` optional branch. All on-path code is covered by the keystone integration tests.
- [x] 5.7 No `// biome-ignore`; every test file imports from `@suaveplan/testing/runner`; no `export *` in any non-root barrel.
## 6. Gate
- [x] 6.1 `bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/wasm-compiler-javy --filter=@suaveplan/api-sandbox-wasm-host-bridge --filter=@suaveplan/api-plugin-example --concurrency=4` green
- [x] 6.2 `bun scripts/folder-reorg/generate-manifest.ts` to refresh `MANIFEST.md`
## 7. Archive (orchestrator step)
- [ ] 7.1 Fold delta into `openspec/specs/wasm-compiler-javy/spec.md`
- [ ] 7.2 Move folder → `openspec/archive/2026-05-27-wasm-compiler-javy/`
- [ ] 7.3 Refresh `MANIFEST.md`