Repository navigation
feat(registry/types): allow attestation constraints on skill provenance - #251
Merged
Merged
Conversation
#247 stripped `attestation` from the skill provenance contract because the verifier could silently skip it: an expected attestation was only compared when the artifact also carried one, so the constraint was dropped exactly when it mattered. #248 closed that gap -- `compareAttestation` now fails when the provenance asks for an attestation and the statement is absent. That removed the reason for the restriction but not the restriction, and left two comments on main asserting the opposite of what the code does. Both the schema description and the `Skill.Provenance` doc comment still claimed a declared attestation "would be silently skipped", and the Go comment's "until that gap is fixed" read as permanent. Re-add the key and correct both. The skill `verified_attestation` definition is deliberately stricter than the server one in publisher-provided.schema.json: `predicate` is typed as an object. The verifier normalizes an expected predicate through `structpb.NewStruct`, which requires `map[string]any`: string(str) -> err=expected predicate is string, want an object int(42) -> err=expected predicate is int, want an object []interface{}([a]) -> err=expected predicate is []interface {}, want an object map[string]any{k:v} -> ok A non-object predicate therefore mismatches every artifact. Untyped, the schema would accept a constraint that can never be satisfied and the failure would surface at install time as an unexplained verification failure. Typing it makes that a validation error at authoring time. `additionalProperties: false` is kept on both definitions -- it was added for typo protection rather than for the attestation restriction, and it now also catches a misspelled `predicatetype`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Samuele Verzi <samu@stacklok.com>
Review nit: "an empty one is not checked" contradicted the next sentence,
since an empty Attestation is deliberately checked and requires the
artifact to be attested.
Scope the rule to empty strings and call out Attestation as the exception,
noting that its own PredicateType and Predicate go back to the usual
constrain-only-when-set behaviour.
The schema's provenance description carried the same contradiction ("an
absent or empty value leaves that dimension unconstrained", which is false
for `attestation: {}`), so fix it there too.
Comments only; no behaviour change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Samuele Verzi <samu@stacklok.com>
rdimitrov
approved these changes
Aug 21, 2026
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.
Follow-on to #247 and #248. Small, and mostly correcting documentation that those two left inconsistent.
Why
#247 stripped
attestationfrom the skill provenance contract because the verifier could silently skip it — an expected attestation was only compared when the artifact also carried one, so the constraint was dropped exactly when it mattered:#248 closed that gap.
compareAttestationnow returnsfalsewhen the provenance asks for an attestation andr.Statement == nil.That removed the reason for the restriction but not the restriction — and it left two comments on
mainasserting the opposite of what the code now does:skill.schema.json,$defs/provenance: "the verifier only compares an expected attestation when the artifact also carries one, so a declared attestation constraint would be silently skipped"skills_types.go: "Only the identity fields are honored… Refusing it loudly keeps the contract fail-closed until that gap is fixed"The gap is fixed. This PR re-adds the key and corrects both.
Deliberately stricter than the server schema
The skill
verified_attestationdefinition typespredicateas an object, whichpublisher-provided.schema.jsondoes not. This isn't gratuitous — the verifier normalizes an expected predicate throughstructpb.NewStruct, which requiresmap[string]any. Checked against the merged code:So a non-object predicate mismatches every artifact. Left untyped, the schema would happily accept a constraint that can never be satisfied, and the failure would surface at install time as an unexplained verification failure with nothing pointing at the catalog entry. Typing it turns that into a validation error at authoring time.
Worth considering the same tightening for the server schema separately — I haven't touched it here, since it's a live contract and out of this PR's scope.
additionalProperties: falsestays on both definitions. It was added for typo protection, not for the attestation restriction, and it now also catches a misspelledpredicatetype.Semantics (matching #248 exactly)
attestationabsentattestation: {}predicate_typesetpredicatesetTests
TestSkill_ValidateWithProvenance— attestation cases flipped fromexpectErrorback to success, plusempty attestation constrains only presence,predicate type without predicate, and rejection of scalar/array predicates.TestValidateSkillBytes_Provenance— valid full and empty attestation constraints; rejection of a non-objectattestation, a non-objectpredicate, and a misspelledpredicatetype.Verification
task— green, 0 lint issues.task license-check— passes.registry/typescoverage: 81.1%.TestSkill_Validateuntouched and passing.Note on release sequencing
v0.0.41predates #247 and #248, so neither is released yet. Landing this before the next tag means the skill provenance contract ships complete in one version, rather than shipping a restriction that's immediately relaxed in the next — worth doing before cutting the release that unblocks stacklok/toolhive#6310's go.mod bump.🤖 Generated with Claude Code