Offer mem0 as a place for shared memory to live - #97
Merged
Merged
Conversation
Shared memory gains a second provider beside `files`: approved notes are stored in a mem0 project through the Platform API (v3). Everything a colony can see or do is unchanged, which is the point of how it is built rather than a limitation of it. Review stays on the Mothership. Proposals queue locally as before, and mem0 only receives a note once a human approves it. Every note is written with `infer: false` and `immutable: true`: with inference on, mem0's extraction model rewrites what it stores and later consolidates it with other memories, so a colony could end up reading text no human reviewed — the one thing review exists to prevent. `add` also refuses to call it a success when mem0 answers without stored results, since that is what an inferred write looks like. Colonies never reach mem0 and never see the key. At boot the Mothership lists the colony's three scopes and writes them into its read-only session directory in exactly the layout `files` mounts, so `memory_search`, `memory_propose` and the prompt pointing at MEMORY.md work without a line changing in the guest. The index is ordered by mem0's relevance to the task — the issue title, instructions and body, not the full prompt, which is mostly boilerplate shared by every colony and would pull every ranking toward the same notes. A scope is a mem0 `user_id` (`colonizer:repo:<owner>/<repo>` and so on) and every memory carries `app_id: colonizer`. Listing filters on both, and delete first fetches the memory and checks both, because a mem0 project may hold other tools' memories and an id from the web UI proves neither. The key is stored like provider keys (config/memory-keys/mem0, 0600), falls back to MEM0_API_KEY, and is never written to modules.json nor returned by the API. Settings gets its own key row with Save, Remove and Check. Failure never costs a colony or a proposal. mem0 down at boot: the colony starts with an empty layout and a warning. An approval that cannot reach mem0 returns 502 and puts the proposal back. With review off, a note that cannot be stored is queued for review instead of dropped. Verified end to end on a separate instance against a local stand-in for the v3 API (no mem0 key was available): key saved through the UI and checked, three notes stored with infer false and immutable true, a real colony booted with "shared memory: 3 notes from mem0, most relevant to this task first", its agent read both indexes and found the mem0 note with memory_search, its proposal came back through review, an approval with mem0 stopped returned 502 and kept the proposal, and the same approval with mem0 back stored it with its provenance. 107 tests pass, 8 of them new; clippy's warning set is identical to main's. Self-hosted mem0 serves a different API and is out of scope, as is semantic search from inside a colony. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 18, 2026
ParallelEntrepreneur
added a commit
that referenced
this pull request
Sep 23, 2026
Shared memory gains a second provider beside `files`: approved notes are stored in a mem0 project through the Platform API (v3). Everything a colony can see or do is unchanged, which is the point of how it is built rather than a limitation of it. Review stays on the Mothership. Proposals queue locally as before, and mem0 only receives a note once a human approves it. Every note is written with `infer: false` and `immutable: true`: with inference on, mem0's extraction model rewrites what it stores and later consolidates it with other memories, so a colony could end up reading text no human reviewed — the one thing review exists to prevent. `add` also refuses to call it a success when mem0 answers without stored results, since that is what an inferred write looks like. Colonies never reach mem0 and never see the key. At boot the Mothership lists the colony's three scopes and writes them into its read-only session directory in exactly the layout `files` mounts, so `memory_search`, `memory_propose` and the prompt pointing at MEMORY.md work without a line changing in the guest. The index is ordered by mem0's relevance to the task — the issue title, instructions and body, not the full prompt, which is mostly boilerplate shared by every colony and would pull every ranking toward the same notes. A scope is a mem0 `user_id` (`colonizer:repo:<owner>/<repo>` and so on) and every memory carries `app_id: colonizer`. Listing filters on both, and delete first fetches the memory and checks both, because a mem0 project may hold other tools' memories and an id from the web UI proves neither. The key is stored like provider keys (config/memory-keys/mem0, 0600), falls back to MEM0_API_KEY, and is never written to modules.json nor returned by the API. Settings gets its own key row with Save, Remove and Check. Failure never costs a colony or a proposal. mem0 down at boot: the colony starts with an empty layout and a warning. An approval that cannot reach mem0 returns 502 and puts the proposal back. With review off, a note that cannot be stored is queued for review instead of dropped. Verified end to end on a separate instance against a local stand-in for the v3 API (no mem0 key was available): key saved through the UI and checked, three notes stored with infer false and immutable true, a real colony booted with "shared memory: 3 notes from mem0, most relevant to this task first", its agent read both indexes and found the mem0 note with memory_search, its proposal came back through review, an approval with mem0 stopped returned 502 and kept the proposal, and the same approval with mem0 back stored it with its provenance. 107 tests pass, 8 of them new; clippy's warning set is identical to main's. Self-hosted mem0 serves a different API and is out of scope, as is semantic search from inside a colony.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Shared memory gets a second provider beside
files. Under Settings → Modules → Memory → Provider,mem0keeps approved notes in your mem0 project through its Platform API (v3), and each colony'sMEMORY.mdlists the notes most relevant to its task first.Nothing a colony can see or do changes, and that's deliberate.
infer: falseandimmutable: true. With inference on, mem0's extraction model rewrites what it stores and later merges it with other memories, so a colony could read text nobody reviewed.addalso treats a response without stored results as an error, because that's what an inferred write looks like.filesmounts.memory_search,memory_proposeand the prompt work with no change inside the guest.user_id(colonizer:repo:<owner>/<repo>and so on), and every memory carriesapp_id: colonizer. Listing filters on both. Delete fetches the memory first and checks both, because a mem0 project can be shared.config/memory-keys/mem0(0600) withMEM0_API_KEYas a fallback, and is never written tomodules.jsonor returned by the API. It gets its own row in Settings with Save, Remove and Check.Failure never costs a colony or a proposal
502with the reason, and the proposal stays in the queueOne design choice worth a look
The relevance query is the task (issue title, instructions, issue body), not the prompt. The prompt was 2,225 characters in testing, and most of it is harness boilerplate shared by every colony. Sending it would pull every ranking toward the same notes, and a long preamble could push the task past the query limit. The query in the same test was 85 characters.
Test plan
cargo test: 107 pass, 8 new. They cover the verbatim and immutable write, scope and app isolation on list and delete, refusing unsafe ids, a rejected key never appearing in an error, the colony layout ordered by relevance and rewritten on resume, and the task query.main.tsc --noEmitandvite buildare clean.modules.jsonor in either API response, and the file is 0600 inside a 0700 directory.user_id,app_id=colonizer,infer=falseandimmutable=true.shared memory: 3 notes from mem0, most relevant to this task first. Notes for another repo were left out.memory_search, and proposed a note that came back through review.502and kept the proposal. Approving with mem0 back stored it with the colony and repo it came from.Out of scope