Skip to content

fix(schema): resync the packaged copy, and a vector that uses the field whose absence hid it - #68

Merged
imran-siddique merged 2 commits into
agentrust-io:mainfrom
lywinged:resync/packaged-schema
Aug 20, 2026
Merged

fix(schema): resync the packaged copy, and a vector that uses the field whose absence hid it#68
imran-siddique merged 2 commits into
agentrust-io:mainfrom
lywinged:resync/packaged-schema

Conversation

@lywinged

Copy link
Copy Markdown
Collaborator

What is wrong

schemas/trace-claim.json sits at $id https://agentrust-io.com/schema/trace-v0.1.json and pins eat_profile to the v0.2 sentinel. So it requires a record to declare v0.2, and then rejects two properties v0.2 defines: signature and delegation.

additionalProperties is false — deliberately; test_unknown_fields_rejected asserts it. Measured against trace-spec's corpus:

record packaged schema on main
signed, no delegation rejectedAdditional properties are not allowed ('signature' was unexpected)
signed delegated hop rejected('delegation', 'signature' were unexpected)

Where this does and does not reach

Stated precisely, because it is easy to overstate and I did in a first draft.

  • Not shipped. [tool.hatch.build.targets.wheel] packages = ["src/trace_tests"]. The directory is not in the distribution, so no installed consumer sees it.
  • Not used by the runner. grep jsonschema src/trace_tests/ returns nothing; TR-ENV checks fields by hand. No record anyone has scored was validated against this file.
  • Used by this suite's own schema tests. Its one reader is tests/conftest.py:23, which feeds tests/test_schema.py — the tests carrying the level0 marker, defined in pyproject.toml as "Level 0 conformance — structural and schema validity".
  • Linked from the README as 🗂 Test schemas | schemas/, so a reader browsing the suite for a reference copy gets the stale one.

So the cost is to what this suite can assert, not to any record it has scored. A conformance suite whose schema tests run a version behind cannot accept a signed record in them — which is self-reinforcing, and is why no vector here has one.

Why 176 tests never noticed

tests/vectors/invalid_missing_runtime.json       signature: no   delegation: no
tests/vectors/invalid_wrong_profile.json         signature: no   delegation: no
tests/vectors/valid_cmcp_runtime.json            signature: no   delegation: no
tests/vectors/valid_level0.json                  signature: no   delegation: no
tests/vectors/valid_level0_with_transcript.json  signature: no   delegation: no
tests/vectors/valid_openshell_import.json        signature: no   delegation: no

Not one vector carries a signature. The schema forbade a field the corpus never used.

The fix, and the half that is not the fix

The first commit resyncs the packaged copy from the normative schema. Verified: parsed as JSON it is now equal to trace-spec's schema/trace-claim.json, and both records above validate.

The second commit adds what would have caught it. Two signed vectors — one root, one delegated hop — copied from trace-spec's examples/delegation-link/, which is generated from a published seed and reproducible by anyone.

Nothing compares them to the originals. A guard that needs another repository checked out is a guard that gets skipped. They are held instead to the property that makes them worth having: they carry the fields whose absence hid the defect, asserted directly, so a vector edited into something unsigned fails loudly rather than quietly.

A fourth test checks $id names the version the records it validates declare. The inconsistency was visible in one line the whole time.

Verified by reverting

A regression test that passes before and after the fix is not one. With the schema reverted to main's copy:

3 failed, 1 passed
  FAILED test_a_signed_record_validates
  FAILED test_a_signed_delegated_record_validates
  FAILED test_the_packaged_schema_declares_the_version_it_is

The one that still passes is the vector self-validation, which correctly does not depend on the fix.

The shape, which is the part worth arguing about

This is the second defect in this repository found the same way this month, and both have the same structure: the corpus avoided the case the defect lived in, so the suite stayed green while the thing it scores was wrong.

The other is #66TR-SIG canonicalized with json.dumps(sort_keys=True, ...), and every vector was ASCII, on which that form and RFC 8785 agree byte-for-byte. Here the schema forbade signature, and no vector had one.

Recorded as an observation rather than a dependency: the two branches touch no file in common and neither needs the other. But two instances is a pattern, and the answer to both was the same — a vector that uses the field.

Checks

180 passed, 5 xpassed

The 5 xpassed are pre-existing and unchanged by this branch. ruff is clean on the files touched here; this repository's CI runs no lint step.

Require maintainer approval will fail on this as it does on every PR from outside the allowlist. Nothing to do with the change.


Generated by Claude Code

The packaged schemas/trace-claim.json was a v0.1 body under a v0.2
label: 13 top-level properties, no delegation block, no
provenance_depth. Records signed against the normative schema were
rejected by the packaged one. Since this branch was first written the
gap widened - the normative source gained the delegation link block
and the provenance depth field (trace-spec #173) - so the resync now
carries those too. Packaged copy is byte-identical to
schema/trace-claim.json at trace-spec 1c5a9b9.

The hand-written enforcement enums gain 'declared' to match, which
keeps the agentrust-io#62 parity guard green on the moved schema rather than
green on the stale one. 183 passed, 5 xpassed (the TEE-hardware
smoke set, strict=False by design, unrelated).

Signed-off-by: lywinged <48041247+lywinged@users.noreply.github.com>
The resync in the previous commit fixes the packaged schema. This adds what
would have caught it, because the reason nothing caught it is still here
otherwise.

`schemas/trace-claim.json` sits at `$id` trace-v0.1.json and pins `eat_profile`
to the v0.2 sentinel, so it requires a record to declare v0.2 and then rejects
two properties v0.2 defines: `signature` and `delegation`. additionalProperties
is false, deliberately -- test_unknown_fields_rejected asserts it. Measured
against trace-spec's corpus before writing anything: a signed record fails with
"'signature' was unexpected" and a signed delegated hop with "'delegation',
'signature' were unexpected".

Where that does and does not reach, stated precisely because the first draft of
this message overstated it. The file is not shipped -- the wheel packages
src/trace_tests only -- and no code under src/ reads it; grep for jsonschema
there returns nothing, and TR-ENV checks fields by hand. Its one reader is
tests/conftest.py, which feeds tests/test_schema.py, the suite's own Level 0
"structural and schema validity" tests. README.md also links the directory as
"Test schemas", so a reader browsing the suite for a reference copy gets the
stale one.

So the cost is to what this suite can assert, not to any record anyone has
scored. A conformance suite whose schema tests run a version behind cannot accept
a signed record in them -- which is self-reinforcing, and is why no vector here
has a signature.

Nothing failed because not one of the six vectors carried a `signature` at all.
The schema forbade a field the corpus never used. That is the same shape as the
canonicalization defect in tr_sig: the corpus avoided the case the defect lived
in. Two in one repository is a pattern, and the answer to both is a vector that
uses the field.

The two vectors are copied from trace-spec's examples/delegation-link/, generated
from a published seed and reproducible by anyone. Nothing compares them to the
originals -- a guard that needs another repository checked out is a guard that
gets skipped. They are held instead to the property that makes them worth having:
they carry the fields whose absence hid the defect, so a vector edited into
something unsigned fails loudly rather than quietly.

Verified by reverting the schema: the two validation tests and the `$id` check
fail, and the self-validation test passes, which is the shape a regression test
should have. 180 passed.

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

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

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 18, 2026
@lywinged lywinged closed this Aug 18, 2026
@lywinged lywinged reopened this Aug 18, 2026
@imran-siddique
imran-siddique merged commit 84ecb81 into agentrust-io:main Aug 20, 2026
11 of 13 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.

2 participants