Skip to content

Commit 220d0b6

Browse files
ndemiancclaude
andcommitted
docs(sessions): project memory & continuity — the third of the triad
Design for cross-session memory so a new session already knows the recent arc of the project ('feels like home'). Best-practice core: - Three SEPARATE layers, never conflated: Facts (durable, always-on, tiny), Journal (recent outcomes, always-on recent slice), Recall (full corpus, on-demand tool). Small when always-on, deep when asked. - Plain files per project (MEMORY.md digest + journal.jsonl + facts.jsonl) — editable, greppable, syncable; no opaque store. Rides loadProjectRules for injection + compaction machinery for cheap-lane extraction on seal. - Freshness/provenance/decay + newest-wins; auto-archived-stale fades from the digest but stays in Recall (reconciles the 'not auto-archived' instinct). - Transparency (a Project-memory surface, edit/pin/delete/not-true, inferred vs confirmed) and SECURITY (poisoning from hostile repos: outcomes-not-content, bounded/reviewable, untrusted-context framing, memory-never-executes). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f16fd00 commit 220d0b6

1 file changed

Lines changed: 184 additions & 0 deletions

File tree

docs/levelcode-sessions-memory.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# LevelCode Sessions — Project Memory & Continuity
2+
3+
**Status:** proposed · **Scope:** `extensions/levelcode-ai` + the sessions store · **Third of the triad:** [`levelcode-chat-sessions-design.md`](./levelcode-chat-sessions-design.md) stores a chat · [`levelcode-sessions-experience.md`](./levelcode-sessions-experience.md) lets you browse it · **this doc lets the project *remember itself*.**
4+
5+
> The wish, in the user's words: *"if in one session we tidy the CHANGELOG for v1.0.4, then a new session already knows something about what was achieved — the past experience keeps growing, and it feels like home."* That is the right ambition. It is also the feature most likely to become expensive, stale, creepy, or wrong. This document is mostly about **not** doing those four things while still delivering the magic.
6+
7+
---
8+
9+
## 0. Four disciplines, stated up front
10+
11+
Every decision below serves one of these. If a choice violates one, it's the wrong choice:
12+
13+
1. **Small when always-on.** What loads into *every* session must be a tight digest (a page, not a library). You can never pour past sessions into a new one — it costs money and drowns the task.
14+
2. **Deep when asked.** The full episodic record is retrieved *on demand* (a tool call, a search), not carried in context.
15+
3. **Transparent always.** Memory is plain, readable, editable files the user owns and can correct — never a hidden vector store. This is the BYOK/hackable ethos *and* the only way memory earns trust.
16+
4. **Never trusted blindly.** Memory is context, not gospel. It is dated, sourced, decays, and is treated as *possibly-stale, possibly-poisoned* input the agent verifies — because some of it was distilled from untrusted workspace content.
17+
18+
---
19+
20+
## 1. The trap: three things people call "memory"
21+
22+
Conflating these is why most "AI memory" features feel either useless or unsettling. LevelCode keeps them **separate layers**, each with a different size, lifetime, and trust level:
23+
24+
| Layer | What it is | Size | Loaded | Example |
25+
|---|---|---|---|---|
26+
| **Facts** | durable, curated truths about the project & your preferences | tiny | **always-on** | "Idempotency keys live in Redis." "CHANGELOG is `RELEASE-NOTES.md`." |
27+
| **Journal** | a rolling digest of *recent outcomes* — what got done lately | small | **always-on (recent slice)** | "Shipped v1.0.4 notes; fixed the MCP timeline rail; tidied the CHANGELOG." |
28+
| **Recall** | the full episodic corpus — every session, verbatim & summarized | large | **on-demand only** | *"What did we decide about refund retries last month?"* |
29+
30+
The user's example — *a new session knows we tidied the CHANGELOG* — is the **Journal** layer (recent accomplishments), backed by **Recall** for depth and **Facts** for the stable stuff. The magic is the Journal; the safety is that it's small and the rest is retrieved.
31+
32+
---
33+
34+
## 2. Prior art — what to copy, what to avoid
35+
36+
- **Claude Code `CLAUDE.md` / this project's own `# Memory`** — a *plain-file, curated, indexed* memory folded into every session. **Copy:** plain text, human-editable, an index file, per-item provenance, typed entries (fact / preference / project / reference). It is the proven shape. **Its gap:** it's hand-maintained; we add cheap-lane *auto-extraction* on top, without losing the editability.
37+
- **ChatGPT Memory** — auto-extracts durable facts across chats, **shows them, lets you delete them.** **Copy:** transparency + per-item delete is non-negotiable. **Avoid:** opacity about *when* something was learned.
38+
- **Cursor "memories" / rules** — auto + manual project rules. **Copy:** the always-on project-rules injection. **Avoid:** memories that silently accumulate with no review surface.
39+
- **MemGPT / Letta** — the tiered *core memory (always-on) + archival memory (retrieved)* architecture. **Copy:** exactly this tiering — it is §0.1/§0.2 in one sentence. **Avoid:** its complexity; we don't need a self-editing agent loop to start, just extract-on-seal + a recall tool.
40+
- **Naïve RAG-over-everything** — embed every message, retrieve top-k into every prompt. **Avoid:** unbounded cost, retrieval of stale/irrelevant chunks, and an opaque store nobody can read. Greppable JSONL + a scoped tool beats a mystery index for a single-project developer corpus.
41+
42+
**The synthesis:** a **tiered, plain-text, auto-extracted-but-user-editable** memory, per project, that loads a tight digest always and retrieves depth on demand.
43+
44+
---
45+
46+
## 3. Architecture (grounded in what already ships)
47+
48+
Three pieces of LevelCode already exist and this design just connects them:
49+
50+
- **`loadProjectRules`** (`agent.js`) already folds `AGENTS.md` / `CLAUDE.md` / `.cursorrules` into the cached system block. **That is the always-on injection channel**`MEMORY.md` rides the exact same mechanism.
51+
- **`compactAgentMemory` / `COMPACT_SYSTEM`** already summarize a transcript into a briefing. **That is the extraction engine** — pointed at a *sealed* session instead of a live one, on the **cheap/fast model lane** (the same routing titles use).
52+
- **The sessions store** (`~/.levelcode/sessions/<project-slug>/`) is already per-project, plain-file, greppable, local. **Memory lives beside the sessions it came from.**
53+
54+
### Storage — plain files, per project
55+
56+
```
57+
~/.levelcode/sessions/<project-slug>/
58+
memory/
59+
MEMORY.md ← the always-on digest: Facts + a recent-Journal slice. Capped (~1–2k tokens). Human-editable. Injected into every session.
60+
journal.jsonl ← append-only, one line per sealed session: its outcome summary, files, decisions, links back to the session id.
61+
facts.jsonl ← durable facts with provenance {text, source_session, learned_at, confidence, confirmed:bool}
62+
```
63+
64+
Everything is readable, `grep`-able, `git`-able (a user could even check `memory/` into a dotfiles repo), and rides the future M9 encrypted sync unchanged. No opaque DB, ever (anti-Cursor, per the spine).
65+
66+
### The pipeline
67+
68+
```
69+
session seals ──▶ cheap-lane "outcome" summary ──▶ append to journal.jsonl
70+
(1–3 sentences: what was achieved, key files, any decision)
71+
72+
every N seals ──▶ CONSOLIDATION pass (cheap-lane) ──▶ rewrite MEMORY.md
73+
(or on demand) • fold recent journal entries into a tight "Recently" section
74+
• promote repeated/confirmed observations into Facts
75+
• let stale entries decay OUT of the digest (they stay in journal.jsonl)
76+
• hard cap the file — evict lowest-signal first
77+
78+
any session ──▶ MEMORY.md injected into the system block (like project rules)
79+
80+
on request ──▶ recall_sessions(query) ← an agent TOOL: fuzzy/greppable search over
81+
journal.jsonl + the full session JSONL; returns cited snippets on demand
82+
```
83+
84+
- **Extraction is cheap and incremental:** one small model call when a session seals — not a batch job, not embeddings-of-everything. Reuses the compaction machinery.
85+
- **Consolidation is where "growing memory" happens honestly:** it is the *sleep* of the system — recent experience is folded in, durable patterns are promoted to Facts, and stale detail decays from the always-on digest while remaining fully recoverable in `journal.jsonl` and Recall. This is what makes memory *compound* without *bloating*.
86+
- **Recall is a tool, not a context tax:** `recall_sessions("refund retries")` runs only when the agent (or user) asks, and returns cited results — never loaded speculatively.
87+
88+
---
89+
90+
## 4. Staying true: freshness, provenance, conflict
91+
92+
Memory that lies is worse than no memory. Four guards:
93+
94+
- **Provenance on everything.** Each fact/journal entry carries `source_session` + `learned_at`. The UI and the model can always answer *"says who, and when?"* A memory is never a free-floating assertion.
95+
- **"As of" honesty.** Injected memory is framed to the model as *"known as of <date>; verify against the current code before relying on it"* — the same discipline this project's own memory system uses. Memory informs; the code decides.
96+
- **Newest-wins with flagging.** When a new observation contradicts a stored fact (`uses Redis``moved to Postgres`), consolidation supersedes the old one and keeps a one-line history; a genuinely ambiguous conflict is surfaced for the user, not silently guessed.
97+
- **Decay.** Journal entries lose always-on weight with age and inactivity, so the digest reflects *current* project reality, not a museum. Decayed ≠ deleted — it's still in Recall.
98+
99+
---
100+
101+
## 5. How lifecycle feeds memory (the "not auto-archived" instinct, reconciled)
102+
103+
Your instinct — *draw memory from the sessions that weren't auto-archived* — is right, once we separate two kinds of archiving (see experience doc §4.9):
104+
105+
- **Manual "Done"** = *"I finished this real work."* These are **prime memory** — a shipped feature is exactly what a new session should know about. They feed the Journal and can be promoted to Facts.
106+
- **Auto-archive (30d inactive)** = *"this went cold."* These **fade from the always-on digest** (they're stale — that is the decay in §4) **but stay in `journal.jsonl` and Recall.** So they're never *forgotten*, just no longer *front-of-mind*.
107+
- **Pinned** sessions get **extra memory weight** — a long-running refactor thread you keep returning to should stay in the digest regardless of age.
108+
109+
So: **the Journal keeps growing (append-only), while the always-on digest stays fresh** — recent + done + pinned in front of mind, cold stuff one recall away. That is precisely "the past experience keeps growing" without "every new chat pays for a year of history."
110+
111+
---
112+
113+
## 6. Transparency & control — the non-negotiables
114+
115+
Auto-memory is only acceptable if the user is never surprised by it:
116+
117+
- **A "Project memory" surface** in the Sessions panel (a tab beside History): the current `MEMORY.md` rendered, every Fact and recent-Journal line with its source session and date, and per-item **edit · pin · delete · "not true"**. It is *your* growing knowledge base, visible and yours to curate.
118+
- **It's just files.** `memory/MEMORY.md` opens in the editor. Power users edit it directly; the consolidation pass respects hand-written sections (a `<!-- pinned -->` block is never evicted).
119+
- **Consent posture:** extraction is **on by default but fully reviewable**; auto-inferred Facts are marked *inferred* (lower trust, dimmer) until used/confirmed, at which point they become *confirmed*. Nothing durable is asserted with false confidence.
120+
- **Off switches:** `sessions.memory.enabled` (master), per-project opt-out, and a "forget this session" that removes its contribution.
121+
- **Never a black box:** no hidden embeddings the user can't read. If we ever add vectors for recall speed, they are a *derived cache* over the plain files — rebuildable, never the source of truth (same rule as the sessions index).
122+
123+
---
124+
125+
## 7. Security — memory is an attack surface
126+
127+
This is the part most designs skip, and LevelCode can't (it's the security-forward editor):
128+
129+
- **Poisoning via untrusted content.** Sessions contain workspace text, which in a hostile repo is attacker-controlled. A naïve extractor could be steered into writing a false "memory" (*"the deploy token is safe to print"*). Mitigations: extraction summarizes **outcomes and user/agent actions, not arbitrary quoted content**; the digest is **bounded and reviewable**; and injected memory is **framed as untrusted, verify-first** (§4) — it can inform, never command.
130+
- **Memory never executes.** It is context in the system block, exactly like project rules. It cannot run a tool, approve an MCP call, or edit a file. An injected instruction inside a "memory" is treated like any other untrusted text (the project's existing prompt-injection posture).
131+
- **Provenance limits blast radius.** Because every item is sourced and dated, a poisoned entry is traceable to its session and removable in one click — and its low, *inferred* confidence keeps it from being load-bearing until a human confirms it.
132+
- **Local & private.** Memory never leaves the machine (BYOK promise); M9 sync, if enabled later, encrypts it like the sessions themselves.
133+
134+
---
135+
136+
## 8. The "feels like home" experience
137+
138+
The magic, delivered quietly (never a wall of text):
139+
140+
- **Welcome-back digest.** A new session's empty state shows a tight, dismissible strip: *"This project, lately: shipped v1.0.4 notes · fixed the MCP timeline · tidied the CHANGELOG. 3 pinned threads. [what I remember ↗]"* — glanceable, honest, one click to the full memory surface. The agent already **has** this context, so its first reply is continuous, not amnesiac.
141+
- **Continuity in the answer.** Because `MEMORY.md` is in context, a new session's agent naturally says *"picking up from the v1.0.4 work — the CHANGELOG's already tidied; want me to…"* instead of asking what project this is. That is the "home" feeling: it remembers, so you don't re-explain.
142+
- **The growing artifact.** Over weeks, `MEMORY.md` becomes a genuine, readable project brain the user can watch grow, prune, and pin — a compounding asset, not a chat log. Opening it feels like opening a well-kept lab notebook the project wrote about itself.
143+
144+
---
145+
146+
## 9. Settings
147+
148+
| Setting | Default | Meaning |
149+
|---|---|---|
150+
| `sessions.memory.enabled` | `true` | master switch for extraction + injection |
151+
| `sessions.memory.dir` | `<sessions.dir>/<project>/memory` | hackability: relocate it |
152+
| `sessions.memory.digestTokens` | `1500` | hard cap on the always-on `MEMORY.md` |
153+
| `sessions.memory.consolidateEverySeals` | `5` | how often the consolidation pass runs (also: on demand) |
154+
| `sessions.memory.journalRecentDays` | `21` | recency window fed to the always-on digest |
155+
| `sessions.memory.confirmFacts` | `false` | require a click before an inferred Fact becomes confirmed |
156+
| `sessions.memory.recallTool` | `true` | expose `recall_sessions` to the agent |
157+
158+
---
159+
160+
## 10. Implementation phases (layered on the sessions plan)
161+
162+
**M1 — Journal + recall tool** *(M)*. Extract-on-seal → `journal.jsonl` (reuse compaction, cheap lane); ship `recall_sessions` as an agent tool over journal + JSONL. *No always-on injection yet — deep recall works first, cheaply.* Exit: in a fresh session, *"what did we do about refunds?"* returns cited past-session answers.
163+
164+
**M2 — the always-on digest** *(M)*. Consolidation pass → `MEMORY.md`; inject it via the `loadProjectRules` channel; the welcome-back strip. Freshness/decay/provenance. Exit: a new session opens already knowing the recent arc, in ≤1.5k tokens, with sources.
165+
166+
**M3 — the memory surface & control** *(M)*. The "Project memory" panel tab: view/edit/pin/delete/"not true", inferred-vs-confirmed, per-project off. Exit: a user corrects a wrong memory and the agent stops repeating it.
167+
168+
**M4 — polish & safety hardening** *(S)*. Conflict reconciliation UI, poisoning red-team pass, decayed-entry recall, export. Exit: an adversarial repo cannot plant a load-bearing memory; EXIT-TEST.md green.
169+
170+
**Deliberately later:** cross-*project* memory ("how did I do idempotency in the *other* service?"); a vector cache over the plain files for large corpora; team-shared project memory (rides M9 sync).
171+
172+
---
173+
174+
## 11. Risks, honestly
175+
176+
- **The digest goes stale or wrong** → decay + provenance + "verify-first" framing + one-click correction; and it's capped so a bad line can't dominate.
177+
- **Extraction costs add up** → cheap/fast lane only, one small call per seal, consolidation every N (not every) seals; all tunable, all disableable.
178+
- **Poisoning from hostile repos** → outcomes-not-content extraction, bounded reviewable digest, untrusted-context framing, memory-never-executes (§7).
179+
- **Creepiness** → nothing is hidden; it's plain files with a review surface and a delete button; inferred facts are visibly low-confidence until confirmed.
180+
- **Over-remembering noise** → not every session earns a Fact; the Journal is recency-weighted and decays; one-off Q&A sessions contribute a thin line at most, and fade.
181+
182+
---
183+
184+
*Store it (spine) · browse it (experience) · **remember it (this doc).** Together: a sessions feature where the project accumulates its own experience — small in every prompt, deep on request, legible and yours, and safe against the repo that would lie to it.*

0 commit comments

Comments
 (0)