Skip to content

fix: hold the last three entry points to the type they document - #9

Merged
lywinged merged 1 commit into
mainfrom
fix/entry-point-type-contract-part-2
Aug 27, 2026
Merged

fix: hold the last three entry points to the type they document#9
lywinged merged 1 commit into
mainfrom
fix/entry-point-type-contract-part-2

Conversation

@lywinged

Copy link
Copy Markdown
Owner

Completes what #6 began and deliberately deferred. That change guarded sign.verify_record and sign.jwk_thumbprint and left four functions of the same class alone, because open upstream PRs agentrust-io#225 and agentrust-io#227 were editing them and carrying the fix here would have collided with work someone else was mid-way through.

Those PRs have landed. Re-running the same sweep against the merged tree shows what they closed and what they did not:

before after agentrust-io#225/agentrust-io#227/agentrust-io#229 after this
provenance.tool_catalog_hash 4 of 12 0 (closed by agentrust-io#229) 0
provenance.verify_record 11 of 12 11 0
provenance.check_tool_catalog 11 of 12 11 0
content_marking.verify_assertion 2nd arg unguarded 0

agentrust-io#225 added _as_object for record["identity"] and record["tool_catalog"]. Neither field is reachable until the record itself is a mapping, so a guard on a field inside a record cannot close the record's own type. #6's body said that as a prediction; it is now a measurement.

AttributeError is not the ProvenanceError that verify_record documents, so it passes straight through a caller's except ProvenanceError.

The one that is worse than a crash

content_marking.verify_assertion never checked that record_bytes were bytes. bytes(5) is b"\x00\x00\x00\x00\x00", so an int was hashed, failed to match, and the caller was told:

the record at https://example.com/r.json does not match the assertion: computed sha256:8855508a... The record changed after the asset was signed, or the URL is serving a different one.

Nothing was wrong with the record or the URL. A crash says the call was wrong. This said somebody else's server was serving different evidence, specifically, confidently, with a digest attached. build_assertion has guarded the same argument all along.

test_an_int_no_longer_reports_a_record_mismatch asserts the absence of that diagnosis by name, so it cannot come back quietly.

After this

Every public entry point in the package reports zero leaks under the sweep:

sign.verify_record                0    provenance.verify_record          0
sign.jwk_thumbprint               0    provenance.check_tool_catalog     0
validate.validate_json            0    provenance.tool_catalog_hash      0
validate.iter_errors              0    content_marking.verify_assertion  0
intent_bridge.digest_jcs          0    intent_bridge.sign_bridge         0

sign.sign_record and provenance.sign_record still answer TypeError to a non-mapping. They are producers rather than verifiers, document no error contract, and TypeError is Python's own answer to that call. #6 left them and so does this.

A changelog sentence, fixed because it is in the entry above the one I added

The em dash sweep in agentrust-io#230 removed the punctuation from the entry agentrust-io#227 merged and left:

Valid JSON is not always an object that is an array, a string, a number, null, and a bool are all valid top-level JSON␣␣and record_bytes is ...

"an object that is an array" states the opposite of the point, and there is a leftover double space where the second dash was. tools/check_dashes.py cannot catch this: the removal was clean and the sentence is what broke.

Checks

Thirty-two tests, each shown load-bearing by removing the guard it covers:

guard removed failures
verify_assertion record_bytes 12
provenance.verify_record record 11
provenance.check_tool_catalog record 10

1031 passed, 1 skipped. Ruff clean. tools/check_dashes.py reports nothing in any file this repository shares with upstream.

This branch also carries a merge of upstream main (five commits: agentrust-io#225, agentrust-io#227, agentrust-io#228, agentrust-io#229, agentrust-io#230). The conflicts were in four files and all but CHANGELOG.md were pure em dash removal; those were resolved by taking this fork's content and adopting upstream's own punctuation choice for each identical sentence rather than inventing one. CHANGELOG.md was an additive merge of both sides' entries.


Generated by Claude Code

Completes what #6 began and deliberately deferred. That change guarded
`sign.verify_record` and `sign.jwk_thumbprint` and left four functions of the same
class alone, because open PRs agentrust-io#225 and agentrust-io#227 were editing them and carrying the fix
there would have collided with work someone else was mid-way through.

Those PRs have landed. Re-running the same sweep against upstream `main` shows
what they closed and what they did not:

    provenance.tool_catalog_hash    4 of 12 -> 0    closed by agentrust-io#229
    provenance.verify_record       11 of 12 -> 11   not closed
    provenance.check_tool_catalog  11 of 12 -> 11   not closed
    content_marking.verify_assertion  second argument, not closed

`agentrust-io#225` added `_as_object` for `record["identity"]` and `record["tool_catalog"]`.
Neither field is reachable until the record itself is a mapping, so a guard inside
a record cannot close the record's own type. That was stated in #6's body as a
prediction and is now a measurement.

`AttributeError` is not the `ProvenanceError` `verify_record` documents, so it
passes through a caller's `except ProvenanceError` untouched.

`content_marking.verify_assertion` is the one worth naming separately, because it
was worse than a crash rather than merely undocumented. It never checked that
`record_bytes` were bytes, and `bytes(5)` is five zero bytes, so an int was hashed,
failed to match, and the caller was told:

    the record at <url> does not match the assertion ... The record changed after
    the asset was signed, or the URL is serving a different one.

Nothing was wrong with the record or the URL. A crash says the call was wrong;
this said somebody else's server was serving forged evidence, specifically, with a
digest attached. `build_assertion` has guarded the same argument all along.

After this, every public entry point in the package reports zero leaks under the
sweep, including the two `sign.py` functions #6 fixed.

Also fixes a sentence in the changelog entry agentrust-io#227 merged. The em dash sweep in
agentrust-io#230 removed its punctuation and left "Valid JSON is not always an object that is
an array", which states the opposite of the point. `tools/check_dashes.py` cannot
catch this: the removal was clean and the sentence is what broke.

Thirty-two tests, each shown load-bearing by removing the guard it covers: 12, 11
and 10 failures respectively. 1031 passed, 1 skipped. Ruff clean, and
`tools/check_dashes.py` reports nothing in any file this repository shares with
upstream.

Signed-off-by: Louielunz <48041247+lywinged@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

Contributor Check: UNKNOWN

Check Result
Profile UNKNOWN
Credential LOW
Overall UNKNOWN

Automated check by AgenTrust Contributor Check.

@github-actions github-actions Bot added the needs-review:UNKNOWN Contributor check flagged UNKNOWN risk label Aug 27, 2026
@lywinged
lywinged merged commit 9210e27 into main Aug 27, 2026
4 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review:UNKNOWN Contributor check flagged UNKNOWN risk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant