Repository navigation
Description
Current behavior
spec/server-provenance-v1.md marks tool_catalog.tool_count as required and defines it as the number of tools described by the catalog. The field is intentionally redundant with the hash so a consumer can distinguish a plausible-count/hash mismatch from a completely different catalog.
At current main (25013c59d607860a4a4d2608476280286f688243):
build_record()emitstool_count = len(tools);verify_record()validatestool_catalog.hashbut never validatestool_count;check_tool_catalog()readstool_countonly after the hash has already mismatched, to include it in the diagnostic.
A correctly signed record can therefore carry the correct catalog hash for two tools and tool_count: 999; verify_record() succeeds and check_tool_catalog() also succeeds because the hash matches.
The same is true for missing, negative, boolean, string, or otherwise malformed tool_count values: the required field is not load-bearing on the consumer path.
Why this is distinct from #142
Issue #142 noted in passing that tool_count was never checked, but its merged fix #146 addressed the four structural defects that allowed tee-attested records without evidence and malformed identity/timestamp state. tool_count remains unchanged on current main.
This issue isolates that remaining field rather than reopening #142.
Smallest fix
There are two complementary checks:
verify_record()should requiretool_countto be a non-negative JSON integer (excluding bool), because the spec marks the member required.check_tool_catalog()should compare the declared count withlen(tools)after the hash matches, so a signed internally inconsistent record cannot pass the live-server check.
For example:
declared_count = catalog.get("tool_count")
if (
not isinstance(declared_count, int)
or isinstance(declared_count, bool)
or declared_count < 0
):
raise ProvenanceError("tool_catalog.tool_count must be a non-negative integer")If the live hash mismatches, the existing ToolCatalogMismatch remains the right result: the paper may be fine and the server differs. If the live hash matches but len(tools) != declared_count, the record contradicts its own signed catalog metadata, so that should remain an ordinary ProvenanceError, not ToolCatalogMismatch.
Regression evidence to add
- correct hash + correct count -> unchanged success;
- correct hash + wrong positive count ->
verify_record()can establish only structural validity, butcheck_tool_catalog()refuses the internally inconsistent record; - missing count ->
ProvenanceError; - negative count ->
ProvenanceError; - boolean count ->
ProvenanceError; - string count ->
ProvenanceError; - ordinary catalog hash mismatch -> existing
ToolCatalogMismatchbehavior preserved.
No wire-format or normative specification change is needed; this makes the reference consumer enforce an already required signed field while preserving the existing bad-document versus wrong-server error distinction.
AI-assistance disclosure: ChatGPT assisted with source triage, adversarial-case design, and drafting. altrudev reviewed the bounded claim and remains responsible for the contribution.
Activity
github-actions commentedon Aug 30, 2026
🟡 Contributor Check: MEDIUM
Automated check by AgenTrust Contributor Check.
fix(provenance): enforce tool_catalog.tool_count (agentrust-io#256)