Repository navigation
Description
Part of #10 — Go HTTP Daemon lane. Follow-up to #18.
⚠️ Scope: Spec + validation only. Builds on the route shell from #18; does not change route surface or add business logic.
Goal
Formalise the 25-route contract registered in #18 as a single OpenAPI 3.1 document, and wire a validation middleware so every request and response is checked against the spec at runtime. This locks the contract before phase 1c handlers land.
Depends on: #18 (Route shell) must be merged first — the contract-preview bodies in each 501 stub are the source material that gets translated into the spec.
Tasks
- Author `backend/api/openapi.yaml` (OpenAPI 3.1) covering all 25 routes from feat(api): 1b — Route shell (all 25 routes as 501 stubs) #18 — paths, methods, params, request bodies, response schemas, status codes
- Pick & wire a validator: `github.com/getkin/kin-openapi/openapi3filter` (recommended — pure runtime validation, no codegen lock-in) vs `oapi-codegen` (codegen path)
- Add validation middleware on the `/api/v1` subrouter: rejects malformed requests with `400` + structured error, and (in dev/test mode) flags non-conformant responses
- Serve the spec at `GET /api/v1/openapi.yaml` and `GET /api/v1/openapi.json` for client consumers
- Conformance test: table-driven test that loads the spec, hits every route with a minimal valid request, asserts the response validates against the documented schema (even though body is still 501-with-contract-preview)
- Document the spec authoring workflow in `backend/api/README.md` (how to add a route, how to regen if codegen is chosen, how validation errors surface)
Inputs to the spec
- Contract previews embedded in each feat(api): 1b — Route shell (all 25 routes as 501 stubs) #18 controller stub (the `planned.request` / `planned.response` bodies)
- Prior TS orchestrator contracts (links pending from owner)
- New LCM / SCM port contracts in `backend/internal/ports/`
Design notes / open questions
- Validator choice: `openapi3filter` keeps the spec as source-of-truth without generated handler glue. `oapi-codegen` generates strongly-typed handler interfaces but couples the handler signature to spec regen. Pick `openapi3filter` unless the team explicitly wants codegen.
- Response validation in prod: validating responses on every request is expensive — gate behind a config flag (`HTTPD_VALIDATE_RESPONSES`, default off in prod, on in tests).
- 501 stubs: validation must allow the `{ "error": "not implemented", "planned": { ... } }` envelope until real handlers land. Either model the envelope in the spec as the `501` response for every operation, or skip response validation when status == 501.
Exit criteria
```bash
spec served and parseable
curl -s localhost:3001/api/v1/openapi.yaml | head -5
malformed request rejected by middleware, not handler
curl -i -X POST localhost:3001/api/v1/sessions -d '{}' \
→ 400 with validation error pointing at missing required field
valid request still reaches stub
curl -i -X POST localhost:3001/api/v1/sessions
-H 'content-type: application/json'
-d '{"projectId":"p1","issueId":"i1","kind":"work"}'
→ 501 with planned-contract envelope
```
Conformance test passes: every route's stub response validates against the spec it claims to implement.
Out of scope
- Real handler logic (phase 1c / later issues)
- Client SDK generation from the spec (separate concern)
- Auth / authz schemes in the spec (placeholder `security: []` until auth lane lands)
Owner: TBD (suggest same owner as #18 for continuity)
Activity
refactor(api): replace stubs/ with OpenAPI-as-source-of-truth — #20
feat(api): projects route shell (7 routes, REST-corrected) — #20
refactor(api): replace stubs/ with OpenAPI-as-source-of-truth — #20
feat(api): projects route shell (7 routes, REST-corrected) — #20
refactor(api): replace stubs/ with OpenAPI-as-source-of-truth — #20
neversettle17-101 commentedon May 31, 2026
Closing — this work is being handled in #21 and #20.
feat(api): implement project routes with mock manager/store (#47)