5.7 KiB
5.7 KiB
Tasks — Dual-shape api-plugin-example
0. Pre-flight (non-negotiable)
- 0.1 Clean working tree; account for every
.claude/worktrees/agent-* - 0.2
MANIFEST.mdpackage count matchesfind packages -maxdepth 3 -name package.json - 0.3 §0a Sync: worktree-isolated agent fast-forwards to orchestrator-supplied SHA
1. Extract the pure-domain core
- 1.1 Author
src/domain/invoice.types.ts+.md— pure type definitions (Invoice, InvoiceState, InvoiceLine, etc.) extracted from currentschemas//routes//events/. Zero deps beyond@suaveplan/api-types,@suaveplan/error. - 1.2 Author
src/domain/invoice-rules.ts+.md+.test.ts— state-transition machine, validation, plus any pure business rules the existing routes perform inline. NO Hono, NO OTel SDK, NO Request/Response. - 1.3 Author
src/domain/in-memory-store.ts+.md+.test.ts— deterministic Map-backed store withcreate / get / list / setState / delete. Used by the wasm subpath. - 1.4
src/domain/index.ts— explicit named re-exports (rule 10 — noexport *). - 1.5 100% istanbul coverage on every domain module.
2. Move existing source under trusted/
- 2.1
git mvevery existingsrc/routes/,src/plugin/,src/install/(if present),src/events/directory undersrc/trusted/. Preserve git history viagit mv. - 2.2 Update all internal imports in those files to use the new paths.
- 2.3 Update existing route handlers to delegate to
src/domain/invoice-rules.tsfor business rules — they SHOULD call the same rule functions the wasm tier will call. This is the contract that makes shape-conformance pass. - 2.4
src/trusted/index.ts— internal barrel; re-exportsexamplePluginModule+buildRoutesfor the rootsrc/index.tsto forward. - 2.5 Existing
src/error-codes/,src/instruments/,src/schemas/stay at the package root (shared between tiers). Existing exports remain unchanged.
3. Author the wasm-tier surface
- 3.1
src/wasm/wasm-state.ts+.md+.test.ts— module-singletonInMemoryStoreinstance;resetState()helper for test isolation. - 3.2
src/wasm/routes.ts+.md+.test.ts—export const routes: readonly SerializedRoute[]listing every endpoint the wasm tier serves (excludingstreamLargeReport). - 3.3
src/wasm/handle-request.ts+.md+.test.ts—(req: SerializedRequest) => Promise<SerializedResponse>. Routing is a simplemethod + pathnamelookup againstroutes. Handlers delegate tosrc/domain/invoice-rules.ts. Body decoded frombodyBase64; response body re-encoded tobodyBase64. Headers shaped asReadonlyArray<[string, string]>.SuaveplanError-shaped throws are NOT caught here — the WASI-stdio ABI shim (in wasm-compiler-javy) catches them and emits{kind:"error"}envelopes. - 3.4
src/wasm/start.ts+.md+.test.ts— primes the in-memory store with the same seed data the trusted-tierinstallhook uses. - 3.5
src/wasm/dispose.ts+.md+.test.ts— clears the in-memory store. - 3.6
src/wasm/index.ts—export const plugin = { routes, handleRequest, start, dispose } as const. Explicit named re-exports (noexport *).
4. Subpath exports + dep-graph lint
- 4.1
package.json— add./wasmAND./domainsubpath exports per the proposal. - 4.2
tsconfig.lib.json— confirm new subpaths build intodist/{wasm,domain}/. - 4.3
scripts/lint/check-wasm-subpath-deps.ts(NEW) — walks every static import fromsrc/wasm/index.tstransitively; rejects any specifier matching the wasm-tier ban list (@suaveplan/api-host,hono,@hono/*,@opentelemetry/sdk-*,node:*, plus any package whose graph includes those). Wired intobunx turbo lintas a newlint:wasm-subpath-depstask. Add a corresponding task entry inturbo.jsonand a top-levelpackage.jsonscript (lint:wasm-subpath-deps). - 4.4 Run the new lint script — must pass against the new layout.
5. Conformance test
- 5.1
tests/wasm/shape-conformance.test.ts— for every route shared between tiers, assert that:await handleTrustedRequest(new Request(...))returns the samestatus+ body bytes asawait plugin.handleRequest({ ...serializedRequest equivalent ... }).- The
streamLargeReportendpoint is excluded with a documentedit.skipand an explanatory comment.
- 5.2 100% istanbul coverage on every wasm/* file.
- 5.3 Public-constructor error class preserved; zero
// biome-ignore; every test imports from@suaveplan/testing/runner.
6. Cross-package check
- 6.1 The existing root exports (
@suaveplan/api-plugin-example→ trusted-tier exports) MUST remain byte-compatible. Runbunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-host --filter=@suaveplan/api-plugin-exampleand confirm api-host's consumers of api-plugin-example still pass. - 6.2 Author
tests/trusted/root-export-shape.test.ts— snapshot assertion that the root barrel still exportsexamplePluginModule,buildRoutes,EXAMPLE_CODES,ExamplePluginErrorwith their existing types. Pinned guard against accidental signature drift during the refactor.
7. Gate
- 7.1
bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-plugin-example --filter=@suaveplan/api-host --filter=@suaveplan/api-types --filter=@suaveplan/api-sandbox-wasm-host-bridge --concurrency=4green - 7.2
bun scripts/folder-reorg/generate-manifest.ts— package count unchanged (same package, additive subpath).
8. Archive (orchestrator step)
- 8.1 Fold delta into
openspec/specs/api-plugin-example/spec.md(create if not yet present). - 8.2 Move folder →
openspec/archive/2026-05-27-api-plugin-example-dual-shape/. - 8.3 Refresh
MANIFEST.md.