Skip to content

feat: declare OpenAPI response schemas for every route#35

Merged
ethanj merged 1 commit intomainfrom
openapi-response-schemas
Apr 22, 2026
Merged

feat: declare OpenAPI response schemas for every route#35
ethanj merged 1 commit intomainfrom
openapi-response-schemas

Conversation

@ethanj
Copy link
Copy Markdown
Contributor

@ethanj ethanj commented Apr 22, 2026

Summary

Every /v1 route in the OpenAPI spec now carries a concrete response schema instead of the permissive z.object({}).passthrough() placeholder that shipped before. npm run check:openapi (CI step) will now catch wire-shape drift between the formatters and the committed spec — the guardrail missing from the snake_case wire flip in #32.

What's in the PR

  • src/schemas/responses.ts (new, ~400 lines) — Zod schemas mirroring every response shape emitted by src/routes/memory-response-formatters.ts and the inline route literals:
    • Top-level: Ingest, Search, Expand, List, GetMemory, Stats, Health, ConfigUpdate, Consolidate (scan∪execute discriminated union), Decay, Cap, LessonsList, LessonStats, LessonReport, Reconciliation, ReconcileStatus, ResetSource, Success, MutationSummary, AuditRecent, AuditTrail, Trust, ConflictsList, ResolveConflict, AutoResolveConflicts
    • Sub-schemas for nested wire shapes: Scope, MemoryRow, SearchMemoryItem, TierAssignment, LessonCheck, Consensus, RetrievalTrace / PackagingTrace / AssemblyTrace / Observability, ClusterCandidate, DecayCandidate, LessonRow, ClaimVersionRow, MemoryConflictRow, HealthConfig, AuditTrailEntry. These stay non-exported — they only compose the top-level exports.
  • src/schemas/openapi.ts — every ok('...', GenericObjectResponse) replaced with its specific schema import. Error envelopes (400/404/410/500) unchanged.
  • openapi.yaml + openapi.json regenerated deterministically — +3788 lines now document real response bodies.

Follow-up worth tracking

The route handlers don't yet parse their response through these schemas before sending (runtime drift detection). The spec-level check catches intentional formatter changes that forget to update schemas; runtime parsing would catch accidental Record<unknown> leaks but adds per-request cost. Noted as a follow-up.

Test plan

  • npx tsc --noEmit clean
  • All 1037 core tests pass
  • npm run generate:openapi produces zero diff after regen
  • npm run check:openapi passes locally
  • Pre-commit fallow audit passes (4 changed files, 0 issues)
  • CI green end-to-end

Every /v1 route now carries a concrete response schema in the spec
instead of the permissive `z.object({}).passthrough()` placeholder.
`npm run check:openapi` will now catch wire-shape drift between the
formatters in memory-response-formatters.ts and the committed spec —
the guardrail I flagged as missing during the snake_case wire flip.

- New src/schemas/responses.ts declares Zod schemas for every emitted
  shape (25 top-level + internal sub-schemas): IngestResponse,
  SearchResponse (+ nested observability / consensus / lesson_check /
  tier_assignments / scope subschemas), MemoryRow / LessonRow /
  ClaimVersionRow / MemoryConflictRow for passthrough DB rows,
  HealthConfig shared by /health and /config, Consolidation
  (scan|execute discriminated union), Decay, Cap, LessonStats,
  LessonReport, Reconciliation, ReconcileStatus, ResetSource,
  MutationSummary, AuditTrailEntry/AuditTrail/AuditRecent, Trust /
  ConflictsList / ResolveConflict / AutoResolveConflicts, and the
  shared Success envelope.
- src/schemas/openapi.ts imports those schemas and wires each route's
  200 response to the right one. Error envelopes (400/404/410/500)
  stay as-is.
- openapi.yaml + openapi.json regenerated (+3788 lines of declared
  response shapes).

All 1037 core tests pass; check:openapi produces no diff after regen.

Follow-up worth tracking: the route handlers don't yet parse their
response through these schemas before sending (runtime drift
detection). The spec-level check is enough to catch intentional
formatter changes that forget to update the schemas; runtime parsing
would catch accidental Record<unknown> leaks but adds a per-request
cost. Noted in tech-debt.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ethanj ethanj merged commit eba064e into main Apr 22, 2026
1 check passed
@ethanj ethanj deleted the openapi-response-schemas branch April 22, 2026 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant