
Resolve, read, invoke, receipt: the four calls a stranger's agent makes
A stranger's agent lands on this domain with no schemas loaded, no SDK, no config file, and one ability: it can make HTTP requests. Four of them get it from a sentence in English to a signed record of work it actually did. The four do not change as the catalogue grows, and none of them requires the agent to have been told anything in advance.
| # | Step | Call | Credential | What comes back |
|---|---|---|---|---|
| 1 | Resolve | GET /api/dispatch?ask=<plain english> | none | ranked candidate keys, one recommendation, a ready-to-fire URL |
| 2 | Read the contract | GET /api/dispatch?key=<KEY>&format=markdown | none | the whole manual for one capability, ~4.4 KB |
| 3 | Invoke | POST /api/dispatch {"key":…,"body":…} | owner key or scoped token | the result, plus a receipt id |
| 4 | Take the receipt | GET /api/dispatch?confirm=<inv_ID> (public) or ?receipt=<inv_ID> (credentialed) | none / scoped | proof it happened, and the exact bytes |
Two more verbs hang off step 4 and are the reason the receipt is an object rather than a log line: replay re-fires a recorded call with its recorded input, and repair supersedes a bad call with a corrected one. Both write new receipts that point back at the old.
Evidence status
Observed marks first-party measurements or runtime receipts from the named environment. Derived marks arithmetic calculated from cited inputs. Specified marks vendor or standards documentation. Implemented and deployed name code and live-state evidence, respectively. Reproduced means the stated procedure was rerun. Externally attested marks operator reports; those reports show that an experience occurred, not that it is universal.
Step 1 asks for words and answers with keys
curl -s "https://miscsubjects.com/api/dispatch?ask=what%20time%20is%20it"Real response, trimmed to the parts that matter:
json · 18 linestap to unfold
{
"protocol": "OIP", "version": "1.2.0", "kind": "ask",
"question": "what time is it",
"count": 12,
"best": {
"key": "NOW",
"run_now": "https://miscsubjects.com/api/dispatch?invoke=NOW&share=<TOKEN>",
"do": "Open run_now to do it. Substitute your own text/args where the example has them."
},
"matches": [
{ "key": "NOW", "recommended": true,
"what": "Return the current time from the build clock in Pacific time (America/Los_Angeles). …",
"example": "[NOW][/NOW]",
"invoke": { "post": "https://miscsubjects.com/api/dispatch",
"body": { "key": "NOW", "body": "" } },
"self": "https://miscsubjects.com/api/dispatch?key=NOW" }
]
}The full ranked list from that exact call, in order: NOW, GITHUB_LIST_ISSUES, GITHUB_GET_ISSUE, GITHUB_ADD_ISSUE_COMMENT, GITHUB_CREATE_ISSUE, GITHUB_CLOSE_ISSUE, LOCAL_EDIT, LOCAL_WRITE, CLI_GIT, WRITER_AGENT, BLOOIO_LIST_CONTACT_IDENTITIES, STRIPE_INVOICE_ITEMS_LIST.
The matcher is arithmetic over a table, not a model call
The whole ranking function is 65 lines at /Users/cyrusmassoumi/miscsubjects-pages/functions/_lib/object_contract.js:605-669. The query is lowercased and split on non-alphanumerics into terms of two characters or more. Every enabled row in the directory is scored against those terms over a haystack built from its key, its category and its description:
- term appears in the key: +3 (line 618)
- term appears anywhere else in the row: +1 (line 619)
- the row is a pinned canonical answer for this intent: +1000 (line 621)
- the row is on the demote list, meaning rows that look right but need a channel id the caller does not have: −6 (line 622)
Rows scoring zero are dropped; the top twelve survive (line 626).
That is why the ranked list above is so strange below position one. The query "what time is it" splits into what, time, is, it. The two-letter terms is and it are substrings of issues, so every GitHub issues row scores. The comment sitting above the pin at line 561 says exactly this: the 2-letter query words "is"/"it" substring-match "issues" in GITHUB_LIST_ISSUES and outrank NOW, so "what time is it" hits the wrong door. The fix is not a better retriever. It is a hand-written regex table, ASK_CANONICAL (lines 560-593), that pins about twenty common intents to one correct key each and adds 1000 points to it. NOW sits at the top of the list above because a regex matched \btime is it\b, not because scoring found it.
This is worth stating plainly rather than dressing up: the resolve step is a keyword search with a manual override list, and keyword search over tool descriptions is known to be weak. The ToolRet benchmark put six classes of retrieval model against 7,600 retrieval tasks over 43,000 tools; the best of them, NV-embed-v1, reached an nDCG@10 of 33.83. Substituting retrieved tools for the oracle set dropped GPT-3.5's pass rate on ToolBench-G1 by 11.40 points. A dense retriever here would probably beat substring counting, and it is not deployed.
When nothing matches, the answer says so
curl -s "https://miscsubjects.com/api/dispatch?ask=zzzqqwx"{ "count": 0, "best": null,
"note": "No capability matched. GET /api/dispatch?registry=1 for the full list, or refine the words.",
"registry": "https://miscsubjects.com/api/dispatch?registry=1" }The parallel failure is a key that does not exist. Invoke catches it through didYouMean (functions/api/dispatch.js:1914-1925), which runs a bounded Levenshtein of edit distance ≤3 plus a substring pass over every key (nearestKeys, lines 1830-1836):
{ "error": "unknown_key", "attempted": "NOW_TIME", "ran": false,
"did_you_mean": [ { "key": "NOW", "read": "https://miscsubjects.com/api/dispatch?key=NOW" } ],
"fix": "You invoked a key that does not exist. Nothing ran. Use one of did_you_mean (GET its ?key= for the exact call), or GET ?ask=<what you want in plain words> to find the right one." }ran: false is the load-bearing field. The response also carries an HTTP header: x-ms-agent-note: Do not tell the user this worked — nothing ran. A key far enough away from everything, such as CURRENT_TIME, returns an empty did_you_mean and a different fix string pointing at ?ask= and ?registry=1.
Step 2 hands over one manual, not a schema
curl -s "https://miscsubjects.com/api/dispatch?key=NOW&format=markdown"4,423 bytes, complete, printed here with nothing removed but the token placeholders:
## §SELF — miscsubjects capability (paste without context)
**Principle:** Self-explaining payload — no external context required.
**Path:** OIP > NOW > NOW
**Capability:** `NOW` — Return the current time from the build clock in Pacific time
(America/Los_Angeles). WHEN_TO_USE: any object or model that needs the current date or time.
ARGS: none EX: [NOW][/NOW] OUTPUT: { now, today, time, zone, iso }
**RUN NOW (open this URL):** https://miscsubjects.com/api/dispatch?invoke=NOW&share=<TOKEN>
- **run it:** POST https://miscsubjects.com/api/dispatch {"key":"NOW","body":"<args>"}
- **inputs:** {"args":"none"}
- **outputs:** { now, today, time, zone, iso } — Pacific-offset ISO
- **auth · risk:** none · low
### What this token can do here (computed for: public)
- **contract** — GET …?key=NOW&format=markdown → this object's full contract
- **confirm** — GET …?confirm=INV_ID → public proof that an invocation happened
### Machine Contract
- Read this article first; do not infer the row shape from memory.
- If the call returns ran:false or proof.ok:false, read the receipt and repair the failed
invocation instead of narrating success.
- If the token denies the call, report the denial exactly; do not switch to a broader action.
### Invocation, Ledger, Repair
- append-only ledger: https://miscsubjects.com/api/invocations?object_id=NOW
- receipt pattern: https://miscsubjects.com/api/dispatch?receipt=inv_ID&share=<TOKEN>
- replay: POST /api/dispatch {"replay":"inv_ID"}
- repair: POST /api/dispatch {"key":"NOW","body":"corrected args","repairs":"inv_ID"}
### Troubleshooting
- **unknown key** — Use the did_you_mean links or ask URL; never guess another key.
- **argument/body mismatch** — Read inputs/example_args here, then retry with repairs: inv_ID.
- **expired or corrupted token** — Report token_expired/token_corrupted from the response.
- **tool returned ok:false / exit nonzero** — Do not call it sent. Read the receipt, fire a repair.Five things are in there and each answers a question a cold agent would otherwise guess at. Inputs and outputs answer what do I send and what comes back. Run-now answers what if my only tool is opening a URL. The affordance block, headed "computed for: public", answers which of these moves will my credential actually survive; it is computed against the presented token, so an anonymous reader sees two operations and an owner sees nine. The machine contract answers what do I do when it fails, in imperative sentences aimed at a model rather than a person. Troubleshooting is the same four failures this page catalogues below, shipped inside every contract so the fix travels with the tool.
The field-by-field definition of the row that generates this block is in what a directory row is. What is relevant here is the size: 4,423 bytes for one capability, fetched only when a capability has been chosen. The 876-row registry is 1,606,794 bytes.
Step 3 runs it, and the denial names which of five things went wrong
NOW needs no credential to read, but every invocation is authenticated. There is no anonymous write plane.
curl -sS -X POST https://miscsubjects.com/api/dispatch \
-H "x-terminal-key: $TERMINAL_KEY" \
-H "content-type: application/json" \
-d '{"key":"NOW","body":""}'Real response, trimmed:
{ "ok": true, "ran": true, "kind": "invocation_result", "trace": "t_06myig2y",
"result": "{\"now\":\"2026-07-25T22:02:44-07:00\",\"today\":\"2026-07-25\",\"zone\":\"America/Los_Angeles\"}",
"cost": 0,
"proof": { "ok": true, "did": "DONE — NOW", "invocation_id": "inv_yu9ni6w7y9",
"confirm": "https://miscsubjects.com/api/dispatch?confirm=inv_yu9ni6w7y9",
"receipt": "https://miscsubjects.com/api/dispatch?receipt=inv_yu9ni6w7y9" },
"invocation": { "actor": "owner:terminal-key",
"fingerprints": { "algorithm": "sha-256",
"input": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"output": "db21d92eaed3b25df5e97a1e72f9a70c5b2db351197db71c388f4e98e08b0b3b",
"contract": "b359611ee57c925ee9e4d93b80f2a4533dd214c61c45b77f694c2650e917c977" } } }body is a pipe-joined positional argument string: "" when the capability takes none, "open||30" for a three-argument row. The same two fields invoke every row in the catalogue.
A share token is a row, a clock and a use count, and it can only ever shrink
The owner mints a scoped link instead of handing out the master key:
curl -s -H "x-terminal-key: $TERMINAL_KEY" \
"https://miscsubjects.com/api/dispatch?mint_share=1&scope=row:NOW&ttl=1&purpose=article-measurement"That returned fingerprint cap_ae711ea248b13828, scope row:NOW, risk_ceiling: low, expires_at: 2026-07-25T21:37:47-07:00, and contract pin b359611e…. The response states the rule: this row token fails closed if the current object contract no longer has this fingerprint. Editing the capability revokes every token minted against the old version of it, automatically.
The token also explains itself to whoever holds it, at ?explain=1&share=<TOKEN>, and the delegation law it publishes is the macaroon rule: a holder may mint only an equal-or-narrower child; child uses are reserved from the parent; payload ceilings inherit or shrink; every invocation validates all ancestors. Birgisson and colleagues described the underlying construction as credentials that "embed caveats that attenuate and contextually confine when, where, by who, and for what purpose a target service should authorize requests."
Every denial, measured live against that token:
| What was presented | Response | HTTP | Why it is a distinct string |
|---|---|---|---|
| nothing at all | token_corrupted, can_act: false, ran: false | 401 | no anonymous invoke plane exists |
the token, invoking NOW (in scope) | ok: true, actor cap:cap_ae711ea248b13828 | 200 | the allowed case |
the token, invoking TIME_NOW (out of scope) | scope_mismatch + "This token is LIVE, but it is not allowed to invoke TIME_NOW… it is not expired." | 401 | scope failure is not a clock failure |
| the token with its last 6 characters cut | token_corrupted + "almost always because the link was TRUNCATED or altered on copy-paste" | 401 | truncation is the common cause and is not expiry |
| the same token 76 seconds after a 60-second TTL | token_expired + "Your token is EXPIRED. Nothing was sent or run." | 401 | expiry is recoverable by minting; truncation is recoverable by re-copying |
Separating token_corrupted from token_expired is a deliberate cost. The function that does it, tokenDead (functions/api/dispatch.js:1928-1942), runs a second signature parse purely to tell the two apart, because a model told "your token went bad" will mint a new one when it should have re-copied the link. Every denial also carries x-ms-agent-note: Do NOT tell the user it worked — it did not.
Denied attempts are ledgered under the fingerprint before the 401 is returned (functions/api/dispatch.js:3497). A denial is evidence, not silence. That is the point of the signed denial receipts euan21 built into Capframe: "revocable, signed denial receipts (HMAC-SHA256)."
Step 4 exists because an agent asked to prove its own work invented the proof
This is the strongest argument on the page and it is not this system's argument. In a controlled two-condition experiment reported on Hacker News in March 2026, an agent running without runtime enforcement "fabricated an audit record — invented a governance event that never happened and presented it as compliance evidence." The fix the authors shipped was structural rather than behavioural: write the audit record from the engine, not from the agent, and chain it with SHA-256.
That is the design here. The receipt is written by the dispatcher after the runner returns, in the same code path that produced the result, and the acting model has no write access to it. The alternative is an agent that reports success and produces no record. Sidk24 described that state after an agent modified 47 files and broke a build: "there is no structured trace, no cost attribution per task, no permission audit trail, and no session replay." Four missing things; the receipt object below carries all four.
Two routes read it, and the split matters.
Public confirmation, no credential:
curl -s "https://miscsubjects.com/api/dispatch?confirm=inv_yu9ni6w7y9"{ "kind": "public_receipt/v2", "confirmed": true, "ok": true,
"status": "PROVEN_MATERIAL_RESULT",
"headline": "NOW produced material output at 2026-07-25T22:02:44-07:00.",
"identity": { "invocation_id": "inv_yu9ni6w7y9", "actor": "owner:terminal-key",
"disclosure": "Owner/CLI/legacy actor label; no bearer credential is exposed." },
"integrity": { "fingerprints": { "algorithm": "sha-256",
"input": "e3b0c442…b7852b855", "output": "db21d92e…e08b0b3b" },
"tamper_rule": "Changing the recorded input, output, contract or lineage changes its
fingerprint or chain commitment." },
"execution": { "private_payload_boundary": "Request and response content remain in the scoped
forensic receipt. This public object exposes cryptographic fingerprints and navigable proof only." } }An unknown id returns confirmed: false and "No such invocation — it did not happen." at HTTP 404. The negative is as citable as the positive.
Forensic receipt, credentialed:
curl -s -H "x-terminal-key: $TERMINAL_KEY" \
"https://miscsubjects.com/api/dispatch?receipt=inv_yu9ni6w7y9"{ "kind": "receipt",
"story": "owner:terminal-key invoked NOW → {\"now\":\"2026-07-25T22:02:44-07:00\"…} at 2026-07-25T22:02:44-07:00.",
"receipt": { "id": "inv_yu9ni6w7y9", "trace_id": "t_06myig2y", "object_id": "NOW",
"actor": "owner:terminal-key", "material": true, "waste": false,
"tokens_in": 0, "tokens_out": 0, "cost_usd": 0,
"event_id": "a2443e09-113e-44df-8718-848a98d11740",
"request_full": "", "response_full": "{\"now\":\"2026-07-25T22:02:44-07:00\",…}",
"replay_of": null, "repairs": null, "repaired_by": "inv_sbeb4t5ao2",
"authorized_by": { "actor": "owner:terminal-key",
"note": "not a recorded capability token (owner key, cli, or legacy share) — no token provenance record" } },
"verbs": { "replay": { "method": "POST", "body": { "replay": "inv_yu9ni6w7y9" } },
"repair": { "method": "POST", "body": { "key": "NOW", "body": "<corrected args>",
"repairs": "inv_yu9ni6w7y9" } } } }Without a credential that route returns 401 with "receipt needs an owner access key, admin cookie, read/act token, or the exact scoped token that created this invocation." A tenant token reading another tenant's receipt gets tenant_receipt_isolation at 403.
request_full and response_full hold the bytes, not a summary. A summary of a failed call is somebody's opinion about the failure; the payload is the failure. The Apache Gravitino project reached the same field list from a different direction: "Emit a structured audit record for every MCP tool invocation, capturing the calling principal, tool name, and allow/deny outcome." Rafaself's gateway contract reached the opposite conclusion about bodies, specifying "structured audit logging for MCP tool calls without exposing credentials, signed request data, raw AWS responses, or CloudWatch log message contents." Both are defensible and they genuinely conflict. Gravitino's record is an operational audit trail; rafaself's is a cross-cutting log with an explicit non-goals list, designed to be safe to ship to CloudWatch. The split here follows neither: the public object is fingerprints-only, which is rafaself's position, and the credentialed object is full bytes, which is what debugging needs. The boundary is authorisation, not redaction.
Replay repeats the input; repair supersedes it
Both are POST verbs on the same endpoint, both write new receipts, and they do different things to the lineage graph.
curl -s -X POST https://miscsubjects.com/api/dispatch \
-H "x-terminal-key: $TERMINAL_KEY" -H "content-type: application/json" \
-d '{"replay":"inv_yu9ni6w7y9"}'Returned inv_53l71tl1i0 with replay_of: "inv_yu9ni6w7y9" and a link back to the source receipt. Replay reads the recorded request body out of the ledger event and re-fires the same object with the same input (functions/api/dispatch.js:3355-3370); the caller supplies no arguments. {"replay":…, "key":…} together is rejected: "replay and key are mutually exclusive" at HTTP 400. An unknown id is "unknown invocation" at 404. Replay also requires authority over the source receipt and its object, not just the object.
curl -s -X POST https://miscsubjects.com/api/dispatch \
-H "x-terminal-key: $TERMINAL_KEY" -H "content-type: application/json" \
-d '{"key":"NOW","body":"","repairs":"inv_yu9ni6w7y9"}'Returned inv_sbeb4t5ao2 with repairs: "inv_yu9ni6w7y9". Then, re-reading the original receipt afterwards:
{ "id": "inv_yu9ni6w7y9", "replay_of": null, "repairs": null, "repaired_by": "inv_sbeb4t5ao2" }The back-link is written after the new invocation logs, by linkRepairedBy (functions/api/dispatch.js:3482-3484). Nothing is mutated or deleted: the bad receipt keeps its bad payload and gains a pointer to its successor.
| replay | repair | |
|---|---|---|
| body you send | {"replay":"inv_ID"} | {"key":…,"body":"corrected","repairs":"inv_ID"} |
| input used | the recorded one, read from the ledger event | the new one you supply |
| forward edge on the new receipt | replay_of | repairs |
| back edge written on the old receipt | none | repaired_by |
| idempotency collapse applies | no | no |
| what it is for | reproducing a result, testing a fix to the runner | superseding a wrong call without erasing it |
Repair is the reason did_you_mean and the argument-mismatch guidance both say retry with repairs: inv_ID so lineage closes: a corrected call that does not name what it corrects leaves a dangling failure in the ledger.
The whole loop, copy-paste, ending in a URL anyone can open
# 0. one credential, never printed
export TERMINAL_KEY="<your key>"
# 1. RESOLVE — plain words in, keys out
curl -s "https://miscsubjects.com/api/dispatch?ask=what%20time%20is%20it" \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d['best']['key'])"
# -> NOW
# 2. CONTRACT — read it before calling it
curl -s "https://miscsubjects.com/api/dispatch?key=NOW&format=markdown"
# 3. INVOKE — and capture the receipt id
INV=$(curl -s -X POST https://miscsubjects.com/api/dispatch \
-H "x-terminal-key: $TERMINAL_KEY" -H "content-type: application/json" \
-d '{"key":"NOW","body":""}' \
| python3 -c "import json,sys; print(json.load(sys.stdin)['proof']['invocation_id'])")
echo "$INV"
# -> inv_yu9ni6w7y9
# 4. RECEIPT — public proof, no credential
echo "https://miscsubjects.com/api/dispatch?confirm=$INV"
curl -s "https://miscsubjects.com/api/dispatch?confirm=$INV" \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d['status'], d['headline'])"
# -> PROVEN_MATERIAL_RESULT NOW produced material output at 2026-07-25T22:02:44-07:00.The URL that last block prints is openable by anyone, forever, with no credential: <https://miscsubjects.com/api/dispatch?confirm=inv_yu9ni6w7y9>.
Six failures, their exact strings, and what to do about each
Every string below was produced by a live call, not transcribed from documentation.
| Symptom | Exact response | HTTP | Cause | Fix |
|---|---|---|---|---|
| Key does not exist, close to a real one | {"error":"unknown_key","attempted":"NOW_TIME","ran":false,"did_you_mean":[{"key":"NOW",…}]} | 404 | key guessed from memory instead of read from ?key= | fire one of did_you_mean, then re-invoke with repairs set to the failed id |
| Key does not exist, close to nothing | "fix":"No capability by that name. GET ?ask=<what you want> or ?registry=1 for the full list." | 404 | wrong vocabulary entirely | go back to step 1 |
| Argument or body mismatch | contract field "argument/body mismatch" — "Read inputs/example_args here, then retry with repairs: inv_ID so lineage closes." | 200 with ok:false | positional pipe args in the wrong order or count | re-read inputs in the contract; re-fire with repairs |
| Token cut on copy-paste | {"error":"token_corrupted","can_act":false,"ran":false,"problem":"Your token failed its signature check — almost always because the link was TRUNCATED…"} | 401 | truncated URL, not an expired one | re-copy the entire link including the tail after the final dot |
| Token past its clock | {"error":"token_expired","can_act":false,"ran":false,"problem":"Your token is EXPIRED. Nothing was sent or run."} | 401 | TTL elapsed | owner mints a fresh scoped link |
| Token live but wrong row | {"error":"scope_mismatch","fingerprint":"cap_ae711ea248b13828","note":"This token is LIVE, but it is not allowed to invoke TIME_NOW…"} | 401 | attenuated token used outside its allow-list | ask for a wider link; never substitute a different capability |
| The tool itself failed | {"ok":false,"ran":true,"result":"ERR:fn:D1_QUERY:D1_ERROR: no such table: no_such_table: SQLITE_ERROR"} | 200 | the runner executed and returned an error | read the receipt, correct the body, fire a repairs call |
| Upstream HTTP error | {"ok":false,"ran":true,"result":"ERR:http:404:{\"message\":\"Not Found\",…}"} | 200 | remote API rejected the shaped request | same — the receipt holds the upstream body verbatim |
| Ran, produced nothing | {"ok":true,"ran":true,"proof":{"ok":false,"did":"FAILED — "},"material":false} | 200 | empty result, e.g. a KV key that does not exist | proof.ok tracks material output; ok tracks absence of an error string. They disagree here on purpose |
ok is computed as !shaped && !failed, where failed is the regex /^(?:ERR(?::|$)|PROVIDER_ERROR(?::|$))/ over the result string (functions/_lib/object_contract.js:2191-2193). ran is !shaped; it distinguishes a real execution from a {"shape":true} dry run, which returns the fully-composed outbound payload and fires nothing.
Every one of those failures still writes a receipt. inv_swzanrzqjo is the D1 error above; it is a real, permanent, addressable record of a call that did not work, with material: false. Outcomes include failure, or the ledger is a highlight reel.
What the loop costs, measured
Ten samples per endpoint from the same Mac in the Pacific timezone to the production Cloudflare edge, on 2026-07-25. The five calls below are the published harness. INV is the harmless NOW receipt id created by the runnable loop above.
for i in $(seq 10); do curl -s -o /dev/null -w '%{time_total} %{size_download}\n' \
'https://miscsubjects.com/api/dispatch?ask=what%20time%20is%20it'; done
for i in $(seq 10); do curl -s -o /dev/null -w '%{time_total} %{size_download}\n' \
'https://miscsubjects.com/api/dispatch?key=NOW&format=markdown'; done
for i in $(seq 10); do curl -s -o /dev/null -w '%{time_total} %{size_download}\n' \
-X POST 'https://miscsubjects.com/api/dispatch' \
-H "x-terminal-key: $TERMINAL_KEY" -H 'content-type: application/json' \
-d '{"key":"NOW","body":""}'; done
for i in $(seq 10); do curl -s -o /dev/null -w '%{time_total} %{size_download}\n' \
"https://miscsubjects.com/api/dispatch?confirm=$INV"; done
for i in $(seq 10); do curl -s -o /dev/null -w '%{time_total} %{size_download}\n' \
-H "x-terminal-key: $TERMINAL_KEY" \
"https://miscsubjects.com/api/dispatch?receipt=$INV"; done| Step | min | median | max | response bytes |
|---|---|---|---|---|
1 resolve ?ask= | 62.3 ms | 66.7 ms | 82.0 ms | 12,332 |
2 contract ?key=…&format=markdown | 54.0 ms | 97.9 ms | 150.1 ms | 4,423 |
3 invoke POST {key,body} | 784.9 ms | 940.3 ms | 2,761.0 ms | 15,675 |
4a confirm ?confirm= (public) | 58.5 ms | 98.3 ms | 1,770.5 ms | 15,009 |
4b receipt ?receipt= (credentialed) | 66.0 ms | 81.6 ms | 114.5 ms | 12,971 |
The median read step stayed between 66.7 and 98.3 milliseconds. The 940.3-millisecond invocation median was more than nine times the slowest read median. A POST does the work, then writes the invocation row, writes the ledger event, computes three SHA-256 fingerprints, and finalises the idempotency key before responding. Resolve + contract + invoke + public confirmation sums to 1,203.2 milliseconds at the medians; invocation accounts for 78.1% of it.
That overhead is at the high end of what the literature reports for enforcement layers, because it is doing more than policy evaluation. AgentWall, which intercepts and evaluates but persists asynchronously, measured "average decision latency is 0.198 ms and the p95 latency is 0.745 ms" over 14 policy tests. Agent-Sentry's deterministic provenance checks are single-digit milliseconds; its LLM-judge layer costs about 1.2 seconds per call, which is why it fires on only a small residual. The right reading: sub-millisecond is achievable for a decision, while this measured durable call took 940.3 milliseconds. Persistence and the runner are the combined cost; this harness does not isolate their shares.
Cloudflare's published Workers Standard price is "10 million included per month +$0.30 per additional million" requests, with duration not billed. Four requests at the marginal rate is 4 × $0.30 / 1,000,000 = $0.0000012 per complete loop, or $1.20 per million loops. The D1 side is "First 25 billion / month included + $0.001 / million rows" read and "First 50 million / month included + $1.00 / million rows" written; each invocation writes an invocation row and a ledger event, so two writes, so $0.000002 per loop at the marginal rate. Total marginal cost of resolve + contract + invoke + receipt, with the receipt durably stored: about $0.0000032. Below the included tiers it is zero.
What it replaces is the other way to make 876 capabilities reachable: put their definitions in the model's context. That comparison, with its own measurements, is 891 tools, zero tool schemas, and the projection of this same catalogue into MCP is MCP as a projection. The relevant number for this page is the one on the resolve step: a ?ask= response is 12,332 bytes and is fetched once, at the moment a capability is needed, by an agent that had zero of the catalogue loaded a second earlier.
Four honest weaknesses
Four round trips happen before any work does. For a single call that is roughly 600 ms of latency spent on discovery and reading before the invoke even starts. An agent that already knows the key skips straight to step 3, and any agent doing more than one call with the same capability should. The loop is a cold-start protocol, not a per-call tax; nothing enforces that, and a naive agent will re-resolve every time.
The resolver can miss and does. It is substring scoring with about twenty hand-pinned intents. Anything outside the pin list is at the mercy of term overlap between the user's words and the row's description, which is precisely the failure ToolRet quantified: even a strong general-purpose retriever managed nDCG@10 of 33.83 on tool retrieval, and worse retrieval measurably lowered downstream task pass rates. A miss here is visible in the ranked list, and the agent can reject it. It is still a real miss.
A model still has to decide correctly. Nothing in these four steps prevents an agent from reading the right contract and then choosing the wrong capability, or supplying plausible-looking wrong arguments. The receipt makes that visible afterwards. It does not prevent it. aderix put the general version of this sharply: "If an LLM hallucinates in production and decides to execute a destructive tool defined in SKILL.md (like dropping a table or issuing a Stripe refund), a Git PR approval process doesn't help you mid-flight." The runtime answers here are the risk ceiling on the token and the owner gate on high-risk rows. Both are real, and both are narrower than a general solution.
Nobody else implements this. ?ask=, ?key=, ?confirm=, ?receipt=, replay and repairs are the shapes one system chose. An agent that has internalised MCP will look for tools/list and tools/call. The specification says a client "SHOULD" keep "a human in the loop with the ability to deny tool invocations", leaving the record entirely to the implementation. Convergent work exists and is not compatible: Capframe splits the same loop into find, bind and guard with a public JSON Schema wire format; Rampart evaluates "every shell command, file operation, and MCP tool call … against your rules before it executes" behind a hash-chained trail; socket-link/ampere proposes to "enable agents to discover, select, and invoke MCP server tools through the existing Tool sealed interface, with tool availability emitted as events"; jithinraj's demo "emits a signed, portable receipt per tool call (JSON you can verify offline)". Four groups, four wire formats, one shape. Until one of them is a specification rather than a repository, a stranger's agent has to read the contract to know the shape. That limitation is the argument for step 2.
kxbnb, arguing for a proxy enforcement point outside the agent's context, named the thing all of these are actually for: "The audit trail piece is critical too. Being able to answer \"why was this blocked?\" after the fact builds trust with teams rolling this out." That question has an address here. It is ?confirm=, and it needs no credential to ask.
Key evidence
4 more ranked claims
Model review5 contributions · 1 modelExpand the recursive review layer
/api/articles/dispatch-four-step-loop/contributionsAsk this article · 8 suggested prompts
Text the build (+14245134626) or WhatsApp — slug|question creates a question node. Paste evidence with ingest slug|q:NODE_ID|your paste.