Skip to content

feat: standardize HTTP response shapes on snake_case#32

Merged
ethanj merged 3 commits intomainfrom
snake-case-wire-convention
Apr 22, 2026
Merged

feat: standardize HTTP response shapes on snake_case#32
ethanj merged 3 commits intomainfrom
snake-case-wire-convention

Conversation

@ethanj
Copy link
Copy Markdown
Contributor

@ethanj ethanj commented Apr 22, 2026

Summary

  • Establish snake_case as the canonical wire format for all HTTP responses, matching the request-side convention already in place and aligning with the AI API ecosystem (Anthropic, OpenAI, Stripe) that target OSS adopters are already integrated with.
  • Ingest response splits memory IDs by outcome (stored_memory_ids / updated_memory_ids). The accumulator previously lumped all touched IDs into one array, so callers couldn't distinguish creates from updates — now each list length matches its corresponding count.
  • New src/routes/memory-response-formatters.ts centralizes the translation layer; internal TS types stay camelCase, formatters are the only seam that touches the wire. All leaky routes that were passing internal types directly to res.json() now go through explicit formatters.
  • Search response's nested observability (retrieval / packaging / assembly traces) and scope echo flipped to snake_case — previously leaked camelCase via passthrough of internal summaries.

What changed

  • Ingest: emit episode_id, facts_extracted, memories_stored, memories_updated, memories_deleted, memories_skipped, stored_memory_ids, updated_memory_ids, links_created, composites_created.
  • Search: scope echo → { kind, user_id, workspace_id, agent_id, agent_scope }. Observability nested fields snake_cased (package_type, included_ids, dropped_ids, evidence_roles, has_current_marker, final_token_cost, primary_evidence_position, etc.).
  • Other routes: lesson_id, memory_id / version_count on audit trail, snake_case applied array on PUT /config, and explicit formatters for /stats, /consolidate, /decay, /cap, /reset-source, /reconcile, /lessons/stats, /audit/summary, /audit/recent.
  • Internal types: IngestResult gains storedMemoryIds + updatedMemoryIds; keeps memoryIds (union) for post-write processors that iterate over every touched memory regardless of outcome.

Follow-ups (out of scope)

  • OpenAPI spec declares only request bodies today. Adding response schemas would let npm run check:openapi catch future wire drift automatically.

Test plan

  • npx tsc --noEmit clean
  • All 28 HTTP-seam tests pass end-to-end (route-validation, research-consumption-seams, composed-boot-parity, versioned-mount, smoke)
  • npm run generate:openapi produces zero diff against checked-in spec
  • SDK companion PR (atomicmemory-sdk snake-case-wire-convention) merged in lockstep — maps the new snake_case shapes back to camelCase for SDK consumers

ethanj and others added 3 commits April 22, 2026 12:31
Establishes snake_case as the canonical wire format for all responses,
bringing them in line with the request-side convention already in place
and aligning with the AI API ecosystem (Anthropic, OpenAI, Stripe).

Ingest response now splits memory IDs by outcome (stored_memory_ids +
updated_memory_ids) so consumers can honestly distinguish creates from
updates — the accumulator previously lumped all touched IDs into a
single memoryIds array, losing the per-outcome split. Also flips the
search response's nested observability traces (retrieval / packaging /
assembly) to snake_case, fixes the scope echo, and routes every leaky
route (stats, consolidate, decay, cap, reset-source, reconcile,
lessons/stats, audit/summary, audit trail, lessons/report,
/:id/audit, PUT /config applied) through explicit formatters in a new
src/routes/memory-response-formatters.ts.

Internal TS types stay camelCase; formatters are the only seam that
touches the wire. IngestResult gains storedMemoryIds + updatedMemoryIds
and keeps memoryIds (union) for post-write processors.

Tests: route-validation, research-consumption-seams, and
composed-boot-parity updated to assert snake_case bodies. All 28
HTTP-seam tests pass end-to-end. OpenAPI regenerated with zero diff
(spec declares only request bodies today — response schemas are a
separate follow-up worth doing so CI's check:openapi catches wire
drift automatically).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2.45.0 (installed by npx on 2026-04-22) flags 17 pre-existing cyclomatic
complexity violations on untouched files (llm.ts, quick-extraction.ts,
repository-claims.ts, embedding.ts, supplemental-extraction.ts,
atomicmem-uri.ts). Main's last green CI ran 2.43.0 with the same code
and reported 0 above threshold. Pin until someone deliberately bumps
and addresses the flagged functions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
memory-route-config-seam.test.ts asserted that PUT /memories/config
returned 'maxSearchResults' in the applied array; after the wire flip
this is emitted as 'max_search_results'. The earlier audit missed it
because it was in a file that did not match the camelCase field-name
search pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ethanj ethanj merged commit 9d35270 into main Apr 22, 2026
1 check passed
@ethanj ethanj deleted the snake-case-wire-convention branch April 22, 2026 19:41
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