Agents read freely. Writes wait for you.
Devre puts a switch in the circuit between an agent and its tools. Checking stock and drafting a reorder run on their own. The one call that changes the ledger stops at the gate until a person approves the exact payload.
Local deterministic simulation: inventory, no external system, no real orders.
Open the consoleStudio supply-room console
Six supply-room items. Pick a scenario, then step the agent through its tool calls and decide the write yourself.
Scenarios
- A. Check paper levelsOnly read tools run.
- B. Restock low itemsRead, draft, request approval, then the write stops at the gate.
- C. Clear old stockThe agent asks for delete_item; policy denies it.
The console needs scripts to run. Shown below: scenario B held at the gate.
Example: scenario B, step 4 of 4, commit_reorder held at the gate.
Approval gate
Gate open, write held
commit_reorder is held at the gate. Approving writes 4 lines to on order and moves the ledger to v2.
Lines starting with - show the current value. Lines starting with + show the value after approval.
{ "draft_id": "DR-0001", "ledger_version": 1, "on_order": {- "SR-101": 0,+ "SR-101": 6,- "SR-103": 0,+ "SR-103": 13,- "SR-105": 0,+ "SR-105": 3,- "SR-106": 0+ "SR-106": 6 }}
Supply-room ledger
Ledger v1
| SKU | Item | On hand | Reorder point | On order | Status |
|---|---|---|---|---|---|
SR-101 | A3 matte paper, pack of 250 | 2 | 4 | 0 | Below point |
SR-102 | Fineliner 0.3, box of 12 | 9 | 6 | 0 | OK |
SR-103 | Foam board 5 mm, sheet | 3 | 8 | 0 | Below point |
SR-104 | Archival tape, roll | 5 | 4 | 0 | OK |
SR-105 | Cutting mat A2 | 1 | 2 | 0 | Below point |
SR-106 | Test-print film, pack of 20 | 0 | 3 | 0 | Below point |
Trace
local Example runRun 1: Restock low items
-
1
read_stockreadallowedRequest
{ "below_reorder_point": true }Response
{ "ledger_version": 1, "items": [ { "sku": "SR-101", "on_hand": 2, "reorder_point": 4, "on_order": 0 }, { "sku": "SR-103", "on_hand": 3, "reorder_point": 8, "on_order": 0 }, { "sku": "SR-105", "on_hand": 1, "reorder_point": 2, "on_order": 0 }, { "sku": "SR-106", "on_hand": 0, "reorder_point": 3, "on_order": 0 } ] } -
2
draft_reorderdraftallowedRequest
{ "rule": "top up to 2 x reorder point", "lines": [ { "sku": "SR-101", "quantity": 6 }, { "sku": "SR-103", "quantity": 13 }, { "sku": "SR-105", "quantity": 3 }, { "sku": "SR-106", "quantity": 6 } ] }Response
{ "draft_id": "DR-0001", "lines": 4, "ledger_changed": false } -
3
request_approvaldraftallowedRequest
{ "tool": "commit_reorder", "draft_id": "DR-0001" }Response
{ "approval_id": "AP-0001", "status": "pending" } -
4
commit_reorderwriteheld at gateRequest
{ "draft_id": "DR-0001", "ledger_version": 1, "lines": [ { "sku": "SR-101", "add_on_order": 6 }, { "sku": "SR-103", "add_on_order": 13 }, { "sku": "SR-105", "add_on_order": 3 }, { "sku": "SR-106", "add_on_order": 6 } ] }Response
none: not executed until a person decides
Tool schemas
The four tools the agent can see, written as MCP-style definitions. The side-effect class decides which policy applies.
read_stockread
{
"name": "read_stock",
"description": "Return supply-room rows. No side effects.",
"inputSchema": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["paper", "drawing", "modelling", "conservation"]
},
"below_reorder_point": { "type": "boolean" }
},
"additionalProperties": false
},
"sideEffect": "read"
}
draft_reorderdraft
{
"name": "draft_reorder",
"description": "Build a reorder draft in memory. The ledger is not touched.",
"inputSchema": {
"type": "object",
"properties": {
"rule": { "type": "string" },
"lines": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sku": { "type": "string" },
"quantity": { "type": "integer", "minimum": 1 }
},
"required": ["sku", "quantity"]
}
}
},
"required": ["lines"]
},
"sideEffect": "draft"
}
request_approvaldraft
{
"name": "request_approval",
"description": "Open an approval for one exact write payload.",
"inputSchema": {
"type": "object",
"properties": {
"tool": { "type": "string", "enum": ["commit_reorder"] },
"draft_id": { "type": "string" }
},
"required": ["tool", "draft_id"]
},
"sideEffect": "draft"
}
commit_reorderwrite
{
"name": "commit_reorder",
"description": "Add quantities to the on order column of the ledger. Runs only after a person approves the matching payload.",
"inputSchema": {
"type": "object",
"properties": {
"draft_id": { "type": "string" },
"ledger_version": { "type": "integer" },
"lines": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sku": { "type": "string" },
"add_on_order": { "type": "integer", "minimum": 1 }
},
"required": ["sku", "add_on_order"]
}
}
},
"required": ["draft_id", "ledger_version", "lines"]
},
"sideEffect": "write"
}
Policy
Each tool call is checked against this table before it runs.
| Tool | Class | Allow | Require approval | Deny |
|---|---|---|---|---|
read_stock | read | Yes | No | No |
draft_reorder | draft | Yes | No | No |
request_approval | draft | Yes | No | No |
commit_reorder | write | Held | Yes, at gate | No |
delete_item | not declared | No | No | Yes |
delete_item has no schema. Any call to a tool outside the allowlist is denied before it reaches the gate, so there is nothing for a person to approve.
How the gate is built
-
Allowlist by side effect
Every tool is declared as read, draft or write. Reads and drafts pass. Anything undeclared is denied.
-
Approval covers one payload
The person sees the exact lines and ledger version that will be written. The next write needs its own approval.
-
Append-only trace
Calls, decisions and responses are added in order and never edited. Reset clears the trace and the ledger together.
Minimolo designs AI agents and MCP tool setups with permission rules your team can read.
Plan an agent with Minimolo