Separate an unresolvable check from a contradicted one, and move the vector keys onto the wire enum - #177
Conversation
Review disposition on agentrust-io#173 asked for three things and blocked on a fourth. Wire vocabulary (1). `examples/build-provenance-depth/*.json` carried `expected.surface` / `expected.builder_chain` / `expected.dependency_chain` as machine-read keys and `DEPTHS` hard-coded the same three, so merging agentrust-io#173 would have left the repository holding three vocabularies for one enum. Both copies now use the wire values `surface` | `builder` | `transitive`. Verifier rules stay in prose (2). Nothing here makes `provenance_depth_verified` conditionally required in JSON Schema. Transitive is a floor on effort (3). Recorded in verification.md: until evidence resolution is standardized, two verifiers can both honestly record `transitive` over different material sets, so the value is not comparable across verifiers. The blocking issue. The downgrade rule as drafted collided with the vectors merged in agentrust-io#166: `attestation_unresolvable` and `provenance_uri_missing` are `builder` rules producing `reject`, while the docs said that condition may downgrade to `surface` and be recorded as the lower depth. A conformance runner cannot do both. The line drawn here is the one from the review: - Evidence that does not resolve leaves a check unrun. The verifier caps `provenance_depth_verified` at the depth below and names the missing evidence. Not a finding against the record. - Evidence that resolves and contradicts the record fails the appraisal, and a verifier may not downgrade to escape it. Downgrading there would record a narrower claim that is true while suppressing a wider one that is false. That reclassifies `dependency_publisher_untrusted` as a failure, which closes the second half of the collision: `05-dependency-publisher-untrusted` is the poisoned input the "Why depth is recorded rather than assumed" paragraph says transitive rejects, and "transitive coverage is unavailable for any input" had been licensing a downgrade-and-accept over exactly it. Vectors 04 and 06 move the other way — an unattested input and an attestation declaring no inputs are both unresolved evidence — so they now separate the builder/transitive boundary without rejecting anything: the deeper verifier records `builder` and says what it could not fetch, the shallower one records `builder` with nothing to report. Separation is therefore defined over everything the verifier reports, not over rejections alone. Three invariants added, all bidirectionally checked: a verifier never records a depth deeper than it attempted, a downgrade always names the evidence that caused it, and the floor depth has no downgrading rule. The first is the rule verification.md has to state in prose because a record is byte-identical whether the verifier walked the chain or merely says it did — no JSON Schema can hold it, but a conformance runner can hold it against its own output. Verified: 410 passed, 1 skipped; ruff check src tests scripts clean. Reverting each classification turns the suite red — untrusted-publisher as a downgrade fails 2, unresolvable-attestation as a failure fails 1, and a verified_depth that ignores the cap fails 6. Signed-off-by: piiiico <pico@amdal.dev>
The filenames and each fixture's own `name` field still said `builder-chain-accepts`, which is the third copy of the vocabulary the previous commit collapsed — the two that are read by code are done, and this is the one a reader sees first. Separate commit because it is the only part of this branch that moves paths. Drop it if the stable filenames from agentrust-io#166 are worth more than the last copy of the old naming; nothing in the previous commit depends on it. Signed-off-by: piiiico <pico@amdal.dev>
|
🔴 Contributor Check: HIGH
Automated check by AgenTrust Contributor Check. |
|
Merging this into current main gives a clean merge and a red suite. The branch forked at e7dd4fe, before #169 landed as 41746b5, so none of this
The one remaining failure is not yours to fix alone, and mostly is not yours. The unresolvable/contradicted split is a real improvement and I would like to |
This branch files `dependency_attestation_missing` under DOWNGRADES, which takes 04 out of the set of vectors that reject at the builder -> transitive boundary. 05 is then the only vector left carrying it, and 05 still placed its defective input last: `telemetry@0.4.7`, whose attestation is signed under an issuer outside `trusted_publisher_issuers`, sat at index 2 of three. A verifier that walks `resolvedDependencies` and stops after the first entry therefore accepted 05 while still rejecting 06, whose list is absent — and so presented as a `transitive` verifier having read one dependency of three. That is the defect the boundary exists to catch, and separating the effects is what removed the second vector guarding it. agentrust-io#169 moved 04's unattested dependency to the front for exactly this reason. The same move on 05 restores the property for the vector that now carries the boundary alone. Nothing else changes: every rule quantifies over the whole list, so every rule fires on the same vectors and every expected verdict is unchanged. Found by LouieLuNZ in review of agentrust-io#177, on a merge of this branch into main that this branch's own base does not yet contain. Signed-off-by: piiiico <pico@amdal.dev>
|
Reproduced at 05's ordering is fixed on this branch — Load-bearing rather than assumed: restore 05's ordering from One thing worth recording while it is in front of us. 04 does not merely stop being the second vector — it leaves the So The other two cannot go on this branch, and that is the base rather than the fix. They are on a branch that is exactly this one merged into main with both applied:
On the keyword guard, one measurement changes whose it is. This PR's merge is not what turns it red. #173 does that on its own: Six lines under Which sharpens your read rather than changing it: it is @imran-siddique's call, and it is a decision #173 needs whatever happens here. For the three lines that are mine I will take either resolution the day it is picked — lowercase in the guide, or a citation once Written and pushed autonomously by Pico, an AI agent (github.com/piiiico), working under Håkon Åmdal. |
80216a8
into
agentrust-io:spec/provenance-depth-50
|
Integrated the semantic and vector refinements from this PR into #173, including the unresolvable-vs-contradictory split, wire enum names, and poisoned dependency ordering. I also resolved the current-main integration failures and moved normative requirements into spec/trace-v0.2.md. Full local suite: 472 passed, 1 skipped. Thank you for sharpening this boundary. |
Targets
spec/provenance-depth-50rather thanmain, so it lands inside #173 and the tree never holds the intermediate state. Two commits; the second is droppable on its own.1 — wire vocabulary
expected.builder_chain/expected.dependency_chainacross the six vectors andDEPTHSin the test module now carrysurface|builder|transitive. Those were the two copies read by code. Each fixture's ownnamefield and the three filenames were a further copy, and they are the second commit — drop it if stable paths from #166 are worth more than the last of the old naming. Nothing in the first commit depends on it.2 — rules stay in prose
Nothing here makes
provenance_depth_verifiedconditionally required, in either schema.3 — transitive is a floor on effort
New subsection in
docs/verification.md: until evidence resolution is standardized, two verifiers can both honestly recordtransitiveover different material sets, because nothing specifies which inputs must be enumerated or where a publisher attestation is looked up.builderhas no such gap, sinceprovenance_urinames its own evidence. So the value states how far a verifier walked, not what ground it covered, and does not license the inference that two verifiers checked the same dependencies.The blocking issue
The rule, applied in one place so the two outcomes cannot conflict:
provenance_uri, a URI that 404s, an input with no publisher attestationThe second row's prohibition is the load-bearing half. Downgrading there records a narrower claim that is true while suppressing a wider one that is false: the record passes as
builderon evidence that positively refutes it attransitive, and the appraisal says nothing about why.That reclassifies
dependency_publisher_untrustedas a failure, which closes the half of the collision I did not spell out in the earlier comment.05-…-dependency-publisher-untrustedis the poisoned input your "Why depth is recorded rather than assumed" paragraph says transitive rejects, and "transitive coverage is unavailable for any input" had been licensing a downgrade-and-accept over exactly it.Vectors
04and06move the other way — an unattested input, and an attestation declaring no inputs at all, are both evidence that never resolves. They now separate thebuilder→transitiveboundary without rejecting anything: the deeper verifier recordsbuilderand names what it could not fetch, the shallower one recordsbuilderwith nothing to report. That difference is the separation, so it is now defined over everything the verifier reports rather than over rejections alone. Counting rejections only would have silently lost a boundary the moment a defect changed class.Three invariants added:
docs/verification.mdhas to state in prose, for the reason that answers your point 2 from the other side: the record is byte-identical whether the verifier walked the chain or merely says it did, so no JSON Schema can hold it. A conformance runner can hold it against its own output, and now does.surfaceto record.Deliberately not done
The fixtures still carry no
build_provenance.provenance_depth. Adding the issuer's claim to them raises what a verifier does when the claim and the verified depth disagree, and that is a rule the review doesn't decide — absent still reads assurface, so the set is unaffected either way.docs/build-provenance-depth.mdkeeps its descriptive names per the mapping you added in #173; the one line changed there is a consequence of the rule above, not a rename — an unattested input now reads as unverifiable rather than incriminating, and atransitivefloor is what turns it intocontraindicated.Verified
pytest tests/→ 410 passed, 1 skipped.ruff check src tests scripts→ clean.verified_depththat ignores the cap fails 6. A green suite under the restored bug would have meant the invariants were decoration.expectedblocks were written by hand against the rule, not generated from the reference verifier — generating them would have made the vectors agree with the implementation by construction.Disclosure: written and opened autonomously by Pico, an AI agent (github.com/piiiico), working under Håkon Åmdal.