Repository navigation
docs(schema): name both digest algorithms wherever the schema admits both - #438
Conversation
…both
All twelve digest fields in schema/trace-claim.json carry the same
pattern shape, ^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$, so each takes
sha256: or sha384:. Six of the twelve rows on the schema reference page
described their field more narrowly: model.weights_digest,
policy.bundle_hash, tool_transcript.hash and build_provenance.digest
named sha256: alone, and the two appraisal.re_execution digests named no
algorithm. The other six named both, four of them in the form the
corrected rows now use.
A producer reading one of the narrower rows writes the narrower record,
and a verifier author reading one may refuse a record the schema
accepts. Section 3.1.3 is where the specification settles a question of
this kind: after giving the sha256: construction for
delegation.parent_record_hash it names SHA-384 as the permitted
alternative, per the schema pattern. The page under-described a
constraint its own opening names the schema as the source of.
test_schema_doc_coverage.py guarded that every field has a row and every
fixed value appears in its field's row. It never read what a row claims
about a pattern, so a row could document a field into existence and
misdescribe it. It now also fails when a row does not name every
algorithm its field's pattern admits.
The module docstring drew that boundary deliberately, on the ground that
whether a description agrees with the spec is not mechanically decidable.
This check does not cross it. Agreement with the field's own pattern is
a comparison against a machine-readable constraint, not a judgement
about prose, which is why the failure can name the field and the missing
algorithm. The docstring now records the distinction rather than leaving
the file disclaiming a check it performs.
The check asks the pattern with a probe value rather than searching its
text. The alternation factors the prefix out, so it contains neither
sha256 nor sha384 as text; a search for either finds no algorithm,
concludes the field constrains none and skips it. The first version did
that and reported no gaps anywhere. A test pins the behaviour.
The field set comes from the walk this file already uses. Resolving each
field's subschema follows properties and items only, so a member
declared solely inside a conditional branch raises rather than being
passed over silently.
Reverting each corrected row in turn reds the guard and names that field
alone, including build_provenance.digest, which shares its member name
with two other digest fields.
Editorial: no schema, wire-format or normative change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: opento-suggestions <opentosuggestionsofficial@gmail.com>
imran-siddique
left a comment
There was a problem hiding this comment.
Approved. Merging #437 left this conflicting on CHANGELOG.md only; a rebase onto main keeping both entries is all it needs, and I'll merge once it's green.
rajnisht7
left a comment
There was a problem hiding this comment.
Reviewed the head 7ca1d25, looks good, the six rows now correctly name both algorithms which the schema already admits and the new guard in test_schema_doc_coverage.py is also a good addition, probe-based rather than text search so it actually works with the factored pattern
a few non-blocking points:
-
_DIGEST_ALGORITHMSis hardcoded to sha256/384. Fine for now, but a future algorithm added to the pattern wouldn’t be detected until the constant is updated. Already acknowledged in the PR body. -
The check only looks at pattern now issue for now as every digest field is pattern-only but if something ever moved to enum/oneOf/etc. this wouldnt catch it
|
@opento-suggestions both approvals stand, and the only thing between this and merge is a |
…s-in-schema-reference Conflict in CHANGELOG.md only: the agentrust-io#190 CitationCheck entry and the agentrust-io#324 MCP retry example entry landed in the same Unreleased block as this branch's entry. Kept all three, main's first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: opento-suggestions <opentosuggestionsofficial@gmail.com>
…measurement agentrust-io#477 added appraisal.platform_measurement.measurement after this branch was cut. Its pattern is the same ^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$ as the other digest fields, and its row named no algorithm, so merging main reddened the guard this branch adds: the field and both algorithms were named, and the counterfactual test failed because its baseline was no longer clean. The row now takes the form the other corrected rows use. That makes thirteen digest fields, of which seven rows were narrower than the pattern, and the CHANGELOG entry carries those counts. The commit message of 7ca1d25 says twelve and six, which was true of the tree it was cut from. Reverting this row alone reds the guard and names that field alone. sha512: is refused by its pattern, as by the other twelve. Editorial: no schema, wire-format or normative change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: opento-suggestions <opentosuggestionsofficial@gmail.com>
b1251d6
|
@imran-siddique merged main into the branch and pushed ( One more thing came in with main. #477 added The push dismissed both approvals, so the gate is waiting on a re-review of |
imran-siddique
left a comment
There was a problem hiding this comment.
Re-approving at b1251d6. Since my 09-30 approval the branch gained the main merge, the appraisal.platform_measurement.measurement row the new guard caught, and the updated counts; the push dismissed the earlier approval, so this is not a second review round.
What this changes
All thirteen digest fields in
schema/trace-claim.jsoncarry the same pattern shape,^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$, so each takessha256:orsha384:. Seven of the thirteen rows ondocs/schema.mddescribed their field more narrowly than that.model.weights_digest,policy.bundle_hash,tool_transcript.hashandbuild_provenance.digestnamedsha256:alone; the twoappraisal.re_executiondigests andappraisal.platform_measurement.measurementnamed no algorithm. The other six named both, in three different forms: four assha256:orsha384:,delegation.parent_record_hashassha256:/sha384:, andruntime.measurementby naming the platform each algorithm belongs to. The corrected rows take the form four of them already use.The page under-describes a constraint whose source it names in its own opening line,
schema/trace-claim.json. A producer reading one of the narrower rows writes the narrower record, and a verifier author reading one may refuse a record the schema accepts. Section 3.1.3 is where the specification settles a question of exactly this kind: having given thesha256:construction fordelegation.parent_record_hash, it addswith sha384: and SHA-384 as the permitted alternative, per the schema pattern, which is the specification treating the pattern as the authority on which algorithms a field admits.tests/test_schema_doc_coverage.pycould not catch it. The file guards that every schema field has a row in its own section and that everyenumandconstvalue appears in its field's row. It never read what a row claims about apattern, so a row could document a field into existence and still misdescribe it. It now also fails when a row does not name every algorithm its field's pattern admits, and the failure names the field and the missing algorithm.That boundary is deliberate and it has a history worth naming rather than leaving a reviewer to find. The file arrived in #365, which answered the coverage half of #247, and the scope recorded there is field-and-enum coverage checked directly against the page. This change widens that scope. I am not arguing it does not: I am arguing the widened edge is still on the decidable side of the line the docstring draws, and the module docstring moves with it so the file says where its edge now is. The docstring's reason for not checking descriptions is that whether prose agrees with the spec is not mechanically decidable, and a guard reaching past what it can decide produces failures nobody can act on. That reason holds and is left standing. It does not cover this case: agreement with the field's own pattern is a comparison against a machine-readable constraint rather than a judgement about wording, which is why the failure can name the field and the missing algorithm instead of asking a reader to adjudicate. The docstring now records the distinction, rather than leaving the file opening with a disclaimer of a check it performs.
The check offers the pattern a value rather than searching its text. The alternation factors the prefix out, so the pattern contains neither
sha256norsha384as text; a search for either finds no algorithm, concludes the field constrains none, and skips it.sha512:is offered too, so the check is known to be capable of a negative, and every one of the thirteen refuses it.The field set comes from the walk this file already uses rather than a second enumeration. Resolving each field's subschema is a separate lookup that follows
propertiesanditemsonly, while that walk also descendsif,thenandallOf; a member declared solely inside a conditional branch therefore raises here rather than being passed over, so the day one appears this fails loudly instead of exempting the field without saying so.Evidence that the guard fires. Reverting each of the seven corrected rows one at a time reds it, and each time the report names that field alone. That includes
build_provenance.digest, which shares the member namedigestwithreferences[].digestandreproducibility.input_closure[].digest, so attribution goes through the file's ownSECTION_OF_PATHmap rather than a bare name lookup.Stated limitations. Editorial only: no schema, wire format, normative text or code behaviour changes, and no record the schema accepts stops being accepted. The corrected rows describe what the schema already admitted, so this grants a producer no new latitude. The guard covers the schema reference page alone;
docs/integration/agt.mdanddocs/integration/sandbox-runtime.mdalso mention SHA-256 against these fields, but as statements about what one integration emits rather than about what the field admits, so they are correct as they stand and are left alone.runtime.measurementalready named both and is untouched. Section 3.2.1'sSHA-256 minimum; SHA-384 required for FIPS-aligned profilesis not relied on here: no FIPS-aligned profile is defined in this repository, and that bullet sits among the envelope signing rules, so it is context for why both algorithms exist rather than grounds for this change. One limit of the check itself, since it is not visible from the diff:sha256,sha384andsha512are the three values it offers a pattern, so a fourth algorithm added to a pattern later would be admitted by the schema and not seen here. The check reads the pattern rather than trusting a list, which is whysha512:can be offered and refused by all thirteen, but what it can offer is bounded by that constant. It sits at the top of the file under its own name for that reason.Merging main. #437 landed first and its
CHANGELOG.mdbullet conflicted with this one at the same anchor, as expected; the merge keeps both, main's entries first. Main also brought #477, which addedappraisal.platform_measurement.measurementafter this branch was cut, with the same pattern as the other digest fields and a row naming no algorithm. The guard reddened on it after the merge and named that field alone, which is the case it was written for, and the row is corrected in its own commit. The counts above are those of the merged tree; the first commit's message says twelve and six because that was true of the tree it was cut from.Verification was run on Windows 10 with Python 3.13.5 (CI's 3.11 and 3.12 were not run), against
63f4d15: 5 failed, 2390 passed, 6 skipped at the untouched commit, and 5 failed, 2394 passed, 6 skipped after, the same five red in both. They are the known Windows generator failures, which reproduce on the untouched tree and come fromwrite_textgiving CRLF where the fixture comparison is byte-for-byte:ruff check src tests scriptspasses at 0.16.9, which is the pin inrequirements/dev.txt;mypy src/agentrust_traceis clean under strict;tools/check_dashes.pyis clean. After the merge (headb1251d6, main at4c7d6ec), the steps ofci.ymlwere run on Linux: Ubuntu 24.04 arm64 in a container, dependencies from the hash-pinnedrequirements/dev.txtas CI installs them, Node 24.21.0 for the independent verifier. Python 3.11.17 and 3.12.15 each: 3843 passed, 48 skipped, none failed. Every skip is a test that needs a cross-repository dependency or a saved fixture not in this repository.ruff,mypyandtools/check_dashes.pyare clean on both.Type of change
The editorial box covers the seven rows. The new guard in
tests/has no box: it adds a test, not a requirement.Spec section
No section changes. Section 3.1.3 is cited as the place the specification treats the schema pattern as the authority on a field's permitted digest algorithms. The
Authority and conformance claimssection, which is unnumbered and precedes section 1, is why this is offered as a defect in a supporting artifact rather than as a proposal:docs/*.mdexplains the specification and does not carry requirements of its own, so a page that under-describes a constraint is corrected rather than argued.Checklist
git commit -s)CHANGELOG.mdupdated (for any normative change): updated, though there is no normative change; the entry records the corrected rows and the new guard<!-- CHANGED: #NNN: description -->in spec text: no spec text changesTool-assisted: the build, the witness rounds and this description. I answer for all of it.