Skip to content

fix(intent-bridge): compare transcript call by canonical JSON - #318

Open
altrudev wants to merge 1 commit into
agentrust-io:mainfrom
altrudev:fix/intent-bridge-transcript-json-equality
Open

fix(intent-bridge): compare transcript call by canonical JSON#318
altrudev wants to merge 1 commit into
agentrust-io:mainfrom
altrudev:fix/intent-bridge-transcript-json-equality

Conversation

@altrudev

Copy link
Copy Markdown
Contributor

Closes #317.

What

verify_bridge() now compares transcript.before.tool_call with the executed tool_call using the bridge's existing RFC 8785/JCS byte definition rather than Python container equality.

Python container equality aliases booleans and the corresponding integers (True == 1, False == 0). That let a required transcript carry a JSON-distinct call while the transcript-binding check still treated it as equal to the executed call.

The signed tool_call_digest check was already correct and remains unchanged. This patch only makes the separately supplied transcript use the same JSON identity relation as the bridge's canonical digest layer.

Regression coverage

Focused tests cover:

  • unchanged call -> existing success;
  • executed integer 1 vs transcript boolean true -> AuthorizationMismatch;
  • executed integer 0 vs transcript boolean false -> AuthorizationMismatch;
  • reverse-direction boolean/integer substitutions -> AuthorizationMismatch.

The tests also pin that ordinary Python container equality would consider each pair equal while the bridge's JCS digests differ, so reverting to the old comparison reopens the defect.

Scope

No schema, wire-format, scope, digest, signature, or authorization semantics change. The existing AuthorizationMismatch result and diagnostic are preserved.

The branch is based directly on upstream a247244fb0bb97aea4e60d655f36d445cca18cd5. Hosted CI is not claimed until the repository executes it; no manual workflow run is requested.

DCO trailers are present on the contribution commits. AI-assistance disclosure: ChatGPT assisted with repository triage, duplicate search, reproduction design, implementation drafting, and regression construction. altrudev reviewed the bounded claim and remains responsible for the contribution.

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

🔴 Contributor Check: HIGH

Check Result
Profile HIGH
Credential LOW
Overall HIGH

Automated check by AgenTrust Contributor Check.

@github-actions github-actions Bot added the needs-review:HIGH Contributor check flagged HIGH risk label Sep 10, 2026

@lywinged lywinged left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an approval waiting on one addition. Reviewed at 63ae280 on a247244. It is posted as a comment rather than an approval because the PR is a draft and the addition below will move the head, and the maintainer-approval check only counts an approval on the commit it was given on; I will approve the head that carries the entry once you mark it ready.

The defect is real and the fix is the right shape. On main, a signed and digested call of {"approved": 1} with a transcript before.tool_call of {"approved": true} passes the transcript check; on this head it is refused. It is the identity relation the #300 review asked for in the tool-schema check, applied here where the transcript is compared: the signature covers RFC 8785 bytes, so comparing the two calls as _jcs bytes puts the transcript under the same relation as the digest it sits beside.

What I measured rather than read:

  • Five lines in and one out in intent_bridge.py, reusing the module's own _jcs wrapper and the compare_digest it already imports.
  • The five new tests pass. With the old comparison restored, four fail and the unchanged-call control passes.
  • The four CI steps with the pinned install, on 3.11 and on 3.12: green, 1407 passed and 1 skipped.
  • The new canonicalisation step cannot leak. A transcript call carrying 2^60 or NaN raises IntentBridgeError from _jcs, because IntegerDomainError and FloatDomainError subclass CanonicalizationError in the pinned rfc8785 0.1.4.

One thing the body does not say, and the entry below should. "The existing AuthorizationMismatch result and diagnostic are preserved" holds for a call that differs. For a transcript call JCS cannot serialise, the exception moves: main raised AuthorizationMismatch from the inequality, this head raises IntentBridgeError from _jcs before the comparison is reached. Same documented base type, and the treatment the authorization, the declaration and the executed call already get, so nothing to change in the code.

The one thing to add: a CHANGELOG.md entry under Unreleased, Fixed. Both code fixes already there carry one, and it is the record a reader of the next release will have. The shape the others use: what was accepted, what is now refused, that the signed digest was never affected, and the exception note above.

…ust-io#317)

Rebased onto upstream 760cc3f after the CHANGELOG-only conflict identified by maintainers.

Signed-off-by: Altru.dev <altrudevelop@gmail.com>
@altrudev
altrudev force-pushed the fix/intent-bridge-transcript-json-equality branch from 80dbb2e to ac1a08f Compare September 11, 2026 17:38
@imran-siddique

Copy link
Copy Markdown
Member

Second conflict in an hour, same file, and this one is mine again: merging #322 re-conflicted #318 and #323 on the CHANGELOG.md block. You rebased the first round inside twenty minutes and I am not going to ask you to do that again for the same reason.

Please drop the CHANGELOG.md hunk from #318 and #323 entirely. Nothing else changes. Force-push without it and both become mergeable immediately and stop being able to conflict each other or anything else in the queue.

I will write the changelog entries for all of them in one maintainer commit once they land, using the wording from your PR bodies. You lose nothing: the entries still say what your changes did, and the attribution is in the commits and the PRs.

Why this rather than another rebase. Every open PR in this repository appends to the same block, so each merge conflicts every other one, and the number of rebases grows with the size of the queue rather than with the size of anyone's change. Two of your PRs have now paid for that twice. The durable fix is a fragment directory, one file per change assembled at release, and I am looking at it separately.

intent_bridge.py in #318 and provenance.py in #323 both merge clean against current main. The changelog is the only thing in the way of either.

@Qiang-Xu Qiang-Xu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review:HIGH Contributor check flagged HIGH risk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

intent_bridge transcript comparison inherits Python bool/int equality

4 participants