From 3eb3a7d9aae409299badca90f7d5291e9daa926e Mon Sep 17 00:00:00 2001 From: mikemolinet Date: Mon, 11 May 2026 16:20:15 -0700 Subject: [PATCH] fix(messages.send): body_received is dict not flat string (empirical wire-shape) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- cueapi/resources/messages.py | 38 ++++++++++++++++++++++----------- tests/test_messages_resource.py | 38 +++++++++++++++++++++++++++------ 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/cueapi/resources/messages.py b/cueapi/resources/messages.py index 55c29fd..d95c06c 100644 --- a/cueapi/resources/messages.py +++ b/cueapi/resources/messages.py @@ -132,28 +132,40 @@ def send( response = self._client._post("/v1/messages", json=payload, headers=headers) - # Verify echo if requested. The substrate-side echo lands in - # response[_VERIFY_ECHO_FIELD] when Layer 1 is deployed; absent - # otherwise (no-op in that case). + # Verify echo if requested. Empirically-locked wire shape (probed + # 2026-05-11 ~23:17Z): substrate echoes back the PARSED request + # body as a dict under ``body_received``. To diff the message + # body specifically, extract ``body_received.body`` (str) and + # compare against the str the caller sent. + # + # Backward-compat: pre-Layer-1 substrate omits ``body_received`` + # entirely → SDK no-ops. Also defensive: if a future substrate + # rev returns body_received as a flat string (per the original + # spec text), the .get("body") chain returns None gracefully via + # the isinstance check, falling through to no-op rather than + # raising spuriously. if auto_verify and isinstance(response, dict): - received = response.get(_VERIFY_ECHO_FIELD) - if received is not None and received != body: + received_dict = response.get(_VERIFY_ECHO_FIELD) + received_body: Optional[str] = None + if isinstance(received_dict, dict): + received_body = received_dict.get("body") + elif isinstance(received_dict, str): + # Defensive: future substrate rev may flatten the echo. + received_body = received_dict + if received_body is not None and received_body != body: msg_id = response.get("id", "") - divergence = first_divergence_byte(body, received) - if divergence == -1 and len(body) != len(received): - # One body is a proper prefix of the other; length - # mismatch is the signal. Report at boundary of the - # shorter body. - divergence = min(len(body), len(received)) + divergence = first_divergence_byte(body, received_body) + if divergence == -1 and len(body) != len(received_body): + divergence = min(len(body), len(received_body)) raise BodyVerifyMismatchError( - f"Body received by substrate ({len(received)} bytes) differs from " + f"Body received by substrate ({len(received_body)} bytes) differs from " f"body sent ({len(body)} bytes); first divergence at byte " f"{divergence}. Likely cause: caller-side shell expansion of " f"$(...) / backticks / ${{VAR}} in the body arg before Python " f"received it. Mitigations: pass body via file (Path.read_text) " f"or use --message-file in cueapi-cli.", sent_body=body, - received_body=received, + received_body=received_body, first_divergence_byte=divergence, message_id=msg_id, ) diff --git a/tests/test_messages_resource.py b/tests/test_messages_resource.py index 86c2903..9eafdfc 100644 --- a/tests/test_messages_resource.py +++ b/tests/test_messages_resource.py @@ -236,12 +236,18 @@ def test_opt_out_omits_header(self): assert "X-CueAPI-Verify-Echo" not in headers def test_byte_identical_response_returns_normally(self): - """When server echoes back the same body, send() returns response.""" + """body_received.body matches sent body → success. + + Empirically-locked wire shape (probed 2026-05-11 ~23:17Z): + substrate echoes back PARSED request body as dict under + ``body_received``, NOT a flat string. SDK extracts + ``body_received.body`` to compare against sent body. + """ mock_client = MagicMock() mock_client._post.return_value = { "id": "msg_x", "delivery_state": "queued", - "body_received": "hi", + "body_received": {"to": "y", "body": "hi", "subject": None, "priority": 3}, } r = MessagesResource(mock_client) @@ -250,14 +256,19 @@ def test_byte_identical_response_returns_normally(self): assert result["id"] == "msg_x" def test_raises_on_body_mismatch(self): - """When server echo differs from sent body, raises BodyVerifyMismatchError.""" + """body_received.body differs from sent body → raises BodyVerifyMismatchError.""" from cueapi.exceptions import BodyVerifyMismatchError mock_client = MagicMock() mock_client._post.return_value = { "id": "msg_mutated", "delivery_state": "queued", - "body_received": "body with INJECT (caller's shell command-substituted)", + "body_received": { + "to": "y", + "body": "body with INJECT (caller's shell command-substituted)", + "subject": None, + "priority": 3, + }, } r = MessagesResource(mock_client) @@ -286,9 +297,9 @@ def test_no_op_when_substrate_omits_echo_field(self): def test_opt_out_skips_verify_even_if_substrate_echoes(self): """auto_verify=False: even if substrate sends body_received, don't check.""" mock_client = MagicMock() - # Mismatched echo but opt-out → no exception mock_client._post.return_value = { - "id": "msg_x", "body_received": "DIFFERENT BODY", + "id": "msg_x", + "body_received": {"to": "y", "body": "DIFFERENT BODY"}, } r = MessagesResource(mock_client) @@ -296,6 +307,21 @@ def test_opt_out_skips_verify_even_if_substrate_echoes(self): assert result["id"] == "msg_x" + def test_defensive_accepts_flat_string_body_received(self): + """Defensive: if a future substrate rev flattens body_received + back to a string (per the original spec wording), SDK still + verifies correctly. Belt + suspenders for spec drift.""" + mock_client = MagicMock() + mock_client._post.return_value = { + "id": "msg_x", + "body_received": "hi", # flat-string variant + } + r = MessagesResource(mock_client) + + result = r.send(from_agent="x", to="y", body="hi") + + assert result["id"] == "msg_x" + class TestFirstDivergenceByte: """Pure helper for diagnostic byte-position-of-first-difference."""