[ v0.1.0 · open source · rust · apache-2.0 ]

Agent memory that is checked, not trusted.

memprove binds every fact your coding agent records to a commit, the worktree and a passing test run. When the code moves, the fact turns stale — decided by a rule, not a model.

~/tinyini
# macOS (Apple silicon) · Linux x86_64 (glibc 2.35+)
$ curl -fsSL https://memprove.dev/install.sh | sh
# checks the release's SHA-256, installs into ~/.memprove, starts the daemon
❯ memprove init
clients: claude-code
~/tinyini/.mcp.json written
memprove doctor: 12 PASS, 2 WARN, 0 FAIL
# or tell your agent: "read memprove.dev/llms.txt and set it up here"

Works with Claude Code · Codex · Cursor

[ fig. 1 — four facts in tinyini, a real session · replayed ] HEAD 558cb252ae87
  1. parse() returns an empty document for empty or whitespace-only input
    #1 · stale · bound to 5ebf8ad58118
  2. input of only comments is an empty document
    #2 · stale · bound to 5ebf8ad58118
  3. keys before the first section header go into the default section
    #3 · stale · bound to 5ebf8ad58118
  4. a duplicate key keeps the last value
    #4 · stale · bound to 5ebf8ad58118
  1. › memprove run · r1 PASS · 8 test cases at 5ebf8ad58118
  2. › git commit 558cb252ae87 · "parser: strip inline comments from values"
  3. › 4 facts stale · Rerun: 「python3 -m pytest -q」
  4. › memprove run · r1 PASS · 10 test cases · reaffirmed 4 (verified 4), skipped 0

Recorded with memprove 0.1.0 and pytest by demo/record.sh. Nobody edits a fact: a commit moves HEAD, the rule re-judges each one, a passing rerun brings them back. The full output →

[ 01 · the problem ]

Notes don't know when they stop being true.

A line in CLAUDE.md says the parser handles empty input. Three commits later it doesn't, and your agent still believes it. Memory files, auto-memory and hosted memory layers merge and expire text by timestamps and wording. None of them checks whether the code still passes.

[ fig. 2 — CLAUDE.md, three commits later ]no checks
01## Parser 02- The parser handles empty input.▨ true three commits ago · nobody re-checked 03- Run the tests before committing. 04- The public API lives in tinyini/__init__.py.

[ 02 · how it works ]

Three steps. No model in the loop.

01 · run→ 02
memprove run --test-format pytest -- python3 -m pytest -q

Runs the tests in your shell and records the exit code, the case count and the exact repository state: HEAD, the index, the worktree.

02 · record→ 03
memprove checkpoint create --fact "…" --evidence r1

Binds the fact to that run. A fact needs a passed test run; a failure can only be recorded as an open item.

03 · re-judge on every read↺
memprove resume --pack

Each read compares the run's state with the repository now. A fixed rule decides; no model is asked.

[ fig. 3 — the four verdicts ]always shown with their word
■ verified

A passed run of the current state backs the fact.

▨ stale

The repository moved since that run. The Pack names the command to run again.

□ unconfirmed

The evidence can't answer for this state, for example a later run of the same command failed.

▩ tampered

The ledger changed behind the daemon's back. The word "verified" appears nowhere.

[ 03 · no integration project ]

Your agent does the integration.

Enterprise platforms send a solutions architect to wire memory into your stack. memprove ships instructions instead: memprove.dev/llms.txt tells a coding agent how to install it, run memprove init and record a first test run. Setup happens in the session you already work in, and you review what it changed.

  • ■ one prompt, in the agent you already use
  • ■ no account to create, no SDK to call
  • ■ run init again and nothing changes
your agent — ~/your-repo [ fig. 4 · mock-up ]
❯ read memprove.dev/llms.txt and set memprove up in this repo
reading https://memprove.dev/llms.txt
[ OK ] memprove 0.1.0 installed in ~/.memprove
[ OK ] memprove init · clients: claude-code
[ OK ] memprove doctor: 12 PASS, 2 WARN, 0 FAIL
[ OK ] memprove run · pytest r1 PASS
changed: .claude/settings.local.json, .mcp.json

A mock-up: the setup through llms.txt is being recorded in Claude Code, Codex and Cursor. Read llms.txt →

[ 04 · context pack ]

What the next session reads.

In Claude Code, a hook puts a Context Pack at the start of every session: the Task, who worked on it, the last passed run, every active fact with its verdict and commit, the open items and the next action. Same ledger, same repository, same budget: same bytes.

  • — fits a budget, 9,000 UTF-16 units by default; the next action is never cut
  • — agent-written text is quoted, so a statement can't forge a line
  • — the last line is a SHA-256 digest of everything above it
[ fig. 5 — the Pack Session 2 got from the hook · excerpt ]19 lines
⋮ 03 Task「parser: handle empty input」 (in_progress): stale (reasons: head_changed, index_changed, evidence_stale); active claims 6: facts 4 (stale 4), open items 1, decisions 0, next actions 1; superseded 0; withdrawn 0 (facts 0) ① the Task and its verdict ⋮ 05 Sessions: 1 in all, 1 ended normally, 0 ended abandoned, 0 not ended, 0 declared a resumed_from Pack ② who worked on it 06 Session 1 01a1dc2c-4168-7511-b3a3-220085a788c5 ended normally (started 2026-11-14T22:27:30.280957974Z) 07 Cannot: complete the Task (transitionTask; reasons: evidence_stale; needs mcp-write) ⋮ 10 Base: 5ebf8ad58118f1d7bf8cf479951c3b36932f9676; files changed since: 「tests/test_comments.py」, 「tinyini/__init__.py」 ③ what changed since the run ⋮ 12 #1 fact stale: 「parse() returns an empty document for empty or whitespace-only input」 (reasons: head_changed, index_changed, evidence_stale; evidence bound to commit 5ebf8ad58118; from Session 1) ⋮ 16 Rerun: 「python3 -m pytest -q」 on the current HEAD (for #1, #2, #3, #4) ④ what to run again 17 #6 next action: 「write the unterminated-header test and run pytest」 (no evidence cited; from Session 1) ⋮ 19 Digest: sha256:210efbefabe5507a82157336d13976f119a58705799a9ff65205239a1bffe932 ⑤ SHA-256 of the lines above

[ 05 · hand-offs ]

Built for more than one agent.

Sessions end, crash, or get handed to sub-agents. A Pack lists every Session that hasn't ended, memprove records who ended whom, and a sub-agent can't record a Decision, complete the Task or overrule another agent's facts.

How a takeover works →
[ fig. 6 — when a session may end another as abandoned ]all three must hold
A
■ Its agent is not you.
B
■ It looks gone: no write for 15 minutes, or the Session that dispatched it has ended.
C
■ It is not your own parent.
A ∧ B ∧ C → ended as abandoned
otherwise → it stays open, and the first checkpoint records that another Session may still be working

[ 06 · integrations ]

One command per repository.

memprove init finds the clients you use and writes what each needs. Running it again changes nothing.

claude codehook + mcp

A SessionStart hook injects the Pack; the MCP server does what the commands do.

.claude/settings.local.json · .mcp.json

codexmcp + note

An MCP server entry, plus a short note in AGENTS.md.

~/.codex/config.toml · AGENTS.md

cursormcp + rule

An MCP server entry, plus a rule.

.cursor/mcp.json · .cursor/rules/memprove.mdc

[ ten mcp tools ] packclaimscontexttasksledger_verify begincheckpointdecideendcomplete Read tools in grey, write tools in green. No tool runs commands: tests always go through memprove run in your shell.

[ 07 · local by design ]

Nothing leaves your machine.

The daemon, the ledger and the tokens all live on your computer. There is no hosted service to sign up for and nothing to send home.

  • — the daemon talks over a Unix socket and has no network access
  • — every record is hash-chained; memprove ledger verify recomputes the chain
  • — tokens stay in ~/.memprove/config, never in your repository
  • — nothing is deleted: superseded statements stay, each naming its Session
memprove doctor · ledger verify — tinyini
PASS socket: ~/.memprove/data/memproved.sock has mode 0600
PASS daemon: memproved answers and is ready on ~/.memprove/data/memproved.sock
PASS token-file: ~/.memprove/config/token has mode 0600 and holds one token
PASS config-dir: ~/.memprove/config is outside every Git worktree
$ memprove ledger verify
checked 17 events, 2 checkpoints, 6 evidence, 2 snapshots
"valid": true · head sha256:1e58ce6d8ca5…
❯

[ 08 · compare ]

What decides whether a memory is still true?

#approachwho decides
01CLAUDE.md, AGENTS.mdYou, when you remember to edit it
02Claude Code auto-memory, hosted memory layersA model reading wording and timestamps
03stalebrain, true-memory-fragmentsChecks of sentences against paths, symbol hashes or commits
■memproveA passed test run in the repository state you have now

From these projects' own docs and READMEs, November 2026. Tell us if something here is out of date.

[ 09 · status ]

v0.1.0, and honest about its limits.

Changelog →
[ known limits ]v0.1.0
01Stable: the ledger format, the control API and the Pack's line set. 02Binaries for macOS on Apple silicon and Linux x86_64 (glibc 2.35+). Linux on arm64 isn't supported yet; elsewhere, build from source. 03Case counts are read from cargo and pytest output; other runners pass --test-cases. 04Ignored files and environment variables are not part of the repository state. 05No tests, no verified: memprove is for repositories that have them.
memprove — ~/your-repo

Give your agent memory it can prove.