Skip to content

How each claim is held

Every claim the pages make about what the agent hosts do, what the plugin hands them, and what the verifiers agree on with vectors other people wrote, is held by one of three things: a file of this repository that fails when the claim stops being true, a result that was read once and is kept here as history, or nothing yet. The table says which, per claim, and the last column names the file: a test or a specification that is in the tree today. Every file named on this page is checked to exist.

The words mean one thing each:

  • held by a test: a case in the suite fails when the claim stops being true. That is the product’s half: what mnema answers, records and prints. For the hosts that run on a free runner with no model and no account, Claude Code and VS Code, it is the host’s half too: the real host is started against a stand-in for its model, and the case reads what the host did. Those cases run on every pull request against one pinned release of the host, and every week against the newest ones, and a round that measured another release than the one it names is red.
  • historical: a result read on one day, with one host or model and one number of runs. It is stated with those and with no link, because nothing in the tree reruns it.
  • not held yet: nothing in the tree holds the claim. It is the host’s behaviour, read once against a real host, and it expires with that host’s version.

What the verifier proves, and what it does not, is on What it proves, not here.

The whole table, of which the README carries a summary: what each host does with mnema, and how each cell is known.

Host (a) The MCP server (a) Rules in a file (b) The opening of a session (c) A refusal before a write (d) A pause for a person The rules beside a write (not a rung) Rung
Claude Code yes, held by a test yes, held by a test yes, held by a test yes, held by a test yes, held by a test yes, held by a test (d)
VS Code’s agent yes, read on VS Code 1.137 with Copilot Chat 0.65, 23 September 2026 yes, held by a test yes, read on VS Code 1.137 with Copilot Chat 0.65, 23 September 2026 yes, held by a test yes, held by a test not ported (d)
Cursor’s command-line agent yes, read on Cursor agent 2026.09.18, 23 September 2026 yes, held by a test yes, read on Cursor agent 2026.09.18, 23 September 2026 yes, read on Cursor agent 2026.09.18, 2 October 2026 no, read on Cursor agent 2026.09.18, 30 September 2026 not ported (c)
Codex yes, held by a test yes, held by a test yes, held by a test yes, held by a test no, held by a test yes, held by a test (c)
GitHub Copilot CLI yes, held by a test yes, held by a test yes, held by a test yes, held by a test yes, held by a test yes, held by a test (d)
OpenCode yes, held by a test yes, held by a test yes, held by a test yes, held by a test no, held by a test yes, held by a test (c)
Gemini CLI yes, held by a test yes, held by a test yes, held by a test yes, held by a test no, held by a test yes, held by a test (c)
Qwen Code yes, held by a test yes, held by a test yes, held by a test yes, held by a test no, held by a test yes, held by a test (c)
Cursor’s editor documented, not measured (read 10 October 2026) documented, not measured (read 10 October 2026) not ported not ported not ported not ported (a), documented, not measured
Antigravity’s command line (agy) documented, not measured (read 10 October 2026) documented, not measured (read 10 October 2026) not ported not ported not ported not ported (a), documented, not measured
Factory Droid documented, not measured (read 8 October 2026) documented, not measured (read 8 October 2026) not ported not ported not ported not ported (a), documented, not measured
Goose documented, not measured (read 8 October 2026) documented, not measured (read 8 October 2026) not ported not ported not ported not ported (a), documented, not measured

Claude Code. When a subagent stops: yes, held by a test. After a question to the person is answered: yes, held by a test.

Codex. The refusal fails open where nothing says the path is governed: no mnema on the PATH (held by its test) or a hook that errors (read in pre_tool_use.rs), lets the patch through. A mnema that is there and cannot answer, at a path a rule that refuses it addresses, is a deny from the hook (held by the test of the node it runs on), and so is a record the gate cannot read (held by its test). The opening is cut to Codex’s own ceiling, 2,500 tokens of 4 UTF-8 bytes (output_spill.rs), at a whole rule, held by the same test. When a subagent stops: documented, not measured (read 9 October 2026). After a question to the person is answered: documented, not measured (read 21 October 2026).

GitHub Copilot CLI. Its hooks are read under the PascalCase event names, which hand the payload in Claude Code’s tool names (Write, Edit) and snake_case fields, with the path under path. A command hook that exits non-zero denies the call there (read in the hooks reference), so the plugin’s ends in exit 0 whatever happened: no mnema on the PATH is held by its test; a hook past its timeout is let through by the host (read, not measured: read 14 October 2026). The opening is the same text Claude Code gets, cut at 10,000 units, far under the 10 MiB the host accumulates (read, not measured). The rules of the record at an edit ride in postToolUse, whose top-level additionalContext the host appends to the result of the write (held by its arrival case): the hook before a write has no field for text. Run without a person (copilot -p), a hook’s ask is a denial (held by its test); with one, the host asks (held by its test). The model is any the host is pointed at, with no GitHub account (COPILOT_OFFLINE), under the license at LICENSE.md. When a subagent stops: documented, not measured (read 9 October 2026). After a question to the person is answered: documented, not measured (read 21 October 2026).

OpenCode. Its hook is a JavaScript module the person copies into .opencode/plugins/ (see docs/hosts.md), which runs mnema as a process: the refusal throws, and OpenCode hands the model the reason as the call’s error; the opening is added to the system prompt through experimental.chat.system.transform, a hook the host marks experimental, so the contract pins the version it was held on. The tools it writes through are write and edit, and apply_patch for the models that use it (registry.ts); a file a shell command writes meets no hook, as on every host. The module fails open where nothing says the path is governed: with no mnema on the PATH, or a program of that name that is not this product, the write goes through (held by its test); a mnema that is there and cannot answer (no node takes it, it exits non-zero, it does not finish) refuses a write at a path a rule that refuses it addresses, and runs on the first node that takes it (held by the test of the node it runs on). OpenCode’s own permission.ask hook is not used, so a rule that only asks for a person is let through in silence. The opening is the text Claude Code gets, cut at 10,000 units; a ceiling of OpenCode’s on a system prompt was not found in its documentation or in the source read. The model is any the host is pointed at, with no account, under the MIT license at LICENSE. After a question to the person is answered: documented, not measured (read 21 October 2026).

Gemini CLI. Its hooks come from an extension, a folder the person installs from a checkout of this repository (see docs/hosts.md): the extension declares the server, and its hooks run one script that runs mnema as a process. The refusal answers BeforeTool on its two tools that write, write_file and replace, with a top-level decision of deny and its reason, which Gemini CLI hands the model as the call’s error; a file a shell command writes meets no hook, as on every host. The opening is a SessionStart hook’s additionalContext: Gemini CLI escapes < and > in it and wraps it in <hook_context>, and in a non-interactive run puts it before the prompt (read in types.ts and gemini.tsx); a ceiling of its own on that text was not found in the source read, so it is cut at 10,000 units. The rules of the record at an edit ride in an AfterTool hook, whose hookSpecificOutput.additionalContext the host appends to the result of the write inside <hook_context> (held by its arrival case): BeforeTool has no field for text. A hook that exits with a code other than 0 or 2 is a warning and the call goes on, and the script fails open where nothing says the path is governed: with no mnema on the PATH, or a program of that name that is not this product, the write goes through (held by its test); a mnema that is there and cannot answer (no node takes it, it exits non-zero, it does not finish) refuses a write at a path a rule that refuses it addresses, and runs on the first node that takes it (held by the test of the node it runs on). The documentation lists allow and deny as the decisions of a BeforeTool hook; the source also has an ask, which was not read for what it does with a write and is not used, so a rule that only asks for a person is let through in silence. Run without a person, in a folder it does not trust, the host does not start (exit 55) unless it is told to trust the folder (--skip-trust or GEMINI_CLI_TRUST_WORKSPACE=true, which the contract sets). The model is any the host is pointed at, with no account, under the Apache-2.0 license at LICENSE. After a question to the person is answered: documented, not measured (read 21 October 2026).

Qwen Code. Qwen Code installs the Claude Code plugin of this repository as an extension, converted (qwen extensions install on a checkout, see docs/hosts.md), and runs the plugin’s hooks file. It has no hook that calls into a server, so the entry that does is passed over, and the command of its own that this plugin ships for it runs only where QWEN_PROJECT_DIR is set, which Qwen Code sets for every hook and no other host this plugin serves sets (it also sets CLAUDE_PROJECT_DIR and GEMINI_PROJECT_DIR, which is why neither can tell it apart; read in hookRunner.ts). The refusal answers PreToolUse on its two tools that write, write_file and edit, with a permissionDecision of deny, which Qwen Code hands the model as the call’s error; a file a shell command writes meets no hook, as on every host. The opening is a SessionStart hook’s additionalContext, which Qwen Code escapes (< and >) and puts at the end of the system instruction, in a <qwen:session-start-context> block (read in types.ts and llm-chat.ts); a ceiling of its own on that text was not found in the source read, so it is cut at 10,000 units. The host defers the server’s tools, naming them in the first request and sending a schema only when the model asks for one, so the weight of the 27 definitions is not pinned for it. The handler answers a refusal and nothing else, so a rule that only asks for a person is let through in silence. With no mnema on the PATH the write goes through (held by its test); a program of that name that is not this product cannot answer, and a write at a path a rule that refuses it addresses is refused (held by its test). The model is any endpoint that speaks the OpenAI protocol the host is pointed at, with no account, under the Apache-2.0 license at LICENSE.

Cursor’s editor. Only its documentation was read, on 10 October 2026: the agent of the editor runs preToolUse and sessionStart hooks, a preToolUse that answers deny blocks the action, ask is accepted there and not enforced, and a sessionStart hook can add context to the session. That documentation says the hooks in Claude Code’s settings files are loaded, and says nothing of the hooks of a plugin installed in Claude Code, which the command-line agent was read to load. Whether the editor runs this plugin’s hook, sets CURSOR_VERSION for it and hands it the payload the command-line agent does was not read, so nothing above the first rung is promised for it. The script that reads it is plugin/captures/cursor-ide-script.md.

Antigravity’s command line (agy). Only its documentation was read, on 10 October 2026, and its hooks page again on 14 October 2026 (read 14 October 2026). It documents hooks in a dialect of its own, in a hooks.json kept in the project’s .agents folder or in an installed plugin: six events (SessionStart, PreToolUse, PostToolUse, PreInvocation, PostInvocation, Stop); a SessionStart answers with injectSteps, steps put into the conversation before the first model invocation; a PreToolUse answers allow, deny, ask, force_ask or deny_unless_prior_grant and is handed toolCall.name and toolCall.args; the tools that write are write_to_file, replace_file_content and multi_replace_file_content, each with its path under TargetFile; a PreInvocation can add a message to the conversation. All of it is documented, not measured: nobody has run the host here. This plugin ships no hooks file in that dialect and no reader of that payload, so nothing above the first rung is promised for it. The script that reads it is plugin/captures/antigravity-cli-script.md.

Each cell says how it is known: held by a test of this repository; read once against the real host, on the version and the day it names, and held by no file yet; or documented, not measured: the host’s own documentation or code says the host does it, at the commit or on the page the link names, and nothing was run. Not ported: the plugin hands that host nothing for it. For the first three hosts, the rules file is the one mnema rules-file --host prints in the host’s format, and the test holds what it prints, not how the host matches its globs; for the others, it is AGENTS.md, which the host’s documentation says it reads (Gemini CLI’s is GEMINI.md: it reads AGENTS.md only where its settings list it). A host reaches a rung when every rung before it is a yes; the last column is not a rung: it says whether the host is handed the rules of the record addressed at a file beside a write that goes through, and a host can have it with or without the pause. Aider was read too, and is not here, because it has no MCP client.

Where the record can reach the work, host by host

Section titled “Where the record can reach the work, host by host”

Every channel named below can be switched off, and two begin off: the switches page lists them.

The rung table says what a host does at the opening and before a write. This one says, for each moment of the work, whether the host offers something the plugin could ride, and how that is known.

Host A choice settled with the person The host’s own memory A plan or decision file The opening of a session An edit The end of a response A subagent starts A subagent stops
Claude Code PostToolUse on the question tool: yes, held by a test PostToolUse on a write to this project’s memory folder, copied to the private tree by host-memory-mirror, which is on until switched off: yes, held by a test only a rule addressed at the path, beside the write: yes, held by a test yes, held by a test yes, held by a test Stop: documented, not measured (read 21 October 2026) SubagentStart: yes, held by a test SubagentStop: yes, held by a test
VS Code’s agent not read a memory folder of the host, the tool that writes it not read: documented, not measured (read 21 October 2026) PostToolUse, which the host runs whatever the matcher: documented, not measured (read 21 October 2026) yes, read on VS Code 1.137 with Copilot Chat 0.65, 23 September 2026 yes, held by a test Stop: documented, not measured (read 21 October 2026) SubagentStart: documented, not measured (read 21 October 2026) SubagentStop: documented, not measured (read 21 October 2026)
Cursor’s command-line agent no channel no channel postToolUse, with additional context: documented, not measured (read 21 October 2026) yes, read on Cursor agent 2026.09.18, 23 September 2026 yes, read on Cursor agent 2026.09.18, 2 October 2026 stop, which can only ask for a new turn: documented, not measured (read 21 October 2026) not read subagentStop: documented, not measured (read 21 October 2026)
Codex PostToolUse on request_user_input: documented, not measured (read 21 October 2026) its memories are made in the background, with no tool and no hook: no channel PostToolUse on apply_patch: documented, not measured (read 21 October 2026) yes, held by a test yes, held by a test Stop: documented, not measured (read 21 October 2026) not read SubagentStop: documented, not measured (read 9 October 2026)
GitHub Copilot CLI postToolUse on ask_user: documented, not measured (read 21 October 2026) store_memory, which keeps it in the cloud: documented, not measured (read 21 October 2026) postToolUse on a write: documented, not measured (read 21 October 2026) yes, held by a test yes, held by a test agentStop: documented, not measured (read 21 October 2026) not read subagentStop: documented, not measured (read 9 October 2026)
OpenCode tool.execute.after on question: documented, not measured (read 21 October 2026) no channel tool.execute.after: documented, not measured (read 21 October 2026) yes, held by a test yes, held by a test not read not read not read
Gemini CLI AfterTool on ask_user: documented, not measured (read 21 October 2026) GEMINI.md, kept by project, the tool that writes it not read: documented, not measured (read 21 October 2026) AfterTool on write_file and replace: documented, not measured (read 21 October 2026) yes, held by a test yes, held by a test AfterAgent, which cannot add context: documented, not measured (read 21 October 2026) no channel no channel

The same three words as the rung table, and two more: no channel is the host itself having nothing at that moment that this product could ride, and not read is that nobody has read the host for it, which says neither yes nor no. A cell that is documented names the event or the tool the host documents for it; that the plugin hands the host something there is held only where a cell says held by a test. The opening and the edit are the rung table’s cells (b) and (c). A cell is not a promise that mnema records at that moment: it is where a hook could reach, and where it does, what the plugin hands over there is on the pages that describe the hook.

The claim Held by File
The rules addressed at the file about to be written are handed over beside the result of that write, and recorded as served a test the-rule-reaches-the-writing.test.ts
The opening document and the notes arrive in a session without anybody asking a test the-record-arrives-unasked.test.ts
Text pushed into a hook stops at a whole rule below 10,000 characters, and says how many it left out a test within-a-hook.test.ts
The opening Codex is handed stops at a whole rule below 2,500 of its tokens (4 UTF-8 bytes each), and Codex hands it over without cutting it a test codex-opens-and-refuses.codex.test.ts
In Codex, a gate before a patch that cannot answer because there is no mnema on the PATH says nothing, and the patch is applied; that a hook that errors does the same is read in Codex’s source and not run a test codex-opens-and-refuses.codex.test.ts
In OpenCode, the module that refuses a write lets it through when no mnema is on the PATH or the one there is another program, and runs no verb of that program but --identify a test opencode-opens-and-refuses.opencode.test.ts
A rule of the record that asks for a person asks only where it is linked, cites its id, and the reply can express no other decision a test the-record-asks-for-a-person.test.ts
In VS Code’s command door, the write waits for a person exactly where the MCP tool would ask a test a-host-that-runs-commands-asks-for-a-person.test.ts
A rule that refuses a write answers deny and cites the rule at each of the three doors, and the refusal wins over a pause a test a-rule-that-refuses-a-write.test.ts
A rule of the record a project inherits is listed by rules, why, governing_rules and both gate doors at the path it addresses, beside the project’s own and naming its origin and commit, labelled by the record that wrote the address (a link of the origin to a rule of the project included); one that refuses or asks applies there, and a refused rule only the origin holds names the clone it opens in; rules counts the inherited addresses apart from the project’s and the brief counts them after the project’s own; the switches stay the project’s; a record that does not verify at the pinned commit governs nothing and the readers say so a test an-inherited-rule-reaches-the-edit.test.ts
The plugin’s hooks file names only events and keys the host reads a test the-hooks-file-says-only-what-the-host-reads.test.ts
The server-only plugin declares the server and runs no hook a test the-server-only-plugin-runs-no-hook.test.ts
The server goes by the directory a client starts it in when the client names no workspace a test a-client-that-names-no-workspace.test.ts
The rules-file verb writes an address as a glob only when it becomes that glob exactly, and names every rule it leaves out a test a-rules-file-carries-only-what-becomes-a-glob-exactly.test.ts
The doctor verb says why VS Code’s agent loads no plugin a test doctor-tells-vs-code-where-the-plugin-is.test.ts
The plugin’s hook and its server declaration name the server alike, so renaming either half fails a test the-rule-reaches-the-writing.test.ts
The plugin’s skills say what they carry a test every-skill-the-plugin-ships-is-what-it-says.test.ts
The numbers and tables the pages draw are read off the file they came from a test the-front-page-says-what-its-sources-say.test.ts
The two example stacks pass the stack validator and skills-ref, the validator the Agent Skills specification publishes, bring no hook, and say truly what they bring a test the-example-stacks-hold.test.ts
The built binary installs each example stack into a sandbox home, with the digest the plan shows, byte for byte, and the record still verifies; skills-ref reads the skill folders it wrote a test the-example-stacks-install.test.ts
The stack index lists the example stacks first, holds no stack, and for a stack kept in this repository carries the digest the package computes over its folder a test the-index-names-the-stacks-it-lists.test.ts
Every file this page names exists a test the-evidence-page-cites-what-exists.test.ts
In Claude Code, the opening document arrives in the first request of a session, at session start, as the text mnema brief --hook prints, and without a decision that was superseded among its rules (the rule that replaced it names it) a test the-session-opens-with-the-record.test.ts
In Claude Code, the host calls the plugin’s server at an edit under the name the plugin gave it, and the rules addressed at the path land beside that write’s result, in the request that comes next a test the-rules-arrive-beside-the-write.test.ts
In Codex, Qwen Code and OpenCode, the rules addressed at the path of a write that goes through reach the model beside that write’s result, handed over by the command that runs before the write, and what the host did with them is read from the request it sends next, with the same read finding nothing where the push is switched off; the fact that the push was live is recorded once for a session of the host, however many writes it makes, and not for a payload with no session id a test the-rule-arrives.codex.test.ts, the-rule-arrives.qwen.test.ts, the-rule-arrives.opencode.test.ts and, for the command, a-rule-is-pushed-beside-a-write.test.ts
In Copilot CLI and Gemini CLI, whose hook before a write has no field for text, the rules addressed at the path of a write that goes through reach the model beside that write’s result, handed over by the command that runs after the write (the verb beside-a-write), and what the host did with them is read from the request it sends next, with the same read finding nothing where the push is switched off; the fact that the push was live is recorded once for a session of the host, and no refusal or asking is recorded by that command a test the-rule-arrives.copilot.test.ts, the-rule-arrives.gemini.test.ts and, for the command, a-rule-is-handed-after-a-write.test.ts
In Claude Code, a PreToolUse hook of type mcp_tool that answers deny stops the write and the model reads the rule, and one that answers ask holds it: with nobody to ask, the call is refused and the model is told which rule asked a test a-refusal-and-a-pause-hold-the-write.test.ts
In VS Code, the agent runs the plugin’s PreToolUse command before create_file: on deny the file is not created and the model reads the rule, and on ask the agent stops on a confirmation and creates nothing a test an-editor-holds-or-refuses-the-write.vscode.test.ts
VS Code’s agent loads the plugin from a folder listed in its chat.pluginLocations setting, and runs the command its hooks file declares a test an-editor-holds-or-refuses-the-write.vscode.test.ts
VS Code puts the text of a command hook that allows a call inside the result of that call, as <PreToolUse-context> a test an-editor-puts-a-hooks-text-in-the-result.vscode.test.ts
Claude Code hands a command hook’s text to the model whole at 10,000 UTF-16 code units, and as a path with a 2,000-unit preview at one more; the text the product hands over for a record too long for it stops below that, at a whole rule a test a-hook-hands-over-ten-thousand-units.test.ts
Claude Code keeps the first 2,048 characters of a server’s instructions and cuts the rest up to 2.1.295, and the first 4,096 since 2.1.296, where CLAUDE_CODE_MAX_MCP_DESCRIPTION_LENGTH=2048 brings it back to 2,048; the contract asks for 2,048, which 2.1.285 and 2.1.287 keep with or without the variable, and the plugin’s own instructions arrive whole a test a-servers-instructions-are-cut-at-2048.test.ts
A write through the shell goes round a rule that refuses it, and the case requires that it does: the limit is a declared one a test the-shell-goes-round.test.ts
Without mnema on the PATH the plugin’s hooks fail open in Claude Code: the session opens with nothing added and a write a rule would refuse goes through a test without-mnema-the-hooks-fail-open.test.ts
A mnema that is there and cannot answer (it exits non-zero, does not answer in ten seconds (six in Gemini CLI and OpenCode, with the search for a node and the identification inside the host’s fifteen), prints what no host reads, or has no node on the PATH that takes it) makes the gate refuse the write in Cursor’s agent, Codex, VS Code, Copilot CLI, Gemini CLI and OpenCode, the reason saying why, only where a rule in force that refuses a write or asks for a person at it addresses that path: a path no such rule addresses, a rule only proposed, rejected or superseded, a project whose only links govern, a rule taken back, and a record where mnema runs and its switch is off, are let through. The first node of the hooks’ PATH is not trusted: where it is below the floor (Cursor’s agent puts the Node it carries, 24.5.0 on 2026.09.18, in front) the hooks run mnema on the first node that takes it, within three seconds, and a node that hangs on its version is cut at one second. Simulated with a first node that says it is 24.5.0 and a mnema that dies on it as the product does; the same condition on the real Node 24.5.0 of the Cursor agent was run by hand, with no model, and not by CI. Not covered: a hook whose own node cannot start or whose handler files are missing, which the host reads as nothing to say a test the-hooks-run-mnema-on-a-node-it-takes.test.ts
A line of the record that the reader cannot read (not JSON, a kind or a field this version does not know, bytes that are not canonical) makes the gate refuse every write it is asked about, at a path no rule addresses too: the command answers deny in each host’s shape with exit 0, and the MCP route answers it as a reply and not as a tool error, the reason naming the tree (public, private or global) and the line in the reader’s words, with nothing recorded; a line of the global tree refuses the writes of a second project on the same machine too; a last line a crash cut short is dropped and refuses nothing; the same record with the line removed refuses only where a rule does a test a-rule-that-refuses-a-write.test.ts
In Claude Code, Gemini CLI, Qwen Code and Codex, a write no rule addresses is not made while the record holds a line of a kind the reader does not know, and the model reads the reader’s refusal a test a-refusal-and-a-pause-hold-the-write.test.ts, gemini-opens-and-refuses.gemini.test.ts, qwen-opens-and-refuses.qwen.test.ts, codex-opens-and-refuses.codex.test.ts
The host contract starts the hosts where only loopback exists, reads where Claude Code connected, and refuses a round that measured another binary than the one it names a test the-instrument-sees-what-leaves.test.ts
Whether a rule kept in a file, or in the mnema record, reaches the model in six hosts (Claude Code, Codex, OpenCode, Copilot CLI, Gemini CLI, Qwen Code), by where it is kept and by moment (the start, late in a session, after a compaction, at an edit): a sentinel found, or not, in the requests the host sends the stand-in model, with a control that keeps the rule nowhere. What was not measured is in the table with its reason (the page) a test the-rule-arrives.test.ts, the-rule-arrives.codex.test.ts, the-rule-arrives.opencode.test.ts, the-rule-arrives.copilot.test.ts, the-rule-arrives.gemini.test.ts, the-rule-arrives.qwen.test.ts and, for the page, the-arrival-page-is-the-table.test.ts
What each host hands the MCP server it starts, read by the server itself in the host’s job, names only. Claude Code: announces itself as claude-code, starts the plugin’s server under node (the launcher) under claude, and leaves CLAUDECODE, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_SESSION_ID and AI_AGENT in its environment a test what-the-host-hands-the-server.test.ts
Codex: announces itself as codex-mcp-client, starts the server directly under codex, and leaves none of the variables of the list a test what-the-host-hands-the-server.codex.test.ts
GitHub Copilot CLI: announces itself as copilot-cli, starts the server directly under copilot, and leaves COPILOT_AGENT_SESSION_ID and COPILOT_CLI_BINARY_VERSION a test what-the-host-hands-the-server.copilot.test.ts
Gemini CLI: announces itself as gemini-cli-mcp-client, starts the server under two node, and leaves GEMINI_CLI a test what-the-host-hands-the-server.gemini.test.ts
OpenCode: announces itself as opencode, starts the server directly under opencode, and leaves none of the variables of the list a test what-the-host-hands-the-server.opencode.test.ts
VS Code’s agent: not measured. In its job no handshake of the plugin’s server is seen and none of its tools is offered, so there is nothing to read; why is not held, and the test goes red the day a handshake arrives a test what-the-host-hands-the-server.vscode.test.ts
The env of the plugin’s server manifest reaches the server, read by the server itself: Codex 0.161.0, Copilot CLI 1.0.94 and Gemini CLI 0.63.0 hand it MNEMA_AGENT_KEY with the value the manifest declares (codex, copilot, gemini) a test what-the-host-hands-the-server.codex.test.ts, what-the-host-hands-the-server.copilot.test.ts, what-the-host-hands-the-server.gemini.test.ts
Claude Code 2.1.296: a MNEMA_HOME in the environment the host was started with reaches the plugin’s server; so does a MNEMA_AGENT_KEY there, because the manifest hands the (empty) agent_key option on under a name of its own (MNEMA_PLUGIN_AGENT_KEY) and the launcher drops it when empty; that a filled option wins over the environment’s key is held by a test of the launcher, since the contract cannot fill an option. Measured with sentinel values in a sandbox home a test what-the-host-hands-the-server.test.ts
What Claude Code 2.1.296 writes on the stdin of the command hook that runs after a write, by field name: cwd, duration_ms, hook_event_name, permission_mode, prompt_id, session_id, tool_input (content, file_path), tool_name, tool_response, tool_use_id, transcript_path. The handler reads hook_event_name, tool_name, tool_input and transcript_path, and all four are there. Its gate before an edit is a call into the server, with no stdin a test what-the-host-hands-the-hook.test.ts
What Codex 0.161.0 writes on the stdin of the hook before a patch: cwd, hook_event_name, model, permission_mode, session_id, tool_input (command, the patch), tool_name (apply_patch), tool_use_id, transcript_path, turn_id. The gate reads tool_name and tool_input.command, and both are there a test what-the-host-hands-the-hook.codex.test.ts
What Copilot CLI 1.0.94 writes on the stdin of the hook before a write: cwd, hook_event_name, session_id, timestamp, tool_input, tool_name; for Write, tool_input holds path and file_text, and for apply_patch (named Edit) it is the patch as a string. The gate reads tool_name and path or the patch, and they are there a test what-the-host-hands-the-hook.copilot.test.ts
What Gemini CLI 0.63.0 writes on the stdin of the hook before a write: cwd, hook_event_name (BeforeTool), session_id, timestamp, tool_input, tool_name, transcript_path; tool_input holds file_path for write_file and replace. The gate reads tool_name and file_path, and they are there a test what-the-host-hands-the-hook.gemini.test.ts
What VS Code 1.139.0’s agent writes on the stdin of the hook before create_file: cwd, hook_event_name, session_id, timestamp, tool_input (filePath, content), tool_name, tool_use_id, transcript_path. The gate reads tool_name and filePath, and they are there a test what-the-host-hands-the-hook.vscode.test.ts
What each host sends of the plugin’s 27 tool definitions, read off the request it sends the stand-in model, counted over each entry as the host sent it, re-serialized as JSON (o200k_base is a relative measure, not what a model is billed); the figures are those of the binary the job installs. Claude Code 2.1.296 with tool search off (ENABLE_TOOL_SEARCH=false; it is also what 2.1.296 does by default) sends all 27 whole in the first request that lists tools: 50,876 bytes, 11,610 tokens; the same bytes and tokens in 2.1.304 and 2.1.310 with tool search off. This is the -p run the contract starts; an interactive session is not measured a test the-tool-definitions-the-host-sends.test.ts
Claude Code defers the 27 behind a search when tool search is on (ENABLE_TOOL_SEARCH=true on 2.1.296; what 2.1.304 and 2.1.310 do by default, and the case holds the default of the version the run was started for), in the -p run the contract starts. The first request carries none of the 27 definitions: it lists their 27 names in a system reminder (1,044 bytes, 387 tokens) and offers one ToolSearch tool (1,450 bytes, 332 tokens, the host’s, not the plugin’s). A search loads the definitions into the tools of the next request, each as sent whole with a defer_loading flag: record_decision alone 2,933 bytes, 644 tokens; the five a query for record a decision returned 11,285 bytes, 2,539 tokens; all 27, 51,443 bytes, 11,794 tokens. The search’s own answer is a list of tool_reference blocks (151 bytes, 46 tokens for one tool; 2,195 bytes, 627 tokens for 27). The figures are equal on 2.1.296 with the variable, and on 2.1.304 and 2.1.310 without it; the model’s own queries and an interactive session are not measured a test the-tool-definitions-the-host-defers.test.ts
Codex 0.161.0 sends none of the 27 in the first request that lists tools, in the non-interactive run the contract starts (codex exec), and sends one tool_search entry that lists the server: 2,866 bytes, 678 tokens (o200k, a relative measure). A search returns the server as one namespace, with the server’s instructions once and eight definitions (the limit of the search, whatever the query matched): 16,851 bytes, 3,845 tokens for record a decision; 14,543 bytes, 3,387 tokens for read what the last session left. A different query returns other tools, and what a model would ask is not measured; interactive Codex is not measured a test the-tool-definitions-the-host-sends.codex.test.ts
GitHub Copilot CLI sends all 27 whole in the first request that lists tools, in the non-interactive run the contract starts (copilot -p): 51,540 bytes, 11,942 tokens on 1.0.104 (51,010 and 11,762 on 1.0.94). From 1.0.103 each entry carries a defer_loading flag and the host offers a tool_search_tool beside them; the definitions are still in the request, and what the host does with the flag is not measured a test the-tool-definitions-the-host-sends.copilot.test.ts
Gemini CLI sends all 27 whole in the first request that lists tools, in the non-interactive run the contract starts (gemini -p): 57,702 bytes, 12,941 tokens a test the-tool-definitions-the-host-sends.gemini.test.ts
OpenCode sends all 27 whole in the first request that lists tools (its first request is a title preflight with none), in the non-interactive run the contract starts (opencode run): 51,929 bytes, 11,724 tokens a test the-tool-definitions-the-host-sends.opencode.test.ts
The three verifiers of a record (the product on each Node the CI runs, the Python second reader and the page’s verifier) give the verdict their publishers give on every Ed25519 vector of Wycheproof, CCTV, ed25519-speccheck and RFC 8032 §7.1, under the strict rule of FORMAT.md §6 a test every-verifier-gives-one-ed25519-verdict.test.ts
Both readers of the format spell a number as RFC 8785’s Appendix B does, and as the first 10,000 lines of cyberphone’s numgen.js do, run unmodified and matched to the SHA-256 its author published a test outside-vectors.test.ts
Both readers refuse every JSONTestSuite n_ file as a stored line, and decide every i_ file alike; every file whose bytes are not UTF-8 both refuse for that, naming the same byte a test outside-vectors.test.ts
Each file of a record the readers decode (a segment line, a checkpoint, the tail proof, a committed key) is refused by the product and by the second reader (naming the same byte) when one byte in it is not UTF-8, and a torn final fragment (cut on any byte) is dropped by both a test every-reader-refuses-bytes-that-are-not-utf8.test.ts
A checkpoint or an event that names another key than the one its tail id begins with, even a key enrolled for the same person and genuinely signing, is refused by the product, the second reader and the page as tail-signer-mismatch, an event above the last checkpoint included; a second key’s own tail, a checker’s and an agent key’s events, and a project restored from its backup key still verify a test a-tail-is-written-by-the-key-its-id-names.test.ts, second-reader-agrees-on-the-record.test.ts, the-verdict-in-the-browser.test.ts, second-reader-agrees-on-enrolment.test.ts, key-restore.test.ts
A key no writer puts there, beside event and link, in a line’s link, on a checkpoint line or in the tail proof, with the line left canonical and every hash and signature closing, is refused by the product, the second reader and the page, in the same words; a stored block header with a key beside its two is not read a test both-readers-read-the-same-bytes.test.ts, second-reader-agrees-on-the-record.test.ts, the-verdict-in-the-browser.test.ts
A line broken where the order of the checks decides what is named first (an event that is no object beside a fifth key in its link, a link that is a string, a checkpoint or a tail proof under a scheme no version names) is named for the same check by the product, the second reader and the page, in the order FORMAT.md §4 writes down; an undeclared payload field is held to the same check though each reader words it its own way a test both-readers-read-the-same-bytes.test.ts, second-reader-agrees-on-the-record.test.ts, the-verdict-in-the-browser.test.ts
The vectors above are the files their publishers published: each is copied with its license and commit, and its SHA-256 is checked a test outside-vectors.test.ts
An agent is never its own authority: a which equal to the who, even short or padded, is refused before anything is written a test authority.test.ts (the refusal) · operations.test.ts (nothing written, not even the founding)
The step the Action’s page publishes for an agent in CI, run as written on a clone with an empty home, installs the key the record enrols as the agent and nothing else; a server given only the agent’s name then writes facts that both readers read as attested, founds no identity, and is refused a write to a tree the runner cannot enrol it in. A host passing the job’s environment to the server it starts is not measured a test the-agent-ci-step-installs-the-key-the-agent-signs-with.test.ts
The name of the agent, which is stamped on every event, is screened for secrets like any other free text a test every-door.test.ts
The Action fails a pull request whose record was cut back to an earlier state that is honest in every byte, by holding it to the base with verify --since a test run.test.ts
The Action fails a pull request that supersedes the rule asking for a person on a file and edits that file, as it fails the edit alone, by judging the files with the rules of the base and not of the head a test world.test.ts, judge.test.ts

Results of the runs that gave the plugin its reason to exist. Nothing in this tree reruns them, so they are stated as what they were: the day, the model, the number of runs.

  • The agent did not ask. On 18 August 2026, in a round of 116 cells with Claude Haiku 4.5, the arm that had the mnema server available called none of its tools: mcp_asked was false in 20 of 20 instrumented cells, and the arm scored what the arm with no record scored. A second round on 20 August, with the same model, had it false in 40 of 40.

  • Handing the decision over moved the agent. On 21 August 2026, in 160 cells with Claude Haiku 4.5 (six tasks where the right move depends on a decision the code does not reveal, four runs of each in every arm, counting two negative controls and two development tasks that ran beside them), the rate at which the agent followed the team’s decision, over the six tasks, was:

    arm what the agent had followed the team’s decision
    base no record, no memory, no decision file 33.3%
    host the decision in the host’s own automatic memory 100.0%
    mnema-doc the decision in mnema’s record, handed over as the session opened 100.0%
    mnema+ the same, and the rules for a file handed over at each edit 100.0%

    Handing the decision over moved the agent from 33.3% to 100.0%, and the host’s own memory moved it just as far, so the difference mnema makes is not a higher score. The rules at each edit added nothing measurable: in every cell they landed beside the result of the task’s only write. What mnema changes is where the decision lives: in the repository, shared by the team, in the diff of the pull request, superseded rather than overwritten, and checkable by anyone. And what was measured is conformance to a recorded decision, not whether the decision was right. In that round mcp_asked was true in 2 of 80 cells that had a server to ask, both on one task that already conformed.

  • Where an agent puts a rule it does not have. On 27 August 2026, in 16 cells of one arm, with no decision available, the agent in 2 of the 16 wrote nothing and ended its turn asking the person for the rule. That capture measured no arm, and it does not say why.

  • In the field, the agent did not reach for the record. From 10 to 22 September 2026, in one real project and eight sessions of Claude Code (31 transcripts, its subagents’ included), 6 of 9,448 tool calls went to this server: three record_observation, two resume and one search. The three that wrote came after a person typed that the server should be used, and every tool search the agent made was a select: of a name it had already decided on, never a search by what a tool does. In the same eight sessions the host’s own memory, whose writes come back at the next session on their own, was written 23 times with nobody asking, and no later session was handed a word of the three notes. It is one project, so it says what happened there and not how often it happens.

The host’s own behaviour. Each was read once against a real host, and each expires with the version it was read on. No file of this repository reruns it, so each page that says it says the host and the version beside it.

Cursor’s command-line agent needs an account, and an account is not a thing a free runner has, so what is read of it is read by hand, from a script kept beside the plugin and a file that says the day, the version and the checksum of what ran. How a capture is made. A capture is a dated reading and is never presented here as a test.

Cursor’s editor needs an account too, and no capture of it has been made: the table above says of it only what Cursor’s documentation says, and the script for reading it is what a later capture follows.

The claim Read on Held by
In an interactive Claude Code session, a person is shown the rule that asked and decides, and the write waits for that decision Claude Code 2.1.228, 19 August 2026 not held yet
VS Code’s agent ignores the matcher of a hook: the plugin’s command ran before create_file whatever the matcher named VS Code 1.137 with Copilot Chat 0.65, 30 September 2026, no model and no network not held yet
Cursor’s command-line agent runs the hook and ignores the pause; of its tools, only Write was read Cursor agent 2026.09.18, 30 September 2026 not held yet
Cursor’s command-line agent refuses the write on deny and hands the reason to the model; of its tools, only Write was read Cursor agent 2026.09.18, 2 October 2026 not held yet
The rules hook costs about 1 ms at the median per edit in Claude Code, where it is a call on a server already running (the server’s own start, 270 to 350 ms, is paid once per session), and about 200 ms at the median per edit in Cursor and VS Code, which start a process per edit, where that process starts mnema; where no tree holds a rule with an address the process no longer starts mnema, and it cost about 50 ms at the median one machine, version 0.1.1, 21 October 2026, a sandbox, 50 runs each, without the real host; the 50 ms in a second measurement on the same day, on a machine under load not held yet
In Cursor, the server is a namespace named mnema and its tools keep their own names Cursor, 2026-09-23 session not held yet
The server’s own instructions reach the model in the VS Code model families whose prompt carries them, and do not reach it in the Codex families, where the two opening texts are what arrives VS Code 1.137 with Copilot Chat 0.65, 23 September 2026, offline with a test model not held yet
In Cursor’s command-line agent the server’s tool names arrive up front and a tool’s own description arrives only when the model looks that tool up Cursor agent 2026.09.18, free plan, Auto model, 23 September 2026 not held yet
On Cursor’s free plan with the Auto model, the prompt carries the server’s instructions whole, but for the indentation of their continuation lines, and both opening texts, and the model called the server’s tools Cursor agent 2026.09.18, free plan, Auto model, sessions of 23 September 2026 not held yet
VS Code’s agent and Cursor’s command-line agent do not run a hook of type mcp_tool, and run command hooks VS Code 1.137 with Copilot Chat 0.65 and Cursor agent 2026.09.18, 30 September 2026 not held yet
How Claude Code hands the userConfig value to the hooks and the server was read from its documentation, not run; whether VS Code and Cursor offer the option not read not held yet
Claude Code and Cursor’s command-line agent apply a hook’s matcher: a matcher of VS Code’s tool names never ran the plugin’s VS Code hook in either Claude Code 2.1.281 and Cursor agent 2026.09.18, 30 September 2026 not held yet
Claude Code calls a hook’s MCP server only under the name plugin:<plugin>:<server>; the manifest’s own name and three other spellings were not called Claude Code 2.1.228 not held yet
How VS Code spells the server’s name to the model not read not held yet
That the agent_key option reaches the server in VS Code (no handshake of the plugin’s server is seen in the contract) and in Cursor (its agent needs an account, so no contract exists) not measured not held yet
What Codex, Copilot CLI and Gemini CLI write on the stdin of the hook after a question tool ran (the payload the settled-choice hook reads: hook_event_name and tool_name): no case here makes a question tool run without a person not measured not held yet
What any host writes on the stdin of the hook that opens a session. The handlers read from it: session_id (the Claude Code handlers session-recall.mjs and session-compacted.mjs) and cwd (the open mode of the Gemini handler; a payload that is not JSON makes it answer nothing); session-start.mjs reads none not measured not held yet
What an agent does with a decision the record holds as superseded, against the same rule written in a CLAUDE.md: no run compared them, and no result is published not run not held yet
VS Code puts a hook’s reason in front of the person who decides; what the person sees in the confirmation not read from the screen not held yet
VS Code matches applyTo with a leading **/; Cursor matches globs on its servers; Claude Code’s paths and whether its ** reaches a name starting with a dot not read not held yet
Cursor’s agent loads the plugin from the Claude Code installation on the same machine Cursor agent 2026.09.18 not held yet

mnema 0.1.4 · commit cebc4e8da68c