How others turn conversations into long-term memory, where our choices fit, and where to go next.
Mechanisms are illustrated in Memory subsystem: mechanisms illustrated, and the authoritative text is in
docs/reference/design/memory/overview.md. This page compares implementations only.
Layer 1 · our current implementation
Green rows
Behavior verified against running code in openprogram/memory/. Each table includes a green-background row.
Layer 2 · other implementations
Eight frameworks
references/ contains all eight. Missing features remain listed because not implementing a feature is itself a choice.
Layer 3 · our final design
Purple rows
Node markers (sections 04, 05, 09) and resident content generated from topics/core.md (section 07) are implemented.
Authoritative descriptions are in written-marker.md and overview.md, “Resident content.”
Two notes before reading:First, references/claude-code-leaked and
references/codex-cli are internal builds, much larger than their public counterparts; some capabilities below are absent from public versions.
Second, conclusions refer to actual code in these two snapshots, with recorded line numbers; missing implementations are labeled “call site only.”
“After closing this conversation, will the next one remember?” Four of eight do, four do not. The latter delegate this responsibility
to user-written AGENTS.md or to another wrapped process.
Framework
Present
Form
Verified location
claude-code
Yes
Files only: MEMORY.md index, topic files, and dated logs; team edition synchronizes another copy to an HTTP service
Stores only opaque session ids; the wrapped process owns memory
agent/cli_agent.go:25,226-231
OpenProgram
Yes
Markdown workspace: sources/ plus topics/ plus core.md
openprogram/memory/store.py:39
Two easily misread points:First, the internal codex build has fully implemented but default-disabled long-term memory.
Looking only at default behavior wrongly suggests codex has no memory. Second, weclaw’s only persisted knowledge files
(URLs pasted and saved as Markdown) deliberately bypass the agent and are never read back by a prompt.
Classify by whether content returns to context, not whether it is Markdown.
02When writes happenLayer 2
The four writing frameworks trigger differently: one dispatches a background agent each turn; one starts a fire-and-forget task at turn start;
one uses cron; one lets the model decide. We wait until enough content accumulates.
Framework
Timing
Trigger
claude-code
Background agent at turn end; skip if the main agent already wrote memory
Check each turn; write at 16k tokens, force remaining writes on idle, reorganize at 03:00
openprogram/memory/provider.py:30,117
A shared conclusion:Three of four writers—claude-code, codex, and openclaw—write outside the conversation,
for the same reason: the conversational model is working on the task, and memory-file edits add a call and distract from it.
hermes alone makes the main model write, depending on whether it remembers to do so and requiring reminders every ten turns.
We follow the other three.
A useful codex practice:Its writer task acquires a lease from a SQLite task table
carrying retry counts and the last successful watermark, and checks remaining quota before work.
We currently serialize using a cross-process write lock with a one-second timeout, achieving similar exclusion without retry counts;
after two consecutive batch failures, only an event-bus record remains.
03What is writtenLayer 2
Original statements or model-distilled facts. The four retaining both—claude-code, codex, pi-mono, and ours—share one approach:
originals are append-only, distilled content may be rewritten, and originals remain available for checking.
codex distills twice, not once.First, per-session processing compresses a full conversation into structured raw memories and a session summary.
Second, another agent without network or approval prompts uses all first-stage artifacts and a workspace diff
to rewrite MEMORY.md, summary pages, and skill directories. Writing and organizing are separate roles,
like our writing and nightly reorganization, but separated by pipeline stage rather than time.
04How unwritten content is identifiedLayer 2
Eight implementations use five progress representations: none, scalar position, one message identity, in-data markers, and per-node identities.
OpenProgram currently uses the last; no reference here shares that implementation.
Framework
Form
Storage
Verified location
claude-code
Single message UUID, scan backward
In-process closure variable, not persisted, lost on restart
src/services/extractMemories/extractMemories.ts:305-309,340,348 Missing means all new: :103-108
codex-cli
Integer timestamp watermark at session-file granularity
A practice to adopt:If claude-code cannot find the cursor’s message, it treats all messages as unwritten again,
rather than returning zero. The comment states that zero would stop extraction for the rest of the session.
Rewriting has bounded cost; silent stoppage has unbounded cost. Seeding cursors from source archives in our migration makes the same tradeoff:
prefer a repeated batch to losing everything.
Another practice:openclaw deduplication records two mutually checking copies: promotion time on entries,
plus MEMORY.md body HTML comments with keys. If either copy disappears, the other still prevents duplicates.
Our set is recorded only in the cursor file; source archives can seed it, but the two do not cross-check.
05Branches and restartsLayer 2
This matters most for us. Of eight references, only pi-mono, like us, places branches inside the same session.
Others either never branch or create another session file linked to its parent.
Their cursors need not handle branches sharing a prefix inside one session because that structure does not exist.
Framework
Branching supported
Implementation
Verified location
claude-code
Yes, most complete
/branch copies the whole session into a new one; /rewind can rewind conversation, files, or both; subagents write separate files
/branch copies all history into a new session linked by parent id; compaction also creates child sessions, and /resume automatically follows the chain to the latest one
One per WeChat contact; /new deletes the old session
messaging/handler.go:340-345
OpenProgram
Yes, sessions are DAGs
Reasking, retrying, and spawning agents branch from intermediate points; multiple branches under one session id share prefixes
openprogram/store/session/session_store.py:988
The above explains session structure. What determines missing writes is the next question:
what happens to the cursor after a fork. Eight implementations use three approaches; none simply ignores the issue.
Framework
Cursor scope after a fork
Shared-prefix re-extraction
Verified location
codex-cli
New scope: fork produces a new thread_id, the cursor table’s primary key
Yes, entire prefix. Only Phase 2 consolidation merges semantically
weclaw agent/http_agent.go:76-78 (/new clears the session)
OpenProgram before migration
Unchanged: same session id and positional cursor
No repetition, but a fork can misclassify unwritten nodes as written
written-marker.md layer 1
Current OpenProgram
Unchanged scope; inspect workspace markers backward from current branch tip
No; shared-prefix nodes already carry markers, and inspection stops there
written-marker.md layer 3
The conclusion for this comparison:Index and timestamp cursors depend on one line per cursor scope.
They preserve that assumption by changing scope on a fork: codex changes thread_id, openclaw changes session,
claude-code changes process, and hermes manually advances the index at the fork.
Scope changes work because their forks all copies content, giving the new side fresh coordinates.
Our forks do not copy: both branches share a graph and session id, so neither scope switching nor coordinate translation applies.
We therefore record identities rather than ordinal positions.
pi-mono demonstrates another solution: its in-session tree excludes prefixes by stopping at the nearest common ancestor.
That requires a comparison branch for every traversal, whereas memory asks what remains unwritten in the session, with no second branch to compare.
A useful pi-mono practice:When leaving a branch, it can generate a summary and attach it to the tree,
preserving statements after abandonment. This addresses the gap acknowledged in our overview.md:
only the head branch is written, and nobody revisits branches abandoned midway.
An unexpected opencode behavior:revert itself deletes nothing, but the next prompt starts by permanently deleting messages after the marker.
Editing an old message and rerunning is therefore irreversible unless the old branch is forked first.
Our DAG retains both, requiring a way to identify which content has been written.
06RetrievalLayer 2
How stored content returns to context: inject everything, keyword search, vector search, or model selection.
The fourth is unique to claude-code and is the least expensive approach.
The most useful practice to adopt.claude-code builds no index, instead requiring each memory file to start with a self-description.
Retrieval lists these descriptions and uses an inexpensive small-model call to select five filenames.
We currently lack this path, which naturally matches our topics/ structure.
Selection is more precise than “choose if relevant”: omit uncertain matches and prefer empty results.
A useful codex feedback loop:The model embeds a hidden marker identifying memory used in its response.
The server parses it, increments usage counts, then removes it before showing the response.
Counts feed organization-stage ranking: frequently used content stays prominent while unused content is ranked lower,
without manual scoring. Our nightly reorganization currently merges by content only, without this feedback.
07Resident content: trimming when fullLayer 2
Four of eight implementations limit the small memory block included every turn, with six gates in total. There are only two trimming approaches,
and none delegates removal decisions entirely to an unconstrained model: deterministic truncation, or deterministic rejection followed by model edits.
Framework
Resident block
Limit
When full
Decision maker
Disclosure
Verified location
claude-code MEMORY.md
Yes
200 lines + 25000 bytes
Truncate by lines, then by bytes at the last newline
Deterministic
Append a warning naming the gate
src/memdir/memdir.ts:35,38,57-103
claude-code SessionMemory
Yes
2000 tokens per section / 12000 total
Model compression first, deterministic line truncation if insufficient
The table explains immediate overflow but leaves another question, which is precisely our gap:
can new stable facts enter after the block is full? This depends on maintenance and whether the resident block is itself canonical.
Framework
Canonical source
Maintenance path and trigger
After saturation
Verified location
claude-code
Index of the topic-file directory
Dream agent: 24 hours and at least 5 sessions
No write-path gate; truncation happens only at injection, leaving disk originals unchanged
Deterministically rebuild alongside other derived views after every installed transaction
Render to budget; Topic content outside the resident block stays retrievable
overview.md, “Resident content”
Adoption record.claude-code, codex, and openclaw separate editable canonical content from injection budgets. OpenProgram follows that principle, using
topics/core.md as canonical source and core.md as a deterministic derived view. Unlike them, OpenProgram does not delete
over-budget Topic content; retrieval can still return those blocks. Before migration, root core.md directly held canonical content and rejected over-budget transactions;
that implementation has been replaced.
Conclusion on trimming full resident content:None of seven gates leaves removals to unconstrained model judgment.
Five use deterministic truncation: claude-code MEMORY.md, codex, both openclaw layers, and claude-code SessionMemory’s fallback.
Two reject deterministically before returning choices to the model: hermes and ours.
Only claude-code SessionMemory involves model judgment, using model compression followed by deterministic fallback,
with a prompt explicitly naming the two sections to preserve rather than allowing unconstrained edits.
Four applicable lessons:First, Use two limits: line counts cannot constrain long lines; claude-code’s byte gate followed a 200-line, 197KB index.
Second, ownership matters more than time when trimming; openclaw removes only its own writes and preserves handwritten content.
Third, apply limits to all relevant content: codex limits project AGENTS.md but omits global AGENTS.md; claude-code hard-limits its own MEMORY.md but only warns for user-maintained CLAUDE.md.
Fourth, disclose truncation. codex only logs it, leaving users and models unaware of omitted content.
Our core.md currently falls under deterministic rejection with disclosure.
We will not copy the first two lessons directly: another gate on the write path still excludes stable facts;
ownership matters for openclaw because its resident file is the sole copy and deleting a line destroys it.
Once core.md is derived, omission is non-destructive and ownership need not be checked.
The decision and rationale are in What to adopt after comparison section 02.
08Implementation sizeLayer 2
Excludes tests. Colored bars represent long-term memory itself; gray bars represent compaction and transcript storage. Implementations range from 0 to 34,000 lines,
almost entirely depending on whether they maintain an index.
Retrieval accounts for the complexity.Vectors, embedding caches, index rebuilding, and hybrid ranking dominate openclaw’s 34000 lines.
Removing that layer leaves roughly the same scale as claude-code’s 4900 lines.
Our 6300 lines do not require vectors—embeddings are optional—and additional code implements transactions and topic-format validation.
The dividing factor is maintaining an index, not merely having memory.
09Where our design fitsLayer 3
The preceding eight sections position the current design across dimensions. Most reuse established practices;
node markers follow SessionDAG branch semantics, explained in section 05.
The design itself is documented elsewhere.Where markers live, when they are set, how forks traverse, how missing markers recover, and positional-cursor migration
are authoritatively documented in written-marker.md, with mechanism diagrams in
architecture.html section 04. This page only explains
how that form relates to other implementations.
The background agent inherits the main session’s system prompt and message prefix. topics/core.md is canonical; core.md is generated deterministically within the 2000-token budget described in section 07. The session-start rewrite is performed by a consolidation agent. Its six-hour cooldown remains unchanged.
Current comparison-decision status.
Implemented: topics/core.md as resident canonical source, per-node workspace markers, live-branch enumeration at session boundaries, and Topic footnotes preserving Source references.
Not implemented: index-free retrieval using Topic self-descriptions and small-model file selection (claude-code, section 06); retrieval usage as nightly-organization input (codex, section 06); direct writer notification on head changes instead of post-turn thresholds or idle watcher.
Implementation record (2026-08-11): Four of eight have long-term memory; message-branching session structures appear only in pi-mono, openclaw, and OpenProgram.
OpenProgram has replaced positional cursors with node workspace markers and uses topics/core.md as maintainable canonical resident content.
Pre-migration rows remain as problem evidence; purple rows now indicate current implementation, not future plans.