01Safety
0.0 / 30
What changed in the harness
Selection accuracy 83→88, token cost up 4%, unconfirmed writes 0%→0%.
Category breakdown
Earned points across the four signals Gradable measures. Safety and Legibility are scored out of 30; Economics and Discoverability are scored out of 20.
0.0 / 30
22.4 / 30
19.0 / 20
12.1 / 20
Highest-impact fix
Estimated gain +30 pointsExpose machine-readable principal/tenant confirmation and a non-mutating permission check so agents can verify both before destructive actions.
Description evidence
13 defects found across the exposed tool descriptions. Suggested rewrites make purpose, inputs, boundaries, and returns easier for an agent to understand.
| Tool | Defect types | Suggested rewrite |
|---|---|---|
foodblock_create |
no_return_description |
Create a new FoodBlock and return the created block plus its SHA-256 hash, which other blocks can reference via refs. A FoodBlock is the universal data primitive for the food system. It has three fields: type (what it is), state (its properties), refs (what it references). Base types: actor (person/org), place (location), substance (ingredient/product), transform (cooking/processing), transfer (sale/delivery), observe (review/certification). Use dot notation for subtypes: actor.producer, substance.product, observe.review, etc. |
foodblock_update |
no_return_description |
Create a new version of an existing FoodBlock and return the new version's block and its hash. FoodBlocks are append-only — this creates a new block that references the previous one via refs.updates. Note: state is a FULL REPLACEMENT, not a merge. |
foodblock_get |
no_return_description |
Fetch a specific FoodBlock by its SHA-256 hash. Returns the block's contents (type, state, and refs). |
foodblock_chain |
no_return_description |
Follow the update chain of a FoodBlock backwards through its versions. Shows the full version history: current → previous → original. Returns the sequence of version blocks along the chain, up to max_depth. |
foodblock_tree |
no_return_description |
Build the full provenance tree for a FoodBlock by following ALL refs recursively. Shows the complete story: bread ← baking ← flour ← wheat ← farm. Returns the tree of blocks reachable via refs, up to max_depth. |
foodblock_heads |
no_return_description |
List all head blocks (latest version of each entity/item), optionally filtered by type. Returns the matching head blocks. |
foodblock_info |
no_return_description |
Get an overview of the FoodBlock system: server info, block count, and protocol summary. Call this first to understand what data is available. Returns the server overview including server info, block count, and protocol summary. |
foodblock_load_agent |
no_return_description |
Load a previously created agent using saved credentials. Required after MCP server restart to restore signing ability. Returns and loads the agent so it can sign blocks. |
foodblock_agent_draft |
no_return_description |
Create a draft FoodBlock on behalf of an agent. Draft blocks have state.draft=true. The human operator can approve or reject with foodblock_approve_draft. Returns the created draft block and its hash. |
foodblock_approve_draft |
no_return_description |
Approve a draft block created by an agent. Creates a confirmed version with draft removed. Returns the confirmed block and its hash. |
foodblock_list_agents |
no_return_description |
List all AI agents in the FoodBlock system. Returns the registered agents along with their block hashes. |
foodblock_tombstone |
no_return_description |
Mark a FoodBlock for content erasure (GDPR compliance). Creates an observe.tombstone block that references the target. The target block's state is replaced with {tombstoned: true}. The hash, type, and refs are preserved for chain integrity. Returns the created tombstone block and its hash. |
foodblock_fb |
no_return_description |
The single natural language entry point to FoodBlock. Describe food in plain English and get structured FoodBlocks back. No need to know types, fields, or hashes. Examples: 'Sourdough bread, $4.50, organic, contains gluten', 'Amazing pizza at Luigi's, 5 stars', 'Green Acres Farm, 200 acres, organic wheat in Oregon', 'Walk-in cooler temperature 4 celsius', 'Ordered 50kg flour from Stone Mill'. Returns the extracted structured FoodBlocks and their hashes created from the input text. |
Selection evidence
6 pairs where similar names or overlapping descriptions may send an agent toward the wrong tool.
| Tool A | Tool B | Confidence | Why they collide |
|---|---|---|---|
foodblock_create |
foodblock_create_agent |
medium | A task like 'create an agent for Green Acres Farm' or 'register a new agent' is ambiguous: foodblock_create registers an actor-type FoodBlock (person/org), while foodblock_create_agent registers an AI agent with a keypair. The word 'agent' alone doesn't disambiguate, and both descriptions begin with 'create'/agent-related fields. |
foodblock_create |
foodblock_update |
medium | Both descriptions open with 'Create a new FoodBlock' and accept type/state/refs. A task like 'change the sourdough price to $5' or 'make an updated version of this block' could route to foodblock_create because the agent may not know it needs previous_hash; update is required for any modification of an existing block, yet that requirement is only discoverable in the update schema. |
foodblock_create |
foodblock_fb |
medium | Both can produce FoodBlocks. foodblock_fb is the natural-language entry point that parses plain-English food descriptions, while foodblock_create requires structured type/state/refs. A task like 'Add sourdough bread, $4.50, organic, contains gluten' could be served by either, and an agent may pick structured create instead of the NL parser, or vice versa when the user provides a type directly. |
foodblock_create_agent |
foodblock_agent_draft |
medium | Both involve creating something on behalf of an agent. A task like 'have my bakery assistant draft a new order' or 'create the agent's first block' is ambiguous between registering the agent itself (create_agent, needs operator_hash) and creating a draft block as the agent (agent_draft, needs agent_hash). The shared 'create'+'agent' framing obscures which entity is new. |
foodblock_batch |
foodblock_fb |
low | A bulk task like 'import these food items' is ambiguous: foodblock_batch creates multiple structured blocks (requires parsing into type/state/refs), while foodblock_fb extracts blocks directly from plain English. An agent could pick batch for free-text input or fb for structured data. Low because fb explicitly advertises itself as the NL entry point, which usually resolves the choice. |
foodblock_agent_draft |
foodblock_approve_draft |
low | Both operate on agent-created draft blocks, and a vague task like 'process/finalize the agent's draft' could route to creating a new draft (agent_draft) instead of approving the existing one (approve_draft) for confirmation. Low because the verbs 'create' vs 'approve' in the descriptions are usually decisive for well-phrased tasks. |
Compare the field