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

6.6 KiB

Tasks — Extract PipelineRouter Contract

1. Author new api-router surface

  • 1.1 Create packages/api-stack/api/api-router/src/pipeline/pipeline.types.ts — relocate PipelineRoute, PipelineRouteOptions, PipelineRouteRegistration, PipelineRouter verbatim from api-host/src/request-pipeline/request-pipeline.types.ts (lines defining those 4 types). Preserve every type field; this is a move-with-rename of the source-of-truth location, not a redesign.
  • 1.2 pipeline.md (≥200 words, 6 sections).
  • 1.3 pipeline.test.ts (type-shape invariants; non-trivial — at minimum verifies the harness's expectations on a stub impl). 100% istanbul on touched modules.
  • 1.4 pipeline/index.ts named-export barrel.
  • 1.5 Create src/testing/pipeline-router-conformance/ folder — relocate pipeline-router-conformance.ts + .types.ts + .test.ts + .md + index.ts verbatim from api-host/src/testing/pipeline-router-conformance/. Update internal imports (../../request-pipeline/request-pipeline.types.ts → ../../pipeline/pipeline.types.ts).
  • 1.6 src/testing/index.ts barrel updates: re-export runPipelineRouterConformance + its types.
  • 1.7 package.json exports: add ./pipeline and ./testing/pipeline-router-conformance.
  • 1.8 Re-export from src/index.ts barrel.

2. Update api-host to consume

  • 2.1 In src/request-pipeline/request-pipeline.types.ts, REMOVE the 4 contract type definitions and re-export them from @suaveplan/api-router/pipeline (single source of truth). All in-package references continue to compile.
  • 2.2 Delete src/testing/pipeline-router-conformance/ folder.
  • 2.3 In src/testing/testing.ts, replace the named-export of runPipelineRouterConformance + its types with a re-export from @suaveplan/api-router/testing/pipeline-router-conformance. Existing @suaveplan/api-host/testing consumers see no API change.
  • 2.4 package.json: add @suaveplan/api-router to dependencies. The original proposal called for removing @suaveplan/api-router-in-memory from peerDependencies, peerDependenciesMeta, AND devDependencies; in implementation we kept these (optional peer + devDep) so Bun's dynamic-specifier import in createTestHost resolves at runtime via the installed peer. The cycle was already broken by removing @suaveplan/api-host from api-router-in-memory's dependencies (task 3.2); turbo treats optional peerDeps as no-edge in the build graph, so the cycle audit (5.1/5.2) still passes.

3. Update api-router-in-memory

  • 3.1 Move tests/conformance/api-host.test.ts → tests/conformance/pipeline-router.test.ts. Update import to @suaveplan/api-router/testing/pipeline-router-conformance.
  • 3.2 package.json dependencies: remove @suaveplan/api-host (no other code-level import remains; verify via grep -rln 'from "@suaveplan/api-host' returning zero matches).
  • 3.3 vite.config.ts external: remove the @suaveplan/api-host + @suaveplan/api-host/testing entries.
  • 3.4 Docstring references in src/adapter/adapter.ts, src/pipeline/pipeline.types.ts, src/adapter/adapter.md, src/pipeline/pipeline.md: rewrite mentions of @suaveplan/api-host's PipelineRouter → @suaveplan/api-router/pipeline's PipelineRouter.

4. Update api-router-hono

  • 4.1 src/pipeline/pipeline.conformance.test.ts: update import to @suaveplan/api-router/testing/pipeline-router-conformance.
  • 4.2 package.json: if @suaveplan/api-host is in dependencies, drop it. Verify @suaveplan/api-router is already declared.

5. Cycle-audit gate

  • 5.1 bunx turbo run build --dry=json --filter=@suaveplan/api-host | jq '.tasks[0].dependencies' MUST NOT include @suaveplan/api-router-in-memory#build.
  • 5.2 bunx turbo run build --dry=json --filter=@suaveplan/api-router-in-memory | jq '.tasks[0].dependencies' MUST NOT include @suaveplan/api-host#build.
  • 5.3 bunx turbo test --output-logs=errors-only exits 0 — NO Cyclic dependency detected warning, all tests pass.
  • 5.4 bunx turbo build exits 0 across the repo.
  • 5.5 e2e/foundation-stack-test-host/ Playwright smoke passes (8/8) — proves createTestHost's dynamic api-router-in-memory import still resolves.
  • 5.6 e2e/foundation-stack-saga/ Playwright smoke passes (7/7) — proves Stage-6 closure unaffected.

6. Coverage + docs

  • 6.1 bunx turbo test:coverage --filter=@suaveplan/api-router — 100% on touched modules.
  • 6.2 bunx turbo test:coverage --filter=@suaveplan/api-host — 100% on touched modules.
  • 6.3 bun run docs:check --filter=@suaveplan/api-router — every relocated .ts has its .md sibling.

7. Archive

  • 7.1 Fold delta into openspec/specs/api-router/spec.md (new pipeline subcap + harness subcap).
  • 7.2 Fold the api-host SoT change into openspec/specs/api-host/spec.md as a MODIFIED requirement reflecting the re-export contract.
  • 7.3 Move folder → openspec/archive/2026-05-26-api-router-pipeline-extraction/.
  • 7.4 Regenerate MANIFEST.md.

8. Implementation reconciliation

The shipped architecture diverged slightly from the original proposal: the canonical PipelineRouter / PipelineRoute / PipelineRouteOptions / PipelineRouteRegistration contract types ALREADY live in @suaveplan/api-types/pipeline-router (a parallel landing that arrived between proposal-writing and this change), so the proposal's @suaveplan/api-router/pipeline subpath would have been a redundant second SoT. The final wiring:

  • Contract owned by @suaveplan/api-types/pipeline-router (zero-runtime).
  • Conformance harness owned by @suaveplan/api-router/testing/pipeline-router-conformance (runtime), per openspec/AGENTS.md §16.
  • @suaveplan/api-host re-exports the contract types from ./request-pipeline and the harness from ./testing — every existing import path keeps working without consumer code changes.
  • Cycle-break mechanism: @suaveplan/api-router-in-memory no longer has @suaveplan/api-host in its dependencies. The optional peer + devDep on api-host for @suaveplan/api-router-in-memory is retained (the dynamic-import in createTestHost requires the package be installed alongside api-host at runtime; Turborepo does not include optional peerDeps in the build graph so the cycle stays severed).

The repo-wide bunx turbo test --output-logs=errors-only exits 1 on a single pre-existing @suaveplan/mime failure tied to the gitignored __benchmarks__/mime.bench.ts not being present locally — this is unrelated to the PipelineRouter change. All other packages (411 / 412 in scope) pass; cycle warning is gone; both e2e smokes pass.