Files
suaveplan/openspec/archive/2026-05-26-api-router-pipeline-extraction/specs/api-router/spec.md
2026-05-27 23:29:53 +01:00

3.8 KiB

Capability Spec Delta: api-router — PipelineRouter contract ownership

Package: @suaveplan/api-router Status: PROPOSED

ADDED Requirements

Requirement: Owns the canonical PipelineRouter contract

@suaveplan/api-router SHALL export the canonical PipelineRouter interface and its companion types (PipelineRoute, PipelineRouteOptions, PipelineRouteRegistration) from the ./pipeline subpath. Every concrete pipeline-router adapter (api-router-in-memory, the HonoRouter pipeline facet in api-router-hono, future Stage-7 engines) targets this contract. @suaveplan/api-host MUST consume these types via @suaveplan/api-router/pipeline rather than define them locally.

Scenario: subpath exports the four contract types

  • WHEN import type { PipelineRoute, PipelineRouteOptions, PipelineRouteRegistration, PipelineRouter } from "@suaveplan/api-router/pipeline" is used
  • THEN all four symbols resolve AND match the prior shapes hosted in @suaveplan/api-host/request-pipeline

Scenario: api-host re-exports preserve back-compat

  • WHEN existing code uses import { PipelineRouter } from "@suaveplan/api-host/request-pipeline"
  • THEN the symbol resolves to the same type identity as the new canonical location (verified by structural assignment in a type-level test)

Verified by: src/pipeline/pipeline.test.ts::"PipelineRouter contract exports the canonical surface" AND packages/api-stack/api/api-host/src/request-pipeline/request-pipeline.test.ts::"PipelineRouter re-export matches api-router source-of-truth"


Requirement: Ships the canonical runPipelineRouterConformance harness

@suaveplan/api-router SHALL export runPipelineRouterConformance({ label, build }) from ./testing/pipeline-router-conformance and re-export it through ./testing for ergonomic single-subpath consumption. The harness's invariants — route-match, plugin-ownership-preserved-across-reload, status-map propagation, missing-route returns null — MUST be preserved verbatim from the previous @suaveplan/api-host/testing location.

Scenario: harness invariants unchanged from the api-host source location

  • WHEN an adapter that previously passed runPipelineRouterConformance from @suaveplan/api-host/testing re-targets the harness at @suaveplan/api-router/testing/pipeline-router-conformance
  • THEN the same scenarios pass with no signature or behaviour change

Scenario: ergonomic ./testing re-export

  • WHEN import { runPipelineRouterConformance } from "@suaveplan/api-router/testing" is used
  • THEN the symbol resolves AND matches the dedicated-subpath export by identity

Verified by: src/testing/pipeline-router-conformance/pipeline-router-conformance.test.ts::"invariants match prior api-host location" AND src/testing/index.test.ts::"./testing re-exports runPipelineRouterConformance"


Requirement: Cycle-free build graph

After this change, the workspace build graph MUST NOT contain a cycle between @suaveplan/api-host and @suaveplan/api-router-in-memory. Turborepo's bunx turbo run build MUST complete without emitting Cyclic dependency detected.

Scenario: turbo schedules build with no cycle warning

  • WHEN bunx turbo run build --filter=@suaveplan/api-host is run from a clean tree
  • THEN stderr contains no Cyclic dependency detected line AND the task graph builds successfully

Scenario: repo-wide test gate runs end-to-end

  • WHEN bunx turbo test --output-logs=errors-only is run
  • THEN the exit code is 0 AND no package reports a cycle warning

Verified by: CI assertion in bun scripts/lint/check-no-cycles.ts (new; greps bunx turbo run build --dry=json for any task whose dependencies form a cycle) AND the green-bar evidence captured in the change's archive tasks.md