Memory layer episodic for overstack (4/4 memory layers — working/semantic/procedural/episodic).
The wiki holds distilled knowledge (semantic); the skill holds process (procedural); .claude/memory holds
flat facts. Still missing: concrete session events — "what the previous session did, which files it touched, what the
result was". This skill records exactly that into mem-rank store so the next session query can retrieve it
by MEANING (without anyone having to set [[wikilink]]).
WHAT
Purpose and context
- Purpose: write a structured session episode (did · files · outcome · session) into the memory store so the next session can retrieve "what the previous session did" by MEANING, without needing
[[wikilink]]. - Trigger (when to use):
- At the end of a session/meaningful milestone (closing an issue, finishing a feature, a decision) — wrap up what was done.
- When you want the next session to automatically remember "where task X was done, what the result was" without having to re-read the git log.
- User says "record episode", "record episode", "wrap up this session into memory", "save what the previous session did", "session episode",
/record-episode.
- Non-goals: NOT for durable knowledge (→ that is the wiki
/ingest) or flat user facts (→.claude/memory). Does not run automatically via a hook.
Mental model
phiên làm việc → did (1 câu) + files + outcome + session → mem-rank.py episode → harness/metrics/memory.jsonl → retrieve --kind-filter episode. An episode is an EVENT (episodic), pointing back to the session; the wiki (semantic) remains the source of truth. Editing an episode = supersede with a time link, not overwrite.
Input and output contract
| Field | Required? | Meaning | |
|---|---|---|---|
| In | "<did>" |
yes | main action (verb + object), with enough terms for retrieval |
| In | --files |
no | paths touched, comma-separated (taken from git diff --name-only if needed) |
| In | --outcome |
no | verifiable result (green tests / issue closed / PR number) |
| In | --session |
no | session / issue name |
| In | --id <ID> --supersedes <ID> |
only when editing | replaces the old version, keeps the link supersedes |
| Out | episode in harness/metrics/memory.jsonl |
yes | "done" = retrieve ... --kind-filter episode can return the episode just written |
Rules and capabilities
- RULE-01 (MUST): On-demand, NOT auto-hook — do not register a Stop-hook that writes automatically (ADR-004: /fdk on-demand only).
- RULE-02 (MUST): An episode is an event, not knowledge — if the content is a durable lesson, distill it into the wiki via
/ingest; the episode only points to "which session it was done in", it does not replace the wiki (the wiki remains the source of truth). - RULE-03 (MUST): The store is local/travel-friendly —
harness/metrics/memory.jsonl(already gitignored); do not pull from the cloud. - RULE-04 (SHOULD): The ranker is currently token-overlap (deterministic); embedding is an adapter
mem-rank.config.yaml(verified:false) — enable it when a backend is available, it does not block immediate use. - Capabilities: append writes to the local memory store + ranked retrieval; no network, no hooks.
Failure boundaries
- The content is a durable lesson / architecture decision → clarify/redirect to
/ingestor the wiki ADR, do not write an episode. - No verifiable result → write
did+ files, omit--outcome(partial), do not fabricate an outcome. retrievedoes not return the episode just written → failed, notify the user, do not consider it done.
HOW
Main workflow
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W01 | judgment | session | Summarize the session in 1 sentence did |
did sentence | the content is durable knowledge → /ingest |
| W02 | deterministic | git diff, test/issue results | Collect --files + --outcome |
parameters | no verifiable outcome → leave blank |
| W03 | effect | did + parameters | mem-rank.py episode ... |
episode line in the store | — |
| W04 | effect | old episode is wrong | Supersede with --id <ID> --supersedes <ID> (B01) |
new episode + link | — |
| W05 | deterministic | question | mem-rank.py retrieve "<câu hỏi>" --kind-filter episode |
episode appears in the results | not found → failed |
Details of each step (source of truth for W01–W05):
Steps
- Summarize the session in 1 sentence
did— the main action (verb + object), with enough terms for retrieval. - Collect files + results:
--files= the paths touched (taken fromgit diff --name-onlyif needed);--outcome= the verifiable result (tests green / issue closed / which PR number). - Write the episode:
python3 harness/scripts/mem-rank.py episode "<did>" \ --files "path1,path2" --outcome "<kết quả>" --session "<tên/issue>" - Revise an old episode (temporal supersede): use
--id <ID> --supersedes <ID>— the new version replaces the old one and keeps the linksupersedesto answer "when was this true". - Quick check:
python3 harness/scripts/mem-rank.py retrieve "<câu hỏi>" --kind-filter episode.
Anti-patterns
- Writing an episode as long as a diary — 1 sentence
did+ file + outcome is sufficient for retrieval; length = noise. - Use an episode as the place to store durable architectural decisions — that is the job of the ADR wiki.
Branches
| ID | Kind | Guard | Behavior | Skip / failure | Rejoin |
|---|---|---|---|---|---|
| B01 | user_optional | need to edit an old episode | write a new version with --id <ID> --supersedes <ID> (temporal supersede) |
do not edit → skip | W05 |
| B02 | capability_optional | embedding backend available, mem-rank.config.yaml verified:true |
ranker uses embeddings instead of token-overlap | not available → token-overlap (default) | W05 |
Validation and stopping
Done when W05 retrieves the episode just written. One episode = 1 sentence did + file + outcome; longer is noise (see Anti-patterns).
Examples
- Positive: just closed GH#9 →
python3 harness/scripts/mem-rank.py episode "add episodic memory layer to mem-rank" --files "harness/scripts/mem-rank.py" --outcome "GH#9 closed, mem-proxy eval pass" --session "GH#9"→retrieve "episodic memory làm ở đâu" --kind-filter episodereturns that episode. - Boundary/failure: user wants "write episode: decided to use SQLite instead of JSONL for the store" → this is a durable architectural decision → do not write an episode, switch to the ADR wiki /
/ingest.
Origin
- Source: issue GH#9 (frontier-gap memory, 4/4 memory layers); ledger
030726-memory-episodic-vector.md. Engine:harness/scripts/mem-rank.py(episode/retrieve/temporal). Eval:harness/scripts/mem-proxy.py. - Date: 2026-07-04