65 lines
5.7 KiB
Markdown
65 lines
5.7 KiB
Markdown
# Tasks — Dual-shape api-plugin-example
|
|
|
|
## 0. Pre-flight (non-negotiable)
|
|
|
|
- [x] 0.1 Clean working tree; account for every `.claude/worktrees/agent-*`
|
|
- [x] 0.2 `MANIFEST.md` package count matches `find packages -maxdepth 3 -name package.json`
|
|
- [x] 0.3 §0a Sync: worktree-isolated agent fast-forwards to orchestrator-supplied SHA
|
|
|
|
## 1. Extract the pure-domain core
|
|
|
|
- [x] 1.1 Author `src/domain/invoice.types.ts` + `.md` — pure type definitions (Invoice, InvoiceState, InvoiceLine, etc.) extracted from current `schemas/` / `routes/` / `events/`. Zero deps beyond `@suaveplan/api-types`, `@suaveplan/error`.
|
|
- [x] 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.
|
|
- [x] 1.3 Author `src/domain/in-memory-store.ts` + `.md` + `.test.ts` — deterministic Map-backed store with `create / get / list / setState / delete`. Used by the wasm subpath.
|
|
- [x] 1.4 `src/domain/index.ts` — explicit named re-exports (rule 10 — no `export *`).
|
|
- [x] 1.5 100% istanbul coverage on every domain module.
|
|
|
|
## 2. Move existing source under `trusted/`
|
|
|
|
- [x] 2.1 `git mv` every existing `src/routes/`, `src/plugin/`, `src/install/` (if present), `src/events/` directory under `src/trusted/`. Preserve git history via `git mv`.
|
|
- [x] 2.2 Update all internal imports in those files to use the new paths.
|
|
- [x] 2.3 Update existing route handlers to delegate to `src/domain/invoice-rules.ts` for business rules — they SHOULD call the same rule functions the wasm tier will call. This is the contract that makes shape-conformance pass.
|
|
- [x] 2.4 `src/trusted/index.ts` — internal barrel; re-exports `examplePluginModule` + `buildRoutes` for the root `src/index.ts` to forward.
|
|
- [x] 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
|
|
|
|
- [x] 3.1 `src/wasm/wasm-state.ts` + `.md` + `.test.ts` — module-singleton `InMemoryStore` instance; `resetState()` helper for test isolation.
|
|
- [x] 3.2 `src/wasm/routes.ts` + `.md` + `.test.ts` — `export const routes: readonly SerializedRoute[]` listing every endpoint the wasm tier serves (excluding `streamLargeReport`).
|
|
- [x] 3.3 `src/wasm/handle-request.ts` + `.md` + `.test.ts` — `(req: SerializedRequest) => Promise<SerializedResponse>`. Routing is a simple `method + pathname` lookup against `routes`. Handlers delegate to `src/domain/invoice-rules.ts`. Body decoded from `bodyBase64`; response body re-encoded to `bodyBase64`. Headers shaped as `ReadonlyArray<[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.
|
|
- [x] 3.4 `src/wasm/start.ts` + `.md` + `.test.ts` — primes the in-memory store with the same seed data the trusted-tier `install` hook uses.
|
|
- [x] 3.5 `src/wasm/dispose.ts` + `.md` + `.test.ts` — clears the in-memory store.
|
|
- [x] 3.6 `src/wasm/index.ts` — `export const plugin = { routes, handleRequest, start, dispose } as const`. Explicit named re-exports (no `export *`).
|
|
|
|
## 4. Subpath exports + dep-graph lint
|
|
|
|
- [x] 4.1 `package.json` — add `./wasm` AND `./domain` subpath exports per the proposal.
|
|
- [x] 4.2 `tsconfig.lib.json` — confirm new subpaths build into `dist/{wasm,domain}/`.
|
|
- [x] 4.3 `scripts/lint/check-wasm-subpath-deps.ts` (NEW) — walks every static import from `src/wasm/index.ts` transitively; 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 into `bunx turbo lint` as a new `lint:wasm-subpath-deps` task. Add a corresponding task entry in `turbo.json` and a top-level `package.json` script (`lint:wasm-subpath-deps`).
|
|
- [x] 4.4 Run the new lint script — must pass against the new layout.
|
|
|
|
## 5. Conformance test
|
|
|
|
- [x] 5.1 `tests/wasm/shape-conformance.test.ts` — for every route shared between tiers, assert that:
|
|
- `await handleTrustedRequest(new Request(...))` returns the same `status` + body bytes as
|
|
- `await plugin.handleRequest({ ...serializedRequest equivalent ... })`.
|
|
- The `streamLargeReport` endpoint is excluded with a documented `it.skip` and an explanatory comment.
|
|
- [x] 5.2 100% istanbul coverage on every wasm/* file.
|
|
- [x] 5.3 Public-constructor error class preserved; zero `// biome-ignore`; every test imports from `@suaveplan/testing/runner`.
|
|
|
|
## 6. Cross-package check
|
|
|
|
- [x] 6.1 The existing root exports (`@suaveplan/api-plugin-example` → trusted-tier exports) MUST remain byte-compatible. Run `bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-host --filter=@suaveplan/api-plugin-example` and confirm api-host's consumers of api-plugin-example still pass.
|
|
- [x] 6.2 Author `tests/trusted/root-export-shape.test.ts` — snapshot assertion that the root barrel still exports `examplePluginModule`, `buildRoutes`, `EXAMPLE_CODES`, `ExamplePluginError` with their existing types. Pinned guard against accidental signature drift during the refactor.
|
|
|
|
## 7. Gate
|
|
|
|
- [x] 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=4` green
|
|
- [x] 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`.
|