Skip to content

feat(training-agent): JSON-only Accept + idempotency storyboard fixes (group C start)#2832

Merged
bokelley merged 2 commits into
mainfrom
bokelley/nairobi-v17
Apr 22, 2026
Merged

feat(training-agent): JSON-only Accept + idempotency storyboard fixes (group C start)#2832
bokelley merged 2 commits into
mainfrom
bokelley/nairobi-v17

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Starts Group C (individual specialisms). Closes 4 more storyboard steps; idempotency goes from 3 failures → 1.

  • Accept header negotiation: MCP Streamable HTTP transport rejects JSON-only Accept with 406. SDK storyboard rawMcpProbe (used for `omit_idempotency_key` vectors) sends JSON-only and hit that wall. Rewrite the header (including Node's `rawHeaders` array — which `@hono/node-server` actually reads) to include SSE when client sent JSON-only, enable `enableJsonResponse` on the transport for single-shot JSON responses. No hot-path regression (buyer agents using @adcp/client already send both).
  • Emit `replayed: false` on fresh `create_media_buy` responses: storyboard uses `field_value allowed_values: [false]` which fails on omission despite the spec's MAY-omit clause. Scoped to `create_media_buy` only — other mutating tools continue MAY-omit because several SDK response schemas aren't `.passthrough()` and reject the extra key. Tracked in adcp-client#774.
  • Strip `recovery` from `IDEMPOTENCY_CONFLICT`: SDK's new cross-step assertion `idempotency.conflict_no_payload_leak` enforces a narrow envelope allowlist (code, message, status, retry_after, correlation_id, request_id, operation_id). Our `recovery: correctable` hint fell outside. Drop it on this specific error to satisfy the stolen-key read-oracle defense; other errors keep recovery.

Impact

  • 43/56 clean, 321 steps passing (up from 43/56, 317 after main merge — +4 steps)
  • `idempotency` universal: 3 failures → 1 (webhook-dedup on replay still pending investigation)
  • `property_lists`: unchanged (avoided regression by scoping replayed)

Test plan

  • Typecheck clean
  • 1796/1836 server unit tests passing (1 flaky C2PA test unrelated)
  • Storyboards: 43/56 clean, above CI floor (36/295)
  • Direct-POST probe confirms JSON-only Accept now returns 200 with Content-Type: application/json
  • Storyboard idempotency confirmed +2 steps closing

Follow-ups

  • adcp-client#774 (schema passthrough) — unblocks emitting `replayed: false` on all mutating tools
  • Webhook dedup on idempotency replay (last remaining idempotency failure)

🤖 Generated with Claude Code

bokelley and others added 2 commits April 22, 2026 00:43
…ard fixes

- MCP Streamable HTTP transport requires Accept to list both
  application/json AND text/event-stream or returns 406. SDK storyboard
  probes send json-only and fail the idempotency missing-key vector.
  Rewrite Accept (including Node rawHeaders which @hono/node-server
  actually reads) to include SSE when client sent json-only, and enable
  enableJsonResponse on the transport for single-shot JSON responses.
- Emit replayed:false on create_media_buy fresh responses (universal
  idempotency storyboard requires the literal false via allowed_values,
  not omission). Scoped to create_media_buy; adcp-client#774 tracks
  making all response schemas passthrough so we can emit everywhere.
- Drop `recovery: correctable` from IDEMPOTENCY_CONFLICT envelope —
  SDK's idempotency.conflict_no_payload_leak assertion enforces a
  narrow allowlist (code, message, status, retry_after, correlation_id,
  request_id, operation_id) against stolen-key read oracle.

43/56 clean, 321 steps passing (up from 317). idempotency 3F → 1F.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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