Skip to content

feat(api): Gary header contract — read-only parsing of X-Gary-* headers#3

Merged
OriginalGary merged 2 commits intomainfrom
feat/gary-header-contract
May 5, 2026
Merged

feat(api): Gary header contract — read-only parsing of X-Gary-* headers#3
OriginalGary merged 2 commits intomainfrom
feat/gary-header-contract

Conversation

@OriginalGary
Copy link
Copy Markdown
Owner

Summary

  • Parses four X-Gary-* request headers on every /v1/chat/* call: X-Gary-Action-Id, X-Gary-Stage, X-Gary-Playbook, X-Gary-Sensitivity
  • All headers are optional; missing or unknown enum values → warn + treat as absent, never 4xx
  • Parsed context stored in a 20-entry in-memory ring buffer, surfaced in /api/monitoring/health as garyContextHistory
  • Requests with ≥1 Gary header are persisted to audit_log under action: "gary.context"
  • Four Gary headers added to CORS allowed headers

Files changed

File Change
src/lib/gary/context.ts New module: parser, enum sets, ring buffer, audit hook
src/sse/handlers/chat.ts Call parseGaryContext() at handleChat() entry
open-sse/utils/cors.ts Allow x-gary-* headers in CORS preflight
src/lib/monitoring/observability.ts Add garyContextHistory to health payload
src/app/api/monitoring/health/route.ts Wire getRecentGaryContexts()
tests/unit/gary-context.test.ts 14 tests: all headers, subsets, none, bad enum, long/unicode action-id, case-insensitivity, ring buffer cap

Test plan

  • 14 unit tests, all passing (node --import tsx/esm --test tests/unit/gary-context.test.ts)
  • No new TypeScript errors in changed files (pre-existing errors in health route are unrelated, not in typecheck:core scope)
  • Curl example: curl -H "X-Gary-Stage: plan" -H "X-Gary-Playbook: code-pr" -H "X-Gary-Action-Id: abc-123" -H "X-Gary-Sensitivity: tier-2" http://localhost:20128/v1/chat/completions ...

Design decisions for later prompts

  • Ring buffer is in-process memory; resets on server restart — fine for N=20 visibility, not for durable audit (that goes to audit_log)
  • parseGaryContext is called only from handleChat(), covering /v1/chat/completions, /v1/messages, /v1/completions, /v1/responses; embeddings/search/rerank do not fire (Gary pipeline only uses chat endpoints)
  • @omniroute/open-sse alias not touched (deferred from brand sweep)

🤖 Generated with Claude Code

Parse X-Gary-Action-Id, X-Gary-Stage, X-Gary-Playbook, and
X-Gary-Sensitivity on every /v1/chat/* request. Headers are optional;
unknown enum values log a warning and are treated as absent (never 4xx).
Parsed context is stored in a 20-entry in-memory ring buffer exposed via
the health endpoint's new garyContextHistory field. Requests with at
least one Gary header are also persisted to the audit_log table under
action "gary.context".

- src/lib/gary/context.ts: parser, enum sets, ring buffer, audit logging
- src/sse/handlers/chat.ts: parse Gary context at handleChat() entry
- open-sse/utils/cors.ts: allow four Gary headers in CORS preflight
- src/lib/monitoring/observability.ts: add garyContextHistory to health payload
- src/app/api/monitoring/health/route.ts: wire getRecentGaryContexts()
- tests/unit/gary-context.test.ts: 14 tests covering all parse cases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

CI Coverage Report

  • Coverage job: success
  • PR test policy: success

Coverage artifact was not available for this run.

1. sse-shim-contract.test.ts: revert assertions from @graze/open-sse back
   to @omniroute/open-sse — the TypeScript path alias rename was explicitly
   deferred; the test was incorrectly updated ahead of the rename.

2. executor-codex.test.ts: update expected x-codex-installation-id UUID
   from 7f06a8ee (derived from omniroute-codex-installation salt) to
   5594a324 (derived from the renamed graze-codex-installation salt,
   which was changed in the brand sweep because no existing deployments
   use the old value).

3. electron/package.json: rename name/productName/appId/author/homepage
   from OmniRoute to Graze so electron-builder produces the graze-desktop
   executable that smoke-electron-packaged.mjs expects, and update publish
   target from diegosouzapw/OmniRoute to OpenGaryBot/graze.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@OriginalGary OriginalGary merged commit f7cc266 into main May 5, 2026
63 checks passed
@OriginalGary OriginalGary deleted the feat/gary-header-contract branch May 5, 2026 18:40
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.

2 participants