Skip to content

fix(intent-bridge): compare the required transcript call over canonical bytes - #333

Merged
imran-siddique merged 1 commit into
mainfrom
fix/intent-bridge-transcript-canonical-equality
Sep 12, 2026
Merged

fix(intent-bridge): compare the required transcript call over canonical bytes#333
imran-siddique merged 1 commit into
mainfrom
fix/intent-bridge-transcript-canonical-equality

Conversation

@imran-siddique

Copy link
Copy Markdown
Member

Closes #317.

verify_bridge() checked the required transcript's call against the executed call with before.get("tool_call") != tool_call (intent_bridge.py:232), which is host-language equality. Every other comparison in that function is over RFC 8785 canonical bytes. Python holds True == 1 and False == 0, nested objects included, so a transcript whose before.tool_call substituted a boolean for the corresponding integer had different JCS bytes from the call the authorization digested and was still reported as bound to the execution.

Reproduced at 10fcba4 in all four directions, with controls:

ACCEPTED   control: identical call
ACCEPTED   bool substituted for 1
refused    control: 0 vs 1
refused    control: unrelated value
ACCEPTED   integer 1 substituted for true
ACCEPTED   integer 0 substituted for false

The two refusals are what make the hole narrow rather than merely real: only the substitutions Python cannot distinguish got through.

Scope

The signed tool_call_digest was never affected. It is checked against the actual tool_call, so the executed call could not differ from the authorized one. What could differ was the separately supplied transcript, in the one place whose purpose is to show that the two agree.

Line 232 was also the only JSON-object equality comparison in src/agentrust_trace, so nothing else in the package is in this class.

Change

  • compare_digest over digest_jcs, reusing the digest already computed for the tool_call_digest check rather than hashing the executed call twice.
  • The isinstance guard stays in front of the digest call, and a transcript.before.tool_call that JCS has no form for raises AuthorizationMismatch rather than IntentBridgeError. A malformed transcript keeps its documented result class, which tests/test_public_functions_raise_what_they_document.py cares about.
  • No wire format, schema, scope, digest definition, or normative bridge semantics change.

Tests

Four parametrized substitutions, each asserting the substituted call is Python-equal to the executed one before asserting the refusal, so a test that stopped exercising the defect fails rather than passing quietly. Plus an unchanged-call control and a parametrized non-object case pinning the exception class.

Verified the corpus fails in both directions: the four substitution cases fail on origin/main and pass here; tests/test_intent_bridge.py is 43 passed.

The four repository-wide failures in test_generators_reproduce_fixtures.py and test_safe_integer_range.py are pre-existing Windows path-separator artifacts (schema\... against schema/...), confirmed on a clean origin/main worktree and unrelated to this change.

Reported by @altrudev, who scoped it precisely and anticipated the exception-class trap in the fix.

🤖 Generated with Claude Code

https://claude.ai/code/session_01X27MBo6tGVw1UmmchuGFnW

…al bytes

`verify_bridge()` checked `transcript.before.tool_call` against the executed
call with host-language equality while every other comparison in the function
is over RFC 8785 canonical bytes. Python holds `True == 1` and `False == 0`,
nested objects included, so a transcript substituting a boolean for the
corresponding integer had different JCS bytes from the call the authorization
digested and was still reported as bound to the execution.

The signed `tool_call_digest` was never affected; it is checked against the
actual `tool_call`. What could differ was the separately supplied transcript,
in the one place whose purpose is to show that the two agree.

The comparison now reuses the digest already computed for the
`tool_call_digest` check. The isinstance guard stays in front, and a
`transcript.before.tool_call` that JCS has no form for raises
`AuthorizationMismatch` rather than `IntentBridgeError`, so the documented
result class for a malformed transcript is unchanged.

Regression coverage pins all four substitutions and asserts each one is
Python-equal, so a test that stopped exercising the defect would fail rather
than pass quietly. An unchanged-call control and a parametrized non-object
case cover the other direction.

Reported by @altrudev in #317.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X27MBo6tGVw1UmmchuGFnW
@imran-siddique
imran-siddique requested review from a team and lywinged as code owners September 12, 2026 04:23
@imran-siddique
imran-siddique merged commit 57d5165 into main Sep 12, 2026
9 checks passed
@imran-siddique
imran-siddique deleted the fix/intent-bridge-transcript-canonical-equality branch September 12, 2026 04:25
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.

intent_bridge transcript comparison inherits Python bool/int equality

1 participant