Skip to content

How it works

memprove never asks a model whether a memory still holds. A fact cites test runs; each run sealed the repository state it ran in; on every read the daemon probes the repository again and a fixed rule gives the fact its verdict. Here is that happening in a real repository, then how it is built.

tinyini is a small INI parser in Python, kept in this site’s repository so that every output here is real. demo/record.sh builds it commit by commit and runs the commands below with memprove 0.1.0 and pytest. Nothing in the output is edited, except that the home directory is written as ~.

Session 1 commits the change for the Task parser: handle empty input, runs the tests through memprove run (they pass as r1) and records four facts citing that run. The repository is still in the state r1 ran in, so every fact reads verified:

$ memprove resume --text
Task「parser: handle empty input」 (in_progress): verified (ledger sequence 7, current HEAD 5ebf8ad58118f1d7bf8cf479951c3b36932f9676)
Sessions: 1 in all, 0 ended normally, 0 ended abandoned, 1 not ended
Session 1 01a1dc2c-4168-7511-b3a3-220085a788c5 (the Session this directory's context file records) not ended (started 2026-11-14T22:27:30.280957974Z, last wrote to the ledger at 2026-11-14T22:27:30.825220406Z)
#5 open item: 「an unterminated section header such as [core is untested」 (no evidence cited; from Session 1)
#1 verified: 「parse() returns an empty document for empty or whitespace-only input」 (evidence bound to commit 5ebf8ad58118f1d7bf8cf479951c3b36932f9676, current HEAD 5ebf8ad58118f1d7bf8cf479951c3b36932f9676; verdict: verified; from Session 1)
#2 verified: 「input of only comments is an empty document」 (evidence bound to commit 5ebf8ad58118f1d7bf8cf479951c3b36932f9676, current HEAD 5ebf8ad58118f1d7bf8cf479951c3b36932f9676; verdict: verified; from Session 1)
#3 verified: 「keys before the first section header go into the default section」 (evidence bound to commit 5ebf8ad58118f1d7bf8cf479951c3b36932f9676, current HEAD 5ebf8ad58118f1d7bf8cf479951c3b36932f9676; verdict: verified; from Session 1)
#4 verified: 「a duplicate key keeps the last value」 (evidence bound to commit 5ebf8ad58118f1d7bf8cf479951c3b36932f9676, current HEAD 5ebf8ad58118f1d7bf8cf479951c3b36932f9676; verdict: verified; from Session 1)
#6 next action: 「write the unterminated-header test and run pytest」 (no evidence cited; from Session 1)

Session 1 ends. Someone then commits 558cb252ae87 (“parser: strip inline comments from values”) without running anything through memprove. The facts were not edited, but the state they were bound to is gone: every one reads stale, with the reasons, the files that changed, and the command that would confirm them again:

$ memprove resume --text
Task「parser: handle empty input」 (in_progress): stale (ledger sequence 8, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e) (reasons: head_changed, index_changed, evidence_stale)
Sessions: 1 in all, 1 ended normally, 0 ended abandoned, 0 not ended
#5 open item: 「an unterminated section header such as [core is untested」 (no evidence cited; from Session 1)
#1 fact stale: 「parse() returns an empty document for empty or whitespace-only input」 (evidence bound to commit 5ebf8ad58118f1d7bf8cf479951c3b36932f9676, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; changed files since 5ebf8ad58118f1d7bf8cf479951c3b36932f9676: 「tests/test_comments.py」, 「tinyini/__init__.py」; verdict: stale; from Session 1) (reasons: head_changed, index_changed, evidence_stale)
#2 fact stale: 「input of only comments is an empty document」 (evidence bound to commit 5ebf8ad58118f1d7bf8cf479951c3b36932f9676, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; changed files since 5ebf8ad58118f1d7bf8cf479951c3b36932f9676: same as #1; verdict: stale; from Session 1) (reasons: head_changed, index_changed, evidence_stale)
#3 fact stale: 「keys before the first section header go into the default section」 (evidence bound to commit 5ebf8ad58118f1d7bf8cf479951c3b36932f9676, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; changed files since 5ebf8ad58118f1d7bf8cf479951c3b36932f9676: same as #1; verdict: stale; from Session 1) (reasons: head_changed, index_changed, evidence_stale)
#4 fact stale: 「a duplicate key keeps the last value」 (evidence bound to commit 5ebf8ad58118f1d7bf8cf479951c3b36932f9676, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; changed files since 5ebf8ad58118f1d7bf8cf479951c3b36932f9676: same as #1; verdict: stale; from Session 1) (reasons: head_changed, index_changed, evidence_stale)
Rerun: 「python3 -m pytest -q」 on the current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e (for #1, #2, #3, #4)
#6 next action: 「write the unterminated-header test and run pytest」 (no evidence cited; from Session 1)

Session 2 reads that, runs python3 -m pytest -q through memprove run (it passes as r1 of this Session) and reaffirms the stale facts on it with checkpoint create --reaffirm-all. The old statements are kept as superseded; the new ones are bound to 558cb252ae87:

$ memprove resume --text
Task「parser: handle empty input」 (in_progress): verified (ledger sequence 17, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e)
Sessions: 2 in all, 1 ended normally, 0 ended abandoned, 1 not ended
Session 2 01a1dc2c-452f-7891-b208-8570b22d9fea (the Session this directory's context file records) not ended (started 2026-11-14T22:27:31.247079849Z, last wrote to the ledger at 2026-11-14T22:27:31.711334172Z)
#5 open item: 「an unterminated section header such as [core is untested」 (no evidence cited; from Session 1)
#7 verified: 「parse() returns an empty document for empty or whitespace-only input」 (evidence bound to commit 558cb252ae874fa54bdc721048ab8d042610a88e, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; verdict: verified; from Session 2)
#8 verified: 「input of only comments is an empty document」 (evidence bound to commit 558cb252ae874fa54bdc721048ab8d042610a88e, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; verdict: verified; from Session 2)
#9 verified: 「keys before the first section header go into the default section」 (evidence bound to commit 558cb252ae874fa54bdc721048ab8d042610a88e, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; verdict: verified; from Session 2)
#10 verified: 「a duplicate key keeps the last value」 (evidence bound to commit 558cb252ae874fa54bdc721048ab8d042610a88e, current HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; verdict: verified; from Session 2)
#11 next action: 「write the unterminated-header test and run pytest」 (no evidence cited; from Session 2)
superseded 5: #1–#4, #6; withdrawn 0 (not listed one by one; resume --text --all lists them all)

When Session 2 started, Claude Code’s SessionStart hook put this in front of the agent before its first message: one opening statement, then the Context Pack in a fence. The same ledger, repository state and budget always give the same bytes, and the last line is the SHA-256 of the lines above it.

$ memprove hook session-start # run by Claude Code at the start of a session
memprove: below is the Context Pack of Task 01a1dc2c-4162-74e0-a5da-be1779c133a7 at SessionStart (startup), chosen from this directory's context. The current UTC time is 2026-11-14T22:27:31Z. Under the unattended takeover rule, this session records each Session it leaves open as an open item 「another Session (Session k) may still be working, not ended」 in its first checkpoint.
```text
Pack: memprove-pack v1; capsule_digest sha256:2440ba5f63482c175ae6825d8390ab85a93cc0edc88381bce7b682b6ca6b00f0; ledger sequence 8; probed HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; budget 9000 UTF-16 code units
Ontology: continuity.work-state-ontology.v4 (the version of the verdict rules)
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)
Integrity: no inconsistency found (the event chain is valid, and every record of this Task recomputes and agrees with the ledger)
Sessions: 1 in all, 1 ended normally, 0 ended abandoned, 0 not ended, 0 declared a resumed_from Pack
Session 1 01a1dc2c-4168-7511-b3a3-220085a788c5 ended normally (started 2026-11-14T22:27:30.280957974Z)
Cannot: complete the Task (transitionTask; reasons: evidence_stale; needs mcp-write)
Can: begin a new Session (createSession; needs mcp-write)
Can: record a fact citing the latest run (createCheckpoint; needs mcp-write)
Base: 5ebf8ad58118f1d7bf8cf479951c3b36932f9676; files changed since: 「tests/test_comments.py」, 「tinyini/__init__.py」
#5 open item: 「an unterminated section header such as [core is untested」 (no evidence cited; from Session 1)
#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)
#2 fact stale: 「input of only comments is an empty document」 (reasons: head_changed, index_changed, evidence_stale; evidence bound to commit 5ebf8ad58118; from Session 1)
#3 fact stale: 「keys before the first section header go into the default section」 (reasons: head_changed, index_changed, evidence_stale; evidence bound to commit 5ebf8ad58118; from Session 1)
#4 fact stale: 「a duplicate key keeps the last value」 (reasons: head_changed, index_changed, evidence_stale; evidence bound to commit 5ebf8ad58118; from Session 1)
Rerun: 「python3 -m pytest -q」 on the current HEAD (for #1, #2, #3, #4)
#6 next action: 「write the unterminated-header test and run pytest」 (no evidence cited; from Session 1)
Omitted: level 0; bases with files only counted 0, bodies shortened 0, bodies omitted 0, folded into one line 0, not listed 0; full text of given numbers: memprove resume --pack --claims <numbers> (such as 3,5-7)
Digest: sha256:210efbefabe5507a82157336d13976f119a58705799a9ff65205239a1bffe932
```
Both Sessions, command by command (standard output and standard error)
$ memprove init
memprove init: ~/tinyini
configuration directory: ~/.memprove/config (from HOME/.memprove/config)
clients: claude-code
Project 01a1dc2c-40dd-7160-b318-7542e520ca6e registered for ~/tinyini
~/.memprove/config/contexts/44d77834010dab48a216a9deaf071bf532ac599b796b1a642592935beffdfcb8.pack.json written (the Project for the first `memprove begin` here)
~/tinyini/.git/info/exclude written
~/tinyini/.claude/settings.local.json written
~/tinyini/.mcp.json written
note: permissions.allow was left as it is; the rules an unattended `claude -p` needs are "mcp__memprove", "Bash(memprove run:*)", and "Bash(~/.memprove/bin/memprove run:*)"; `memprove init --allow-tools` adds them
doctor:
PASS data-dir: ~/.memprove/data has mode 0700 (named by ~/.memprove/config/data-dir)
PASS projects-dir: ~/.memprove/data/projects and its 1 Project directory, and ~/.memprove/data/tmp, have mode 0700 or stricter
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 token: memproved accepts the token, and it reaches the mcp-read and mcp-write operations
PASS build: memprove and memproved are the same build, 0.1.0 (71674684285e)
PASS config-dir: ~/.memprove/config is outside every Git worktree
PASS path: ~/.memprove/bin is on PATH, so `memprove run` runs this executable and the rule Bash(memprove run:*) matches it
PASS guide: the guide at ~/.memprove/share/guide.md was installed with this binary (build 71674684285e)
WARN workspace: the Workspace ~/tinyini has no current context yet: `memprove begin --new-task <TITLE>` there starts the first Session, in Project 01a1dc2c-40dd-7160-b318-7542e520ca6e (the one `memprove init` registered)
PASS hook: ~/tinyini/.claude/settings.local.json runs ~/.memprove/bin/memprove hook session-start, which exists; its --config-dir is ~/.memprove/config, the configuration directory doctor uses (the argument wins over env.MEMPROVE_CONFIG_DIR)
PASS mcp-entry: ~/tinyini/.mcp.json has mcpServers.memprove, which runs ~/.memprove/bin/memprove; its --config-dir is ~/.memprove/config, the configuration directory doctor uses (the argument wins over env.MEMPROVE_CONFIG_DIR)
WARN ledger: not checked: no context names a Project here
memprove doctor: 12 PASS, 2 WARN, 0 FAIL
next: memprove begin --new-task "<TITLE>" in this repository starts the first Session; the agent's token and the daemon are found through ~/.memprove/config
$ memprove begin --new-task "parser: handle empty input"
{"schema_version":1,"workspace_root":"~/tinyini","project_id":"01a1dc2c-40dd-7160-b318-7542e520ca6e","workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2","agent_id":"01a1dc2c-4167-7cba-8d2f-d0c8cb0baebd","task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","task_title":"parser: handle empty input","task_revision":3,"task_state":"in_progress","session_id":"01a1dc2c-4168-7511-b3a3-220085a788c5","session_revision":1,"snapshot_id":null,"evidence_id":null,"evidence_status":null,"runs":[],"next_run":1}
memprove: Session started; current context written to ~/.memprove/config/contexts/44d77834010dab48a216a9deaf071bf532ac599b796b1a642592935beffdfcb8.json
$ memprove run --test-format pytest -- python3 -m pytest -q
........ [100%]
8 passed in 0.01s
memprove: "python3" exited with 0 (2026-11-14T22:27:30.401Z to 2026-11-14T22:27:30.714Z)
memprove: counted 8 test cases
{"artifact_type":"test_run","canonical_digest":"sha256:e0fee9e943549ca237436969976256996399775dcf277310892073ef1a8655ea","collector":{"name":"memprove-cli","version":"0.1.0"},"evidence_id":"01a1dc2c-431c-7bb1-a520-6192267fceb6","id":"01a1dc2c-431c-7bb1-a520-6192267fceb6","kind":"test_result","observed_at":"2026-11-14T22:27:30.714Z","payload":{"argv":["python3","-m","pytest","-q"],"exit_code":0,"finished_at":"2026-11-14T22:27:30.714Z","passed":true,"started_at":"2026-11-14T22:27:30.401Z","stderr":"","stderr_sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","stdout":"........ [100%]\n8 passed in 0.01s\n","stdout_sha256":"71288863ec9567395a647dd0c8fbf0e2048bee72283202612ad6f90a7a526ef3","test_case_count":8,"test_format":"pytest","type":"test_result"},"payload_digest":"sha256:6b145997b630503791ea3548618fbd15d0830d7610aa4b3f2f7e0ed493f735b4","revision":1,"schema_version":1,"snapshot":{"branch":"main","canonical_digest":"sha256:fed966dc463d9e02488430f9d760dbfba2085fa1335c6339ae957201e2e28df0","capture_manifest_digest":"sha256:94a9f494f6446abe28923b896b7f3f1a8fa7d145655f428122673735f09719b5","captured_at":"2026-11-14T22:27:30.716742564Z","command_evidence_ids":[],"git_head_evidence_id":"01a1dc2c-431c-7bb1-a520-61a8bcc2cf48","git_worktree_evidence_id":"01a1dc2c-431c-7bb1-a520-61b4b414e64a","head_oid":"5ebf8ad58118f1d7bf8cf479951c3b36932f9676","is_dirty":false,"snapshot_id":"01a1dc2c-431a-7820-914d-e78543891b7a","task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","test_evidence_ids":["01a1dc2c-431c-7bb1-a520-6192267fceb6"],"workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"},"snapshot_ids":["01a1dc2c-431a-7820-914d-e78543891b7a"],"status":"PASS","subject":{"test_name":"python3 -m pytest -q","type":"test_result"},"task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"}
memprove: runs of this repository state you can cite with --evidence <LABEL>: r1 (PASS)
memprove: recorded PASS, 8 test cases, evidence_id 01a1dc2c-431c-7bb1-a520-6192267fceb6, label r1
$ memprove checkpoint create --fact "parse() returns an empty document for empty or whitespace-only input" --evidence r1 --fact "input of only comments is an empty document" --evidence r1 --fact "keys before the first section header go into the default section" --evidence r1 --fact "a duplicate key keeps the last value" --evidence r1 --open "an unterminated section header such as [core is untested" --next "write the unterminated-header test and run pytest"
{"checkpoint_id":"01a1dc2c-4389-7bb3-b197-50270eef820c","created_at":"2026-11-14T22:27:30.825220406Z","digest":"sha256:9006ef2670ad4e7c4d5418a1c7ffce15e95884ada5d6bdfd74ef4e2968a2faf7","id":"01a1dc2c-4389-7bb3-b197-50270eef820c","next_action":{"classification":"recommendation","evidence_ids":[],"text":"write the unterminated-header test and run pytest"},"objective":"parser: handle empty input","open_items":[{"classification":"unknown","evidence_ids":[],"text":"an unterminated section header such as [core is untested"}],"project_event_sequence":6,"revision":1,"schema_version":1,"session_id":"01a1dc2c-4168-7511-b3a3-220085a788c5","snapshot":{"branch":"main","canonical_digest":"sha256:fed966dc463d9e02488430f9d760dbfba2085fa1335c6339ae957201e2e28df0","capture_manifest_digest":"sha256:94a9f494f6446abe28923b896b7f3f1a8fa7d145655f428122673735f09719b5","captured_at":"2026-11-14T22:27:30.716742564Z","command_evidence_ids":[],"git_head_evidence_id":"01a1dc2c-431c-7bb1-a520-61a8bcc2cf48","git_worktree_evidence_id":"01a1dc2c-431c-7bb1-a520-61b4b414e64a","head_oid":"5ebf8ad58118f1d7bf8cf479951c3b36932f9676","is_dirty":false,"snapshot_id":"01a1dc2c-431a-7820-914d-e78543891b7a","task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","test_evidence_ids":["01a1dc2c-431c-7bb1-a520-6192267fceb6"],"workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"},"snapshot_id":"01a1dc2c-431a-7820-914d-e78543891b7a","task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","verified_completed_work":[{"classification":"verified_fact","evidence_ids":["01a1dc2c-431c-7bb1-a520-6192267fceb6"],"text":"parse() returns an empty document for empty or whitespace-only input"},{"classification":"verified_fact","evidence_ids":["01a1dc2c-431c-7bb1-a520-6192267fceb6"],"text":"input of only comments is an empty document"},{"classification":"verified_fact","evidence_ids":["01a1dc2c-431c-7bb1-a520-6192267fceb6"],"text":"keys before the first section header go into the default section"},{"classification":"verified_fact","evidence_ids":["01a1dc2c-431c-7bb1-a520-6192267fceb6"],"text":"a duplicate key keeps the last value"}],"workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"}
memprove: new statements: #1 fact, #2 fact, #3 fact, #4 fact, #5 open_item, #6 next_action
memprove: superseded: none
memprove: withdrawn: none
memprove: reaffirmed: none
$ memprove resume --pack
Pack: memprove-pack v1; capsule_digest sha256:9b2f9bd45319be25010bbe0ecbb937aaa7902f465f76af15a1f176edd669e43b; ledger sequence 7; probed HEAD 5ebf8ad58118f1d7bf8cf479951c3b36932f9676; budget 9000 UTF-16 code units
Ontology: continuity.work-state-ontology.v4 (the version of the verdict rules)
Task「parser: handle empty input」 (in_progress): verified; active claims 6: facts 4 (verified 4), open items 1, decisions 0, next actions 1; superseded 0; withdrawn 0 (facts 0)
Integrity: no inconsistency found (the event chain is valid, and every record of this Task recomputes and agrees with the ledger)
Sessions: 1 in all, 0 ended normally, 0 ended abandoned, 1 not ended, 0 declared a resumed_from Pack
Session 1 01a1dc2c-4168-7511-b3a3-220085a788c5 not ended (started 2026-11-14T22:27:30.280957974Z, last wrote to the ledger at 2026-11-14T22:27:30.825220406Z)
Last passed: 「python3 -m pytest -q」 (5ebf8ad58118; --test --test-format pytest)
Can: complete the Task (transitionTask; cites 1 passed runs of the current repository state; needs mcp-write)
Can: begin a new Session (createSession; first end it as the line of Session 1 says, or confirm it is still working; needs mcp-write)
Can: record a fact citing the latest run (createCheckpoint; needs mcp-write)
Base: 5ebf8ad58118f1d7bf8cf479951c3b36932f9676; files changed since: none
#5 open item: 「an unterminated section header such as [core is untested」 (no evidence cited; from Session 1)
#1 verified: 「parse() returns an empty document for empty or whitespace-only input」 (evidence bound to commit 5ebf8ad58118; from Session 1)
#2 verified: 「input of only comments is an empty document」 (evidence bound to commit 5ebf8ad58118; from Session 1)
#3 verified: 「keys before the first section header go into the default section」 (evidence bound to commit 5ebf8ad58118; from Session 1)
#4 verified: 「a duplicate key keeps the last value」 (evidence bound to commit 5ebf8ad58118; from Session 1)
#6 next action: 「write the unterminated-header test and run pytest」 (no evidence cited; from Session 1)
Omitted: level 0; bases with files only counted 0, bodies shortened 0, bodies omitted 0, folded into one line 0, not listed 0; full text of given numbers: memprove resume --pack --claims <numbers> (such as 3,5-7)
Digest: sha256:372b545739b632561676feda70b67506425a070275722cb3d026b26338b9551b
$ memprove end --reason "empty input handled"
{"agent_id":"01a1dc2c-4167-7cba-8d2f-d0c8cb0baebd","revision":2,"schema_version":1,"session_id":"01a1dc2c-4168-7511-b3a3-220085a788c5","state":"ended","workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"}
memprove: the Session was cleared from the current context
$ git commit -m "parser: strip inline comments from values"
$ memprove hook session-start # run by Claude Code at the start of a session
memprove: below is the Context Pack of Task 01a1dc2c-4162-74e0-a5da-be1779c133a7 at SessionStart (startup), chosen from this directory's context. The current UTC time is 2026-11-14T22:27:31Z. Under the unattended takeover rule, this session records each Session it leaves open as an open item 「another Session (Session k) may still be working, not ended」 in its first checkpoint.
```text
Pack: memprove-pack v1; capsule_digest sha256:2440ba5f63482c175ae6825d8390ab85a93cc0edc88381bce7b682b6ca6b00f0; ledger sequence 8; probed HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; budget 9000 UTF-16 code units
Ontology: continuity.work-state-ontology.v4 (the version of the verdict rules)
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)
Integrity: no inconsistency found (the event chain is valid, and every record of this Task recomputes and agrees with the ledger)
Sessions: 1 in all, 1 ended normally, 0 ended abandoned, 0 not ended, 0 declared a resumed_from Pack
Session 1 01a1dc2c-4168-7511-b3a3-220085a788c5 ended normally (started 2026-11-14T22:27:30.280957974Z)
Cannot: complete the Task (transitionTask; reasons: evidence_stale; needs mcp-write)
Can: begin a new Session (createSession; needs mcp-write)
Can: record a fact citing the latest run (createCheckpoint; needs mcp-write)
Base: 5ebf8ad58118f1d7bf8cf479951c3b36932f9676; files changed since: 「tests/test_comments.py」, 「tinyini/__init__.py」
#5 open item: 「an unterminated section header such as [core is untested」 (no evidence cited; from Session 1)
#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)
#2 fact stale: 「input of only comments is an empty document」 (reasons: head_changed, index_changed, evidence_stale; evidence bound to commit 5ebf8ad58118; from Session 1)
#3 fact stale: 「keys before the first section header go into the default section」 (reasons: head_changed, index_changed, evidence_stale; evidence bound to commit 5ebf8ad58118; from Session 1)
#4 fact stale: 「a duplicate key keeps the last value」 (reasons: head_changed, index_changed, evidence_stale; evidence bound to commit 5ebf8ad58118; from Session 1)
Rerun: 「python3 -m pytest -q」 on the current HEAD (for #1, #2, #3, #4)
#6 next action: 「write the unterminated-header test and run pytest」 (no evidence cited; from Session 1)
Omitted: level 0; bases with files only counted 0, bodies shortened 0, bodies omitted 0, folded into one line 0, not listed 0; full text of given numbers: memprove resume --pack --claims <numbers> (such as 3,5-7)
Digest: sha256:210efbefabe5507a82157336d13976f119a58705799a9ff65205239a1bffe932
```
$ memprove begin
memprove: the Session declares that it resumes from the Pack of ledger sequence 8 (pack_digest sha256:210efbefabe5507a82157336d13976f119a58705799a9ff65205239a1bffe932)
{"schema_version":1,"workspace_root":"~/tinyini","project_id":"01a1dc2c-40dd-7160-b318-7542e520ca6e","workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2","agent_id":"01a1dc2c-4167-7cba-8d2f-d0c8cb0baebd","task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","task_title":"parser: handle empty input","task_revision":3,"task_state":"in_progress","session_id":"01a1dc2c-452f-7891-b208-8570b22d9fea","session_revision":1,"snapshot_id":null,"evidence_id":null,"evidence_status":null,"runs":[],"next_run":1}
memprove: Session started; current context written to ~/.memprove/config/contexts/44d77834010dab48a216a9deaf071bf532ac599b796b1a642592935beffdfcb8.json
$ memprove run --test-format pytest -- python3 -m pytest -q
.......... [100%]
10 passed in 0.02s
memprove: "python3" exited with 0 (2026-11-14T22:27:31.256Z to 2026-11-14T22:27:31.549Z)
memprove: counted 10 test cases
{"artifact_type":"test_run","canonical_digest":"sha256:caa9b8692554cb35f40f19f260dbc6d56eafbee4501760a48a6e84b3b197385d","collector":{"name":"memprove-cli","version":"0.1.0"},"evidence_id":"01a1dc2c-465f-7452-ace1-5249a5819346","id":"01a1dc2c-465f-7452-ace1-5249a5819346","kind":"test_result","observed_at":"2026-11-14T22:27:31.549Z","payload":{"argv":["python3","-m","pytest","-q"],"exit_code":0,"finished_at":"2026-11-14T22:27:31.549Z","passed":true,"started_at":"2026-11-14T22:27:31.256Z","stderr":"","stderr_sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","stdout":".......... [100%]\n10 passed in 0.02s\n","stdout_sha256":"4c78d91fecebd9ff2601366b209fedef5a34c74b72f29aadd727b0d4e4957970","test_case_count":10,"test_format":"pytest","type":"test_result"},"payload_digest":"sha256:e69ce33c165aac0b1e4325c616d5dd639d60a5cbca0191cf44e4aced735f6286","revision":1,"schema_version":1,"snapshot":{"branch":"main","canonical_digest":"sha256:178a1549ae50b7d3a8ae06638f18e92773af221d62f2a156fb9a989ed4f3596b","capture_manifest_digest":"sha256:8f9b3d2a9676dfc59cf1b8fe8e50524ca6d36e0c8362a9b3dbe8a48b62e6fd9c","captured_at":"2026-11-14T22:27:31.551942635Z","command_evidence_ids":[],"git_head_evidence_id":"01a1dc2c-465f-7452-ace1-52583276fc63","git_worktree_evidence_id":"01a1dc2c-465f-7452-ace1-526e956906e9","head_oid":"558cb252ae874fa54bdc721048ab8d042610a88e","is_dirty":false,"snapshot_id":"01a1dc2c-465d-7d89-bc7d-4b003921b1c8","task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","test_evidence_ids":["01a1dc2c-465f-7452-ace1-5249a5819346"],"workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"},"snapshot_ids":["01a1dc2c-465d-7d89-bc7d-4b003921b1c8"],"status":"PASS","subject":{"test_name":"python3 -m pytest -q","type":"test_result"},"task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"}
memprove: runs of this repository state you can cite with --evidence <LABEL>: r1 (PASS)
memprove: recorded PASS, 10 test cases, evidence_id 01a1dc2c-465f-7452-ace1-5249a5819346, label r1
$ memprove checkpoint create --reaffirm-all --next "write the unterminated-header test and run pytest"
{"checkpoint_id":"01a1dc2c-46ff-75f2-838a-9ab2fdd33cd0","created_at":"2026-11-14T22:27:31.711334172Z","digest":"sha256:d48ccc98cce6724cfa7f49f93c710f2b4cf67bd8d4bb2dd2ee3df30d999ac586","id":"01a1dc2c-46ff-75f2-838a-9ab2fdd33cd0","next_action":{"classification":"recommendation","evidence_ids":[],"text":"write the unterminated-header test and run pytest"},"objective":"parser: handle empty input","open_items":[],"project_event_sequence":12,"revision":1,"schema_version":1,"session_id":"01a1dc2c-452f-7891-b208-8570b22d9fea","snapshot":{"branch":"main","canonical_digest":"sha256:178a1549ae50b7d3a8ae06638f18e92773af221d62f2a156fb9a989ed4f3596b","capture_manifest_digest":"sha256:8f9b3d2a9676dfc59cf1b8fe8e50524ca6d36e0c8362a9b3dbe8a48b62e6fd9c","captured_at":"2026-11-14T22:27:31.551942635Z","command_evidence_ids":[],"git_head_evidence_id":"01a1dc2c-465f-7452-ace1-52583276fc63","git_worktree_evidence_id":"01a1dc2c-465f-7452-ace1-526e956906e9","head_oid":"558cb252ae874fa54bdc721048ab8d042610a88e","is_dirty":false,"snapshot_id":"01a1dc2c-465d-7d89-bc7d-4b003921b1c8","task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","test_evidence_ids":["01a1dc2c-465f-7452-ace1-5249a5819346"],"workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"},"snapshot_id":"01a1dc2c-465d-7d89-bc7d-4b003921b1c8","task_id":"01a1dc2c-4162-74e0-a5da-be1779c133a7","verified_completed_work":[{"classification":"verified_fact","evidence_ids":["01a1dc2c-465f-7452-ace1-5249a5819346"],"text":"parse() returns an empty document for empty or whitespace-only input"},{"classification":"verified_fact","evidence_ids":["01a1dc2c-465f-7452-ace1-5249a5819346"],"text":"input of only comments is an empty document"},{"classification":"verified_fact","evidence_ids":["01a1dc2c-465f-7452-ace1-5249a5819346"],"text":"keys before the first section header go into the default section"},{"classification":"verified_fact","evidence_ids":["01a1dc2c-465f-7452-ace1-5249a5819346"],"text":"a duplicate key keeps the last value"}],"workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"}
memprove: new statements: #7 fact, #8 fact, #9 fact, #10 fact, #11 next_action
memprove: superseded: #6 by #11
memprove: withdrawn: none
memprove: reaffirmed: #1 as #7, #2 as #8, #3 as #9, #4 as #10
memprove: reaffirm-all reaffirmed: #1 (r1) as #7: verified, #2 (r1) as #8: verified, #3 (r1) as #9: verified, #4 (r1) as #10: verified
memprove: reaffirm-all skipped: none
memprove: reaffirmed 4 (verified 4), skipped 0
$ memprove resume --pack
Pack: memprove-pack v1; capsule_digest sha256:9414b8d8e72952af25d7789995690c6d062a89c9a9ff0037dbb41dbc30783c23; ledger sequence 17; probed HEAD 558cb252ae874fa54bdc721048ab8d042610a88e; budget 9000 UTF-16 code units
Ontology: continuity.work-state-ontology.v4 (the version of the verdict rules)
Task「parser: handle empty input」 (in_progress): verified; active claims 6: facts 4 (verified 4), open items 1, decisions 0, next actions 1; superseded 5; withdrawn 0 (facts 0)
Integrity: no inconsistency found (the event chain is valid, and every record of this Task recomputes and agrees with the ledger)
Sessions: 2 in all, 1 ended normally, 0 ended abandoned, 1 not ended, 1 declared a resumed_from Pack
Session 1 01a1dc2c-4168-7511-b3a3-220085a788c5 ended normally (started 2026-11-14T22:27:30.280957974Z)
Session 2 01a1dc2c-452f-7891-b208-8570b22d9fea not ended (started 2026-11-14T22:27:31.247079849Z, last wrote to the ledger at 2026-11-14T22:27:31.711334172Z); continues from the Pack at ledger sequence 8 (self-reported, digest sha256:210efbefabe5)
Last passed: 「python3 -m pytest -q」 (558cb252ae87; --test --test-format pytest)
Can: complete the Task (transitionTask; cites 1 passed runs of the current repository state; needs mcp-write)
Can: begin a new Session (createSession; first end it as the line of Session 2 says, or confirm it is still working; needs mcp-write)
Can: record a fact citing the latest run (createCheckpoint; needs mcp-write)
Base: 558cb252ae874fa54bdc721048ab8d042610a88e; files changed since: none
#5 open item: 「an unterminated section header such as [core is untested」 (no evidence cited; from Session 1)
#7 verified: 「parse() returns an empty document for empty or whitespace-only input」 (evidence bound to commit 558cb252ae87; from Session 2)
#8 verified: 「input of only comments is an empty document」 (evidence bound to commit 558cb252ae87; from Session 2)
#9 verified: 「keys before the first section header go into the default section」 (evidence bound to commit 558cb252ae87; from Session 2)
#10 verified: 「a duplicate key keeps the last value」 (evidence bound to commit 558cb252ae87; from Session 2)
#11 next action: 「write the unterminated-header test and run pytest」 (no evidence cited; from Session 2)
Superseded and withdrawn: superseded 5: #1–#4, #6; withdrawn 0 (not listed one by one)
Omitted: level 0; bases with files only counted 0, bodies shortened 0, bodies omitted 0, folded into one line 0, not listed 0; full text of given numbers: memprove resume --pack --claims <numbers> (such as 3,5-7)
Digest: sha256:0383a5b2867948164753057d6f578f65fb38fe6cfb722797a64cf7c49d59f17d
$ memprove ledger verify
{"checked":{"checkpoints":2,"events":17,"evidence":6,"snapshots":2,"tasks":1},"first_failure":null,"head_event_sha256":"sha256:1e58ce6d8ca5bf6d850e018529699268a7f608c5293299b639921df7de83b453","project_id":"01a1dc2c-40dd-7160-b318-7542e520ca6e","project_sequence":17,"schema_version":1,"valid":true}
$ memprove end --reason "facts reaffirmed after the inline-comment change"
{"agent_id":"01a1dc2c-4167-7cba-8d2f-d0c8cb0baebd","revision":2,"schema_version":1,"session_id":"01a1dc2c-452f-7891-b208-8570b22d9fea","state":"ended","workspace_id":"01a1dc2c-40dd-7160-b318-755b385c24a2"}
memprove: the Session was cleared from the current context

One page on how memprove is built. The agent’s manual is docs/guide.md.

Claude Code / Codex / Cursor shell
┌──────────────────────────────┐ ┌──────────────────────────┐
│ SessionStart hook MCP tools │ │ memprove begin / run / │
│ (Context Pack) (10) │ │ checkpoint / end / … │
└──────────────┬───────────────┘ └─────────────┬────────────┘
│ HTTP/1.1 over a Unix socket (mode 0600), bearer token
▼
memproved (one process per data directory, file lock)
┌─────────────────────────────────────────────────────┐
│ registry.sqlite: Projects, Workspaces, tokens │
│ projects/<id>/state.sqlite: the Project's ledger │
└─────────────────────────────────────────────────────┘
  • memproved (crates/memprove-daemon) is a foreground process that serves the control API (openapi/control-api-v1.yaml) on <data dir>/memproved.sock. There is no TCP listener and no network access. A token is a 32-byte random secret; the daemon stores only its SHA-256 digest and its scopes (control for the operator, mcp-read and mcp-write for agents).
  • memprove (crates/memprove-cli) is the client: the shell commands an agent runs, the SessionStart hook for Claude Code, the MCP server (memprove mcp serve, stdio), init, doctor, backup and restore. It keeps one small context file per Workspace (and per client session) outside the repository: Project, Workspace, Task, Session.
  • The store (crates/memprove-store-sqlite) owns the SQLite schema and its append-only migrations; the engine and the domain (memprove-engine, memprove-domain) hold the rules; the protocol (memprove-protocol) holds the wire types and their strict JSON.

Every write to a Project is an Event in a hash chain: each Event carries the digest of the one before it, and every record it names (Task, Session, Checkpoint, Snapshot, Evidence) has its own canonical-JSON digest. ledger verify recomputes all of it and reports the first record that does not hold (exit 5), so a change made behind the daemon’s back is found. A Task whose records do not hold is tampered: none of its documents then says verified.

Statements are claims of a Checkpoint: facts, open items, decisions and the one next action in force. A later claim supersedes or withdraws an earlier one; nothing is deleted, and resume --text --all still lists what was replaced. Sessions declare which agent (name, role, parent Session) wrote each claim, and who ended a Session that was left open.

memprove run -- <command> runs the command in the caller’s shell, records its result (and, for a test run, how many cases passed) and seals a Snapshot of the repository state: HEAD, branch, index, tracked-file digests, untracked files. A fact cites such runs. Reading a Task, the daemon probes the repository again and gives every fact one of four verdicts by deterministic rules, never by a model:

verdict meaning
verified the cited test run passed and the repository is still in the state it ran in
stale the state moved on (a commit, an edit, a branch) since the run
partial (unconfirmed) something needed for the judgement is missing: no passed test, untracked files, a failed later run of the same command
tampered a record of the Task does not recompute against the ledger

The reason codes (head_changed, worktree_changed, evidence_not_passed, object_digest_mismatch, …) say why. A Task is completed only on passed runs of the current state; transitionTask to completed refuses otherwise.

resume --pack renders a Task’s Capsule as a fixed set of English lines (Pack:, Task, Integrity:, Sessions:, Session k …, Last passed:, Can:/Cannot:, Base:, the claim lines, Rerun:, Omitted:, Digest:) within a budget of UTF-16 code units (9000 by default), leaving things out in five deterministic levels when it does not fit, and ends with the SHA-256 of itself. The same ledger, probe and budget give the same bytes. A query selects the statements that match a phrase (an in-memory FTS5 index), best first.

Claude Code’s SessionStart hook (memprove hook session-start) prints one opening statement (the Task, the current UTC time, the agent the environment declares, the takeover rule’s open item) and the Pack in a fence, within 8 seconds, and always exits 0; it only reads. The MCP server gives every client ten tools: pack, claims, context, tasks and ledger_verify read, begin, checkpoint, decide, end and complete write. No tool runs a command: tests are recorded with memprove run in a shell and cited by their labels (r1, r2, …), so every piece of Evidence comes from a real run.

A new session reads the Pack first. A Session another agent left open is ended as abandoned only under the unattended takeover rule (its agent is not you, it looks gone for 15 minutes or its dispatcher has ended, and it is not your own parent); otherwise it stays open and an open item records it. The takeover evaluation (docs/takeover-eval.md) runs a real client through six such scenarios and scores the archive deterministically.

From docs/architecture.md at 2625064cbf0a · Markdown

memprove About Roadmap llms.txt GitHub 中文 apache-2.0 · no cookies, no tracking