Memory write contract (epic #147, P5 / GH #155)

What belongs in the vector memory (save_memory / pdr_note_save) and what doesn’t. The store is Claude Code’s research/operational recall — keep it durable and clean so auto-recall (the P3 hooks) surfaces signal, not noise. This is what prevents the “favorite color ×12” rot the audit found.

Save it when it’s DURABLE and would help a FUTURE session

Good test: would I want this resurfaced automatically, weeks from now, in a fresh session?

  • decision — a closed strategic call + its rationale (“Phase 0 uses PDR1’s trainer because …”).
  • insight — a non-obvious finding from analysis (“vanilla CompGCN over-smooths on dense KGs past ~3 layers”).
  • fact — a verified, stable fact (“the ex vivo drug-sensitivity panel was drawn twice, in February and September 2025”).
  • gotcha-style — a trap + how to avoid it (“never use split_KG.py for Phase 0 — ratios/seed differ”). Save these as insight or task_hint; a dedicated gotcha kind is a deliberate future enum migration (native Postgres memorykind type), not yet added.
  • task_hint — “next time do X” / “where is Y” navigation.
  • summary — a digest of a large artifact, run, or thread.
  • preference — a stable user/operator preference.

Do NOT save

  • Transient conversation state, current-turn plans, or thinking-out-loud.
  • Anything trivially derivable from code, git history, GitHub issues, or CLAUDE.md — don’t duplicate the repo.
  • Unverified guesses. A wrong memory silently misleads every future recall — if unsure, lower salience and say so in the content, or don’t save.
  • Secrets / credentials.
  • One-off trivia with no future value.

Discipline (this is what keeps it from rotting)

  1. Search first. Run search_memory / pdr_note_search before saving. If a near-duplicate exists, update_memory it instead of adding another row.
  2. One fact per memory. Tight declarative title (3–8 words) + focused content. Don’t paste transcripts.
  3. Pick the right kind (list above) — it drives filtering and recall.
  4. pinned=true only for a handful of always-relevant anchors (current phase, hard constraints).
  5. salience: default 0.5; raise for high-value durable facts, lower for ephemeral-ish ones.
  6. Prefer pdr_note_save (with scope='run'/'task') for PDR-pipeline notes so they link to the run/task; use save_memory for general research/clinical memory.

Everything lands in the canonical workspace user (MEMORY_WORKSPACE_USER). Curate via the Memory panel (terminal alias memory); the nightly consolidation job (P6) merges accidental near-duplicates.