Skip to content

fix(messages.send): body_received is dict not flat string (empirical wire-shape)#40

Merged
mikemolinet merged 1 commit into
mainfrom
fix/body-verify-echo-wire-shape
May 11, 2026
Merged

fix(messages.send): body_received is dict not flat string (empirical wire-shape)#40
mikemolinet merged 1 commit into
mainfrom
fix/body-verify-echo-wire-shape

Conversation

@mikemolinet
Copy link
Copy Markdown
Collaborator

Summary

Hotfix for PR #39 (Phase 2 auto-verify). Empirically verified 2026-05-11 ~23:17Z via direct curl probe: substrate's X-CueAPI-Verify-Echo response includes body_received as the PARSED request body (a dict with to/body/subject/priority/etc fields), NOT a flat string per the original spec wording.

Bug

PR #39 compared response["body_received"] (dict) against body (str) — type mismatch caused EVERY default-auto-verify send to raise BodyVerifyMismatchError with a degenerate divergence position.

Fix

Defensive shape-handling via isinstance check:

  • If body_received is dict: extract body_received["body"] (current empirical shape)
  • If body_received is str: use as-is (future shape per substrate fix requested by cue-pm)
  • If neither: no-op (backward-compat with pre-Layer-1 substrate)

This means cueapi-python correctly verifies under BOTH the current substrate behavior AND the post-primary-fix shape (cue-pm fired urgent cue to primary at 23:20Z requesting body_received → STRING). Zero re-work needed when fix lands.

Test plan

  • 23 of 23 messages_resource tests pass
  • Tests updated to use the empirical dict shape
  • 1 new defensive test pins the backward-flat-string path

Urgency

This hotfix is unblocking — every auto-verify send was broken in main until this lands. Recommend admin-merge ASAP.

🤖 Generated with Claude Code

…wire-shape)

Hotfix for cueapi-python PR #39. Empirically verified 2026-05-11 ~23:17Z
via direct curl probe: substrate's X-CueAPI-Verify-Echo response
includes ``body_received`` as the PARSED request body (a dict with
to/body/subject/priority/etc fields), NOT a flat string per the
original spec wording.

Before fix: SDK compared ``response["body_received"]`` (dict) against
``body`` (str) — type mismatch → ALWAYS raised BodyVerifyMismatchError
on every default-auto-verify send. Regression in PR #39.

After fix: SDK extracts ``body_received.body`` (str) and compares
against sent body (str). Includes defensive isinstance check that
falls through gracefully if a future substrate rev flattens the echo
back to a string.

Tests updated to use the dict shape; 1 new defensive test pins the
backward-flat-string path. 23 of 23 messages tests pass.

This hotfix is unblocking — every auto-verify send was broken in main
until this lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@govindkavaturi-art govindkavaturi-art enabled auto-merge (squash) May 11, 2026 23:21
@mikemolinet mikemolinet merged commit aae27eb into main May 11, 2026
3 checks passed
@mikemolinet mikemolinet deleted the fix/body-verify-echo-wire-shape branch May 11, 2026 23:21
mikemolinet added a commit that referenced this pull request May 11, 2026
… B + bonus) (#41)

* cues.fire: auto_verify + sha256 constant-cost path (Phase 2 follow-on B + bonus)

Mike body-verify directive 2026-05-11. cue-pm unblocked these
follow-ons at ~23:48Z after primary's substrate-fix (body_received
flat-string + body_received_sha256 hashing body field bytes) verified
on prod.

Changes:

cueapi/resources/cues.py:
- New ``auto_verify=True`` kwarg on ``CuesResource.fire``. Default
  verify-on (symmetric with MessagesResource.send). When set:
  - sends X-CueAPI-Verify-Echo: true request header
  - pre-computes sha256(canonical-JSON-of-body) client-side
  - on response: extracts body_received (defensive isinstance for
    both string post-fix shape AND dict pre-fix shape — sibling of
    PR #40 hotfix pattern)
  - if body_received_sha256 present: constant-cost hex compare first,
    fall back to string compare on sha drift (JSON-canonicalization
    differences could cause spurious sha mismatch)
  - on confirmed drift: raises BodyVerifyMismatchError with
    sent_body, received_body, first_divergence_byte, message_id
    (= execution_id for fire) attributes.

tests/test_cues_resource.py:
- Existing 3 TestFire tests updated to expect the new headers kwarg
  in the post call (X-CueAPI-Verify-Echo: true is now default-on).
- New TestFireAutoVerify class with 5 tests pinning:
  - Default adds verify-echo header
  - --auto_verify=False omits header
  - Byte-identical sha256 match passes
  - No-op when substrate omits echo fields (pre-Layer-1 backward-compat)
  - Opt-out skips verify even if substrate echoes

All 39 tests (messages + cues resource units) pass.

CHANGELOG entry under [Unreleased].

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

* fix(cues.fire): default auto_verify=False until substrate echo semantics locked

CI revealed: substrate's /v1/cues/{id}/fire echoes a pydantic-after-parse
body that includes server-side default-population (e.g. empty {} request
gets echoed as 16-byte populated-defaults JSON). Client's canonical-JSON
serialization diverges from substrate's defaulted echo → spurious
BodyVerifyMismatchError on integration tests.

Mitigation: default auto_verify=False on CuesResource.fire. Callers opt-
in via auto_verify=True when their serialization aligns with substrate's
echo semantic. Once cueapi-primary locks per-field echo semantics for
fire (sibling to the body_received semantic locked for /v1/messages),
flip default to True.

Existing fire tests reverted to expect headers={} (no auto-verify).
TestFireAutoVerify class tests updated to explicitly pass auto_verify=True.

39 of 39 messages + cues resource tests pass. Integration tests in
test_cues.py no longer raise spurious BodyVerifyMismatchError.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mikemolinet added a commit that referenced this pull request May 12, 2026
…dy coverage, bump audit to 2026-05-12 (#43)

Manifest was dated 2026-05-07 and missing the PR-1b event-emit endpoint
coverage (4 endpoints) plus the body-verify Phase 2 + inline_body
extensions that shipped 2026-05-09 → 2026-05-12. Brings the manifest
back in sync with SDK head.

Endpoints added to `endpoints_covered`:
- POST /v1/agents/{ref}/subscriptions (subscriptions_create, PR #38;
  inline_body kwarg in PR #42 / cueapi #791 Item 1)
- GET /v1/agents/{ref}/subscriptions (subscriptions_list, PR #38)
- DELETE /v1/agents/{ref}/subscriptions/{sub_id} (subscriptions_delete,
  PR #38)
- GET /v1/agents/{ref}/events (events_pull, PR #38)

Updates to existing entries:
- POST /v1/messages — added auto_verify body-verify Phase 2 (PR #39 +
  #40, cueapi/cueapi #795 + #798 parity)
- POST /v1/cues/{id}/fire — note that #33 shipped (was "in-flight")
- GET /v1/agents/roster — note that #35 shipped (was "in-flight")
- GET /v1/agents/{ref}/presence — note that #35 shipped (was "in-flight")

Replaced `in_flight_ports_2026_05_07` section with
`ports_shipped_2026_05_08_to_2026_05_12` (now-resolved entries) plus a
near-empty `ports_in_flight_2026_05_12` placeholder for future ports.

Backlog row: cmp1vukmc.

Out of scope:
- `model_drift` section walk-through (Cue/Execution/Worker missing
  fields) — PR #31 just landed the Execution + Worker + Agent +
  Message additive models; a fuller `model_drift` refresh deserves a
  separate audit pass against the now-shipped models to figure out
  what's still drifting.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant