schema: put the references block in the schema and the model - #199
Open
lywinged wants to merge 1 commit into
Open
schema: put the references block in the schema and the model#199lywinged wants to merge 1 commit into
lywinged wants to merge 1 commit into
Conversation
Spec section 3.1.2 landed in agentrust-io#198 as text. Text alone did not make the field usable: schema/trace-claim.json sets additionalProperties: false at the top level and TrustRecord is extra="forbid", so a record carrying references was rejected by both artifacts a producer validates against. examples/tpm2.json validates as committed and fails with "Additional properties are not allowed ('references' was unexpected)" the moment an entry is added. The block is an array. That is the reading the text supports rather than a decision made here: 3.1.2 says "a references entry", "an entry in references", and "omit the entry", and its field table describes the fields of an entry rather than of the block. Two of the four rules in 3.1.2 are properties of a record and are tested: rule 1, a record carrying references and no origin keeps the hardware runtime.platform it earned; rule 2, the signature covers the block, so rewriting any member of any entry in transit fails verification. Rules 3 and 4 bind verifiers rather than records — no schema can express them — and belong to the conformance suite. The schema description says so rather than implying it enforces them. resolver is constrained on presence and not on value. Rule 4 requires a producer that cannot name a resolver to omit the entry, and whether an identifier is self-asserted is not decidable from the record. retention is validated as an ISO 8601 duration by a pattern written out in alternation rather than with a negative lookahead, so the same string serves both files: pydantic's default regex engine has no look-around, and a lookahead form would build in JSON Schema and raise in the model, forcing the two apart. A test asserts the two pattern strings are byte-equal, and that the rel sets match, because a case table only catches a divergence it happens to sample. Both schema copies change together; tests/test_validate.py already fails on drift between them. docs/schema.md gets the field and a table, guarded the same way as the build_provenance table. 604 tests pass. Each guard was checked by reverting what it guards. Signed-off-by: Louielunz <48041247+lywinged@users.noreply.github.com>
Contributor
|
❔ Contributor Check: UNKNOWN
Automated check by AgenTrust Contributor Check. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Spec section 3.1.2 landed in #198 as text. The schema was not part of it, and text alone did not make the field usable:
schema/trace-claim.jsonsetsadditionalProperties: falseat the top level, soreferencesis rejected outright.TrustRecordisextra="forbid", so the reference model rejects it too.examples/tpm2.jsonvalidates as committed. Add onereferencesentry and it fails againstmainwithAdditional properties are not allowed ('references' was unexpected). Both artifacts a producer would validate against reject a record the specification permits, and nothing failed, because nothing connects the field table in §3.1 to the schema.This adds the block to both, plus the reference model,
docs/schema.md, and a test file. Additions only — 163 inserted, 0 deleted across the six existing files.The array shape
referencesis an array of entries. That is the reading the text supports rather than a decision made here: §3.1.2 says "areferencesentry", "an entry inreferences", and "MUST omit the entry", and its field table describes the fields of an entry rather than of the block. If you meant a single object, this is the PR to say so on and it is a small change.What is tested, and what cannot be
Of the four rules in §3.1.2, two are properties of a record:
referencesMUST NOT affectruntime.platform. A hardware record that addsreferencesand nooriginkeeps the platform it earned. The converse is tested too: addingreferencesmust not relax theoriginif/then, or "does not lower assurance" could be satisfied by a schema that had stopped enforcing anything on that record.references. Nine tamper cases (each member, an entry appended, an entry removed, order reversed, the block removed) each fail verification.Rules 3 and 4 bind verifiers rather than records, and no schema can express either. The schema description says that rather than implying it enforces them — a description that recites verifier obligations reads like enforcement to whoever is looking at the file to find out what is checked. They belong in the conformance suite, and I will open that separately against
agentrust-trace-testsunless you would rather it went elsewhere.The two choices I would rather you looked at than accepted
minItems: 1. The text does not say the array must be non-empty. I read it out of rule 4 — omit the entry rather than emit an empty one — and applied the same logic to the block. An empty array is a record that says it points at something and does not. Easy to drop if you disagree.relis a closed enum. §3.1.2 says "Registeredrelvalues", which implies a registry that can grow, and closing it means a new value needs a schema change.origin.kindis closed for the same stated reason — a verifier keying on the value — so I matched it. Also worth naming: this is the shape of decision the registry question in §7 would settle properly.resolveris constrained on presence and not on value. Rule 4 requires a producer that cannot name a resolver to omit the entry, and whether an identifier is self-asserted is not decidable from the record, so a pattern here would be theatre.retentionValidated as an ISO 8601 duration by a pattern written out in alternation rather than with a negative lookahead. That is not style: pydantic's default regex engine is Rust's, which has no look-around, so a lookahead form builds fine in JSON Schema and raises at import in the model — the two files would have to disagree. Written this way, one string serves both, and a test asserts they are byte-equal.
It accepts
P30D,P1Y6M15D,P1Y15D,P2W,PT1H30M15S,P1DT12H. It rejectsPandPT(no components),P1DT(a time designator with no time),P1H(hours outside the time part),P1D1Y(out of order),P1Y2WandP2WT12H(the week form does not combine). It also rejects fractional components such asPT0.5H, which ISO 8601 permits on the smallest component; say the word if a retention period needs them.I deliberately did not add
"format": "duration".iter_errorsruns with aFormatCheckerattached, andformat: durationasserts only when an optional dependency is installed — so the same record would validate differently on two machines, and the field would look checked where it was not.Drift
Both schema copies change together.
test_validate.pyalready fails whensrc/agentrust_trace/schema/trace-v0.2.jsondrifts from the normative file, and the CHANGELOG records the last time that drift shipped and cost a validdid:subject. Two new guards in the same spirit: theretentionanddigestpattern strings must be byte-equal between the model and the schema, and the registeredrelset must be the same in both. A case table only catches a divergence it happens to sample; byte equality does not depend on sampling.docs/schema.mdgets the field, a table and a section, guarded the way thebuild_provenancetable already is.Verification
604 tests pass,
ruff checkclean onsrcandtests. Every guard was checked by reverting the thing it guards and confirming it fails:maindocs/schema.mdrelvalue spelled differently in the modeldocs/schema.mdrow edited three separate waysreferencesand not to something else going wrongThat last row is the one I care about most. A table of "this must be rejected" cases passes trivially if the record was already invalid, so each rejection asserts the error path actually starts at
references.Contribution basis
No sponsoring organization: under CONTRIBUTING this is a schema change tracking an already-merged spec change, plus tests and docs. It adds no normative rule of its own. The uppercase keywords in the schema description are quotations of §3.1.2 and name it in the same sentence;
docs/schema.mdstates the same two rules in lowercase and attributes them, which is whattest_requirement_keywords_are_attributable.pyasks of a file underdocs/(that guard does not readschema/, so the schema description follows the same rule by intent rather than by enforcement).