PROJECT 3.3

🗃️ Project 3: curated memory

The agent proposes, you approve. In this project you stop treating Claude's raw memory as the source of truth and build a layer of approved facts that Claude, Codex and any other executor read from the same place — reusing the vault that openpcbotv3 already runs on this machine.

6
Topics
~40
Minutes
Intermediate
Level
Project
Type

🎯 The project on one screen

Goal

Have ONE place for verified facts about you and your projects, approved by you, read by every executor.

You walk away with

3 facts promoted from Claude's memory to a project's context/overview.md, with source and date; AGENTS.md and CLAUDE.md instructing to read the vault's USER.md.

Acceptance criteria

A new session in Claude AND in Codex cites a personal fact pointing to USER.md as the source. Nothing from the raw memory was bulk-copied.

1

🧱 Claude's memory is raw material, not a source

The diagnosis of this machine found 227 memory folders and 869 files in ~/.claude/projects/*/memory. Each file is an observation Claude thought was worth keeping: a preference, a path, a project fact. None of it went through your approval. Much of it is outdated, duplicated or contradicts another file written weeks later. And only Claude reads that folder.

That's why the plan treats this memory as raw material: a mine of candidate facts, not the truth. The truth is what you approved, with source and date, in a file any runtime can open.

✗ Bulk-copying the memory

  • 869 files become 869 files somewhere else, with the same contradictions.
  • Codex starts believing facts that Claude recorded wrong back in May.
  • No source, no date: no way to tell which version is valid.
  • Secrets and private paths may get copied along.

✓ Promote fact by fact

  • You read the memory of the project you're working on and pick what is still true.
  • Each promoted fact gets a source, a date and a scope.
  • The rest stays where it is, as history.
  • The result fits on one screen and any model can read it.

New here? Claude Code's "memory" is a folder of Markdown files that the agent itself writes between sessions to remember things. "Promoting" a fact means copying it, reviewed, into a file you control. The two differ in just one thing: human approval.

Key concepts

Raw material

Unapproved observations, candidates to become facts.

Source of truth

What you approved, with origin and date.

Promotion

Moving a fact from raw memory to the overview, reviewed.

Bulk copy

The mistake to avoid: it carries noise and contradiction.

2

🗄️ The openpcbotv3 vault as the global overview

You don't need to invent the approved-facts layer: it already exists on this machine. The openpcbotv3 bot keeps a curated vault in ~/vault/MEMORY.md and ~/vault/USER.md. The bot proposes entries; nothing is written until you approve it with /memoria aprovar <id>. And USER.md goes into the prompt of every bot conversation. That is exactly the "global overview" the plan calls for.

raw memory 869 files, unapproved proposal the agent suggests a fact approval you say yes or no approved overview USER.md · overview.md · source + date

Read from bottom to top: each step filters the one before. Only the last step, approved by you, is read by the executors. Raw memory never goes straight up.

📊 What v3 already does (from the README, Brain section)

  • Every message you send becomes memory classified as semantic (durable: "I prefer", "always", "I live in") or episodic. A question never becomes a fact.
  • Keyword search (FTS5) plus a bge-m3 vector; prompt context capped at 600 tokens.
  • Curated vault: the bot proposes, you approve. USER.md goes into every prompt.
  • Nightly consolidation at 4 a.m., on Ollama, at zero cost: merges duplicates and flags contradictions.

💡 Why reuse instead of building new

The migration plan says: don't reinvent. The vault already has the approval mechanism, already runs as a service and is already read by an executor (the bot). All that's missing is pointing Claude, Codex and dsh at the same file. Building a second "global overview" would create the second source of truth the whole course tries to avoid.

Key concepts

Curated vault

MEMORY.md + USER.md, only approved entries get in.

USER.md

Facts about you; goes into every bot prompt.

Semantic vs episodic

Durable vs "happened once".

Global overview

The personal-facts file every executor reads.

3

✅ The propose → approve flow, applied to a project

Now for the hands-on part. Pick a project you've already migrated (Project 1) and promote 3 facts from Claude's raw memory into its context/overview.md. Three, not thirty: the goal is to practice the move, not to empty the folder.

1

List the project's raw memory

Goal: see what Claude stored about this project, with dates. Replace the path with yours.

MEM=~/.claude/projects/-home-nmaldaner-projetos-<meu-projeto>/memory
ls -lt "$MEM" | head -20
# read only the index, which sums up each file in one line
cat "$MEM/MEMORY.md"

How to check: the index lists the files with one hook per line. If it's empty, this project has no memory and you skip to step 3 using facts from its own CLAUDE.md.

2

Ask the agent to PROPOSE, without writing

Goal: the agent reads the memory and returns classified candidates. Paste into Claude Code or Codex, inside the project.

Read the files in <memory/ path above>. Don't edit anything.
Return a table with at most 8 candidates for durable facts about this project.
Columns: fact (one sentence), type (fact | preference | hypothesis | decision),
source file, file date, and "still looks valid?" (yes / no / not sure).
Discard questions, past tasks and any secret or key.

How to check: the table cites the source file on every row. No source, the row doesn't count.

3

Approve 3 and write them to the overview with source and date

Goal: you choose; the agent writes only what you approved, in the template's format.

I approve candidates 2, 5 and 7. Add them to context/overview.md,
in the "Verified facts" section, one per line, in the format:
- <fact> (source: memory/<file>, observed on <YYYY-MM-DD>, promoted on 2026-09-14)
The other candidates do NOT go in. Don't change anything else in the file.

How to check: git diff context/overview.md shows exactly 3 new lines, all with a source and two dates.

💡 The same thing in the bot

In openpcbotv3 the move is identical, just over Telegram: the bot sends a proposal with an id, you reply /memoria aprovar <id>, and only then does the line go into ~/vault/MEMORY.md or USER.md. What you just did by hand in the project is the manual version of the same protocol.

Key concepts

Proposal

A candidate with source, type and date; it doesn't count yet.

Explicit approval

You name which ones go in; the rest stay out.

Two dates

When it was observed and when it was promoted.

Small diff

Three lines. If the diff is big, something went off-protocol.

4

🕰️ Consolidation with superseded_by: never delete, just hide

What happens when an approved fact stops being true? v3 answers with nightly consolidation: when it detects two memories that contradict each other, the older one gets a superseded_by field pointing to the new one. It isn't deleted, it just stops going into the prompt. You can go back and see what was believed before.

old fact · 2026-07-09 "inemavox default voice is bella" status: superseded · stays in history superseded_by current fact · 2026-09-14 "inemavox default voice is rachel" status: active · goes into the prompt source: ~/.claude/CLAUDE.md, decision from 2026-07-09

The old fact still exists, but dashed and out of the prompt. The executor only sees the current fact. If the new decision was wrong, you undo it without losing anything.

✓ How to resolve a conflict

  • Source and accepted decision win. Timestamp only breaks ties between equals.
  • The losing version gets superseded_by and leaves the prompt.
  • Backup before each consolidation round.

✗ What breaks memory

  • "The newest always wins": a file written by mistake wipes out a decision of yours.
  • Deleting the old version: you lose the trail of why you changed it.
  • Keeping both active: the executor picks one at random.

In a Markdown overview, with no database: apply the same idea by hand. Instead of deleting the old line, move it to a "Superseded" section at the end of the file with the note superseded by: <new line>, on <date>. The agent reads only "Verified facts"; the history stays for you.

Key concepts

superseded_by

Pointer from the outdated fact to the fact that replaced it.

Hide ≠ delete

Out of the prompt, kept in the history.

Provenance

Where it came from and who decided; beats date.

Consolidation

Periodic pass that merges and marks; always with a backup.

5

🔗 Claude, Codex and dsh reading the same USER.md

The bot already injects USER.md into every prompt because its code does that. Claude Code and Codex have no such code, and no file is loaded on its own besides CLAUDE.md and AGENTS.md. The fix is the same as throughout the course: an explicit read instruction in those two files.

4

Add the instruction to the global and project AGENTS.md

Goal: every executor that reads AGENTS.md learns where the approved personal facts live. CLAUDE.md inherits it via @AGENTS.md.

cat >> ~/.codex/AGENTS.md <<'EOF'

## Approved personal facts
- Before assuming any preference of mine (voice, image model, git account, paths),
  read `~/vault/USER.md`. It is the only approved source. Cite the file when you use a fact from it.
- Do not propose writing anything there on your own: propose it in text and I approve.
EOF
# does the global CLAUDE.md start with "@AGENTS.md"? If not, add that line at its top.
head -1 ~/.claude/CLAUDE.md

How to verify: grep -n USER.md ~/.codex/AGENTS.md returns the line; head -1 ~/.claude/CLAUDE.md returns @AGENTS.md.

5

Prove it in both runtimes

Goal: a new session cites USER.md as the source of a personal fact. If it doesn't cite the source, it didn't pass.

P='What default narration voice do I use? Answer in one sentence and say which file you read it in. Do not edit anything.'
claude -p "$P"
codex exec --skip-git-repo-check "$P"

How to verify: both answers name ~/vault/USER.md. If one cites Claude's memory or "I don't know", the instruction didn't get through; check that the right AGENTS.md is being read.

🐳 What about dsh?

dsh-sandbox doesn't read ~/vault because the container only sees what was mounted. In local mode, ~/projetos is mounted; the vault isn't. Two ways out: mount ~/vault read-only in docker-compose.projetos.yml, or copy the approved USER.md into the project as context/user.md with the snapshot date. Project 4 covers dsh in detail.

Key concepts

Read instruction

The way to "inject" without code: tell it to read.

One source, N readers

Bot, Claude, Codex and dsh point to the same file.

Cite the source

The acceptance criterion: a right answer without a source doesn't count.

Dated snapshot

When you can't read it live, copy it with a date and a refresh rule.

6

📦 Archive old sessions, only after the cycle is running

This machine has 6,859 Claude JSONL sessions (2.3 GB) and 209 from Codex. They are the raw history of every conversation. While your context lived only there, deleting them meant losing memory. Once handoff and prime (module 2.6) are running, what matters from each session has already gone into handoffs/latest.md and the overview. Then, and only then, archiving becomes a safe cleanup.

Week 1: cycle running

Every session ends with a handoff. Every new session starts with prime. No session is deleted yet.

Week 2: fact promotion

In the projects you touched, 3 to 5 facts promoted to the overview (topic 3). What was relevant raw memory is now approved.

Week 3: archive, don't delete

Sessions older than 90 days go into a compressed archive outside ~/.claude. If anything is missing, it's there.

After that: monthly routine

One command a month. Your disk (88% full today) will thank you.

Step 6: archive sessions older than 90 days (reversible)

Goal: get them out of the way without losing them. First count and list; only the second block moves anything.

# 1) just look: how many and how much they weigh
find ~/.claude/projects -name '*.jsonl' -mtime +90 | wc -l
find ~/.claude/projects -name '*.jsonl' -mtime +90 -print0 | du -ch --files0-from=- | tail -1

# 2) archive (moves into a dated tar.gz outside ~/.claude; nothing is deleted)
ARQ=~/projetos/output/arquivo-sessoes-claude-$(date +%Y-%m-%d).tar.gz
find ~/.claude/projects -name '*.jsonl' -mtime +90 -print0 \
  | tar --null -T - -czf "$ARQ" --remove-files
ls -lh "$ARQ"

How to verify: running the first find again returns 0; the tar.gz exists and opens with tar -tzf "$ARQ" | head. Rollback: tar -xzf "$ARQ" -C /.

⚠️ Project risks and rollback

  • Promoting a wrong fact: the diff is 3 lines; git checkout context/overview.md undoes it.
  • Archiving too early: don't archive before you have at least two weeks of handoffs. The tar.gz is reversible, but the habit of going back to dig through it isn't.
  • Two sources of personal facts: if you create a global context/user.md alongside the vault, pick one and point the other to it.
  • Secret in the overview: keys live in .env; the overview cites the path, never the value.

Key concepts

Archive ≠ delete

Compresses it and moves it out of the way; one command brings it back.

Precondition

Handoff/prime running before any cleanup.

Session = history

Raw, useful for auditing; not a source of context.

Routine

Once a month, the same command.

Self-check (optional): in May, Claude saved that "the default image model is flux2-dev", and in August that "it's flux2-klein". What should you do in the overview?

🎯 Project summary

Raw memory is raw material — 869 unapproved files are not a source; promote fact by fact.
The vault already exists — openpcbotv3's MEMORY.md and USER.md, with human approval, become the global overview.
Propose → approve — 3 facts with a source and two dates; a 3-line diff.
superseded_by and archiving — never delete, hide; old sessions only after the cycle is running.

Next project:

3.4 — Project 4: a third executor (dsh-sandbox and a local model)