What's wrong
A capability's contract.json needs a top-level artifact: {digest, url} field for the capability to be usable at all -- traverse-framework/registry's own scripts/ci/build_index.py reads contract.get("artifact") or {} and copies digest/url straight into the published index record (verified directly: capabilities/<ns>/<id>/<ver>/contract.json -> index.json's digest/artifact_url fields). If a contract has no artifact field, the index entry silently gets digest: null, artifact_url: null -- a capability that appears in registry sync's output but has no way for any consumer to actually fetch its WASM binary.
traverse-cli capability publish doesn't require this field at all: parse_contract/validate_contract accept a contract with no artifact key (verified -- a real contract missing it passed --dry-run and a real publish with validation_status: "passed", traverse-framework/registry#89's first attempt). Registry's own scripts/ci/capability_validation.py also treats it as fully optional (artifact = contract.get("artifact"); if artifact is not None: ... -- the whole check is skipped when the field is absent). Nothing in the real, automated path stops a syntactically-valid, CI-green, human-mergeable PR from publishing a capability that's structurally unusable.
This isn't hypothetical -- it was hands-on reproduced this session authoring validation.validate-email: the contract validated and dry-ran cleanly with no artifact field, and the gap was only caught by separately reasoning through build_index.py's source (not by anything in the actual publish pipeline surfacing it).
Definition of Done
Where this was found
traverse-framework/registry#89, first publish attempt of validation.validate-email -- the initial dry-run/publish succeeded with "validation_status": "passed" despite the contract having no artifact field. Fixed for that specific PR by manually adding the field before the real (non-dry-run) publish, but nothing in the tooling would have caught it if the author hadn't independently checked build_index.py's source.
What's wrong
A capability's
contract.jsonneeds a top-levelartifact: {digest, url}field for the capability to be usable at all --traverse-framework/registry's ownscripts/ci/build_index.pyreadscontract.get("artifact") or {}and copiesdigest/urlstraight into the published index record (verified directly:capabilities/<ns>/<id>/<ver>/contract.json->index.json'sdigest/artifact_urlfields). If a contract has noartifactfield, the index entry silently getsdigest: null, artifact_url: null-- a capability that appears inregistry sync's output but has no way for any consumer to actually fetch its WASM binary.traverse-cli capability publishdoesn't require this field at all:parse_contract/validate_contractaccept a contract with noartifactkey (verified -- a real contract missing it passed--dry-runand a real publish withvalidation_status: "passed",traverse-framework/registry#89's first attempt). Registry's ownscripts/ci/capability_validation.pyalso treats it as fully optional (artifact = contract.get("artifact"); if artifact is not None: ...-- the whole check is skipped when the field is absent). Nothing in the real, automated path stops a syntactically-valid, CI-green, human-mergeable PR from publishing a capability that's structurally unusable.This isn't hypothetical -- it was hands-on reproduced this session authoring
validation.validate-email: the contract validated and dry-ran cleanly with noartifactfield, and the gap was only caught by separately reasoning throughbuild_index.py's source (not by anything in the actual publish pipeline surfacing it).Definition of Done
capability publishrequires anartifact.digest/artifact.urlin the contract before allowing a non-dry-run publish (or, if there's a reason artifact-less contracts are meant to be valid for some other reason, that needs to be an explicit, documented decision -- not silence).capability_validation.py(registry-side) should fail loudly, not skip silently, when a contract lacks it -- matching the precedent already set for corrupt/unreadable contracts inbuild_index.py(registry decision-log entry 27: "a silently-dropped record is the same failure class... as an unverifiable record reaching a consumer").Where this was found
traverse-framework/registry#89, first publish attempt ofvalidation.validate-email-- the initial dry-run/publish succeeded with"validation_status": "passed"despite the contract having noartifactfield. Fixed for that specific PR by manually adding the field before the real (non-dry-run) publish, but nothing in the tooling would have caught it if the author hadn't independently checkedbuild_index.py's source.