11.2.12 End-to-end conformance — drive the whole work-item journey as an EXTERNAL HTTP client with a real PAT
The story's acceptance test, written from the INTEGRATOR's seat rather than the codebase's: a real PAT, real HTTP, the real route handlers, real Postgres — no service imports, no in-process shortcuts, nothing stubbed. 11.1.6 already ships that harness (tests/api/v1/conformance.test.ts over tests/helpers/mcpHttpServer.ts) and proved the envelope; this card extends it with the resource journey, which is the only thing that proves the story is finished.
This is the story's E2E in the form the story actually has. The Story has no user-observable surface — no page, no panel, nothing a person watches — so it is exempt from the acceptance-video rule under its non-UI carve-out and accepts on its tests alone, exactly as 11.1 did. A Playwright browser test would be theatre here: the client is curl, so the test is an HTTP client.
The journey to drive, in one pass, as one client
The Story's verification recipe, automated: mint a scoped token → list a project's work items → narrow with a filter → page to the end → read one in full → create one → patch it → move it through its workflow → comment → link a dependency → archive it → restore it. Each step uses ONLY what the previous step returned (the created item's key, the nextCursor, the ETag), so a break anywhere in the chain fails the test rather than being papered over by a fixture.
What it must assert that the unit and gate suites cannot
- Only the wire is used. Assertions read the HTTP response — status, headers, JSON — never a repository or service. A conformance test that imports a service is testing the codebase, not the contract.
- The scope story, end to end: a
read-only token walks every GET and is refused 403 on every mutation; awork_items:writetoken is refused 403 on archive; the archive token succeeds. One token per capability, as an integrator would actually mint them. - Cross-tenant is 404 everywhere, asserted for every endpoint that takes a key or project key — the whole surface, not a sample, since one endpoint answering 403 is the leak.
- Every response carries the request id and the
X-RateLimit-*headers, success and failure alike, and a full paged scan of a realistic collection never trips the limiter. - The cursor is honest across a mutating collection: insert an item mid-scan and finish the walk — every original row seen exactly once.
- Error bodies are
{ code, error }with a stable code on each failure the journey provokes, and a 500 never appears. - The unhappy paths a real client hits first: a bad token (401, undifferentiated), a malformed cursor (422), an illegal transition (422 +
allowedTransitions), a duplicate link (409), a staleIf-Match(412).
Scope BOUNDARY
One repo, one suite, one PR. It covers this story's endpoints only — 11.3 extends the same harness for its own resources when it lands. It asserts the CONTRACT, not coverage percentages or import boundaries (11.2.11 owns those), and it does not validate against the OpenAPI spec — that guard is 11.4's and does not exist yet.
Acceptance criteria
- The full journey above runs green against the real route handlers over real HTTP with a real minted PAT and real Postgres, using no service or repository import anywhere in the suite.
- Every step consumes only the previous step's response — the created key, the returned cursor, the returned ETag — so the chain cannot pass with a broken link.
- The scope matrix (read-only / write / archive) is asserted across every endpoint of this story.
- Cross-tenant access returns 404 on every key-taking endpoint, enumerated rather than sampled.
- A paged scan with a concurrent insert yields each original row exactly once, and never trips the rate limiter.
- The five unhappy paths above each return their documented status and stable code.
- The suite fails loudly if an endpoint is added to this story's surface without a conformance step — enumerate the expected route set and assert the tree matches it.
Context refs
tests/api/v1/conformance.test.ts— the shipped external-client suite this extends.tests/helpers/mcpHttpServer.ts— the real-HTTP-server harness both suites ride.lib/apiTokens/token.ts·lib/services/apiTokensService.ts— how a real PAT is minted for the test.docs/decisions/public-api-conventions.md— the contract every assertion here is checking.- Subjects: every code card of this story — 11.2.2 · 11.2.4 · 11.2.6 · 11.2.7 · 11.2.8 · 11.2.9 · 11.2.10. Parent story: 11.2.