From 0f7f088ebadb7cbb67f0b5e2831227711d6a2bd7 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 18 Jun 2026 20:05:07 -0400 Subject: [PATCH] conductor(track): fable_review_20260617 cluster 8 (Memory & Storage) sub-report Tier 3 worker dispatch. Verdict: Useful + nagent-stronger. 499 lines. Fable System Prompt.md:166-251 (memory_system + persistent_storage_for_artifacts) cited. Project refs: src/models.py History types, agent_memory_dimensions.md, guide_knowledge_curation.md. Fable artifact NOT committed. --- .../research/cluster_8_memory_and_storage.md | 499 ++++++++++++++++++ 1 file changed, 499 insertions(+) create mode 100644 conductor/tracks/fable_review_20260617/research/cluster_8_memory_and_storage.md diff --git a/conductor/tracks/fable_review_20260617/research/cluster_8_memory_and_storage.md b/conductor/tracks/fable_review_20260617/research/cluster_8_memory_and_storage.md new file mode 100644 index 00000000..ffe95d70 --- /dev/null +++ b/conductor/tracks/fable_review_20260617/research/cluster_8_memory_and_storage.md @@ -0,0 +1,499 @@ +# Cluster 8: Memory System & Persistent Storage + +**Sub-agent dispatch:** Tier 3 Worker (2026-06-17). Read-only research task. +**Sources read:** +- `docs/artifacts/Fable System Prompt.md` lines 166-251 (`memory_system` + `persistent_storage_for_artifacts`) +- `docs/artifacts/Fable System Prompt.md` lines 436-480 (`search_instructions`, the copyright-quote discipline) +- `src/models.py:200-231` (the `#region: History Utilities` block + `parse_history_entries`) +- `src/models.py:523-559` (`FileItem` schema — the curation memory dim) +- `src/history.py:8-100` (`UISnapshot`, `HistoryEntry`, `HistoryManager` — UI undo/redo, not memory) +- `docs/guide_discussions.md` (full file, 353 lines — the discussion dim) +- `conductor/code_styleguides/agent_memory_dimensions.md` (full file, 306 lines — the 4-dim canonical) +- `docs/guide_agent_memory_dimensions.md` (full file, 278 lines — the cross-cutting user guide) +- `docs/guide_knowledge_curation.md` (full file, 358 lines — the 4th dim deep-dive) +- `conductor/code_styleguides/knowledge_artifacts.md` (referenced; canonical for the harvest pattern) +- `conductor/tracks/nagent_review_20260608/nagent_review_v2_3_20260612.md` §2.8 (Pattern 8: Harvest Knowledge), §3.1 (Knowledge harvest subsystem), §3.9 (Per-file knowledge notes), §4.4 (per-file notes sub-pattern) +- `conductor/tracks/fable_review_20260617/spec.md` §5 row 8 (this cluster's scope) + +--- + +## 1. What Fable says + +Fable's `memory_system` section is 5 lines (L166-170) and the `persistent_storage_for_artifacts` section runs L171-251. The two sections are structurally separate but conceptually adjacent: the `memory_system` describes Claude's user-facing memory feature (the setting Anthropic ships in Claude.ai); the `persistent_storage_for_artifacts` describes the JavaScript-key-value storage API that powers artifacts in Claude.ai. Both are framed as "state that persists across sessions" but they target different layers (a per-user memory layer vs. a per-artifact storage layer). + +### 1.1 The `memory_system` section (L166-170) + +The section is two bullets: + +> "Claude has a memory system which provides Claude with access to derived information (memories) from past conversations with the user" (L168) + +> "Claude has no memories of the user because the user has not enabled Claude's memory in Settings" (L170) + +That's the whole section. The framing is **affordance**, not implementation: Fable tells the model what it *can* access (memories), not how the memories are stored, retrieved, ranked, audited, or pruned. The "derived information" hedge — "derived information (memories)" — is the load-bearing word: the model is told the memories are *not raw transcripts* but *extracted facts*. There is no description of the extraction pipeline, the dedup logic, the retention policy, the audit log, or the user controls. + +The "user has not enabled Claude's memory in Settings" disclosure is a transparency move: if the user has the toggle off, the model must say so rather than fabricating memories. This is the same pattern Fable uses elsewhere (the "Claude does not have X" disclaimer) — it's product transparency, not behavioral instruction. + +### 1.2 The `persistent_storage_for_artifacts` section (L171-251) + +This is the substantive part. The section describes the `window.storage` API, a JavaScript key-value store available to artifacts. The section is structured as: + +1. The 4 API methods (L181-184): `get(key, shared?)`, `set(key, value, shared?)`, `delete(key, shared?)`, `list(prefix?, shared?)`. +2. A usage example block (L188-202) showing `await window.storage.set('entries:123', JSON.stringify(entry))` and the corresponding `get`/`list` calls. +3. The "Key Design Pattern" subsection (L206-211): hierarchical keys under 200 chars, "no whitespace, path separators, or quotes"; "combine data updated together in single keys"; the example reframes `cards + benefits + completion` as a single `cards-and-benefits` key. +4. The "Data Scope" subsection (L215-220): personal (shared: false, default) vs shared (shared: true, visible to all users). +5. The "Error Handling" subsection (L222-241): "all storage operations can fail — always use try-catch"; the note that accessing non-existent keys throws (does not return null); the two try-catch patterns for "should succeed" vs "checking existence." +6. The "Limitations" subsection (L245-249): text/JSON only, keys under 200 chars, values under 5MB, rate-limited, last-write-wins, "always specify shared parameter explicitly." +7. A closing recommendation (L251): "implement proper error handling, show loading indicators and display data progressively…consider adding a reset option." + +The substantive rules are concentrated in (3) and (5): + +**The hierarchical-keys rule (L206):** "Use hierarchical keys under 200 chars: `table_name:record_id` (e.g., 'todos:todo_1', 'users:user_abc')." This is a real engineering pattern — namespace prefix + record id is the standard shape for a flat key-value store. The 200-char cap is a backend constraint; the no-whitespace / no-path-separator / no-quote rule is a constraint from the storage parser. + +**The single-key batching rule (L210):** "Combine data that's updated together in the same operation into single keys to avoid multiple sequential storage calls." This is a real anti-pattern warning: the example reframes `await set('cards'); await set('benefits'); await set('completion')` as `await set('cards-and-benefits', {cards, benefits, completion})`. The motivation is rate-limiting — multiple sequential calls hit the limit; one combined call doesn't. + +**The personal-vs-shared rule (L215-220):** The model is told to use `shared=false` by default and to inform users when their data will be visible to others. The "inform users" rule is a transparency directive tied to the personal/shared toggle. + +**The try-catch rule (L222):** "All storage operations can fail - always use try-catch." This is paired with the asymmetry that `get()` *throws* on missing keys (rather than returning `null`), so the "check if a key exists" pattern requires a try-catch rather than a null-check. This is a real edge case in the API design; the model is told to wrap every call. + +### 1.3 What's missing from Fable's framing + +The `persistent_storage_for_artifacts` section is a **developer API reference**, not a **memory model**. It tells the model (or the artifact author) how to *use* the key-value store; it does not tell the model how to *think about* memory. Specifically absent: + +- **No provenance.** Every key is opaque; the model is not told to record where data came from, which conversation, or which user action. +- **No retention / pruning.** The model is told keys can be deleted, but not told when or why. There is no "delete old entries after N days" rule, no "archive before delete" pattern. +- **No user audit.** The user can `rm`-style delete via the artifact, but the model has no obligation to surface the data to the user. The "consider adding a reset option" (L251) is a recommendation, not a requirement. +- **No concurrency control.** "Last-write-wins for concurrent updates" (L247) is stated as a limitation; the model is not told how to detect or resolve conflicts. +- **No transaction model.** The "combine data updated together" rule (L210) is a workaround for the lack of transactions; it's not framed as such. +- **No typing / schema.** Keys store arbitrary JSON; the model is told to namespace via the key prefix, not via any schema. There is no equivalent of nagent's 7-category schema or Manual Slop's `FileItem` schema. + +### 1.4 Brief cross-ref: `search_instructions` (L436-480) + +The `search_instructions` section is mostly about web search behavior (per cluster 7 scope), but the opening copyright-quote discipline (L444-446) is directly relevant to *this* cluster's research task: + +> "15+ words from any single source is a SEVERE VIOLATION. ONE quote per source MAXIMUM—after one quote, that source is CLOSED. DEFAULT to paraphrasing; quotes should be rare exceptions." (L444-446) + +Fable is telling the model to treat external sources the same way the user's cluster-spec tells the sub-agent to treat Fable: ≤15 words per quote, one quote per source, paraphrase by default. The structural parallel is informative — Fable's own discipline is being applied *to Fable itself* in this report. + +--- + +## 2. What this project does + +Manual Slop does not have a "memory system" in Fable's sense, nor a `window.storage` API. It has **4 memory dimensions**, each with a different shape, scope, and edit surface. The 4-dim model is the canonical reference (`conductor/code_styleguides/agent_memory_dimensions.md:13-18`); the project treats memory as **structured state**, not as opaque key-value blobs. + +### 2.1 The 4 memory dimensions (the canonical model) + +Per `conductor/code_styleguides/agent_memory_dimensions.md:13-18`: + +| Dim | Where it lives | What it stores | How it's edited | SSDL | +|---|---|---|---|---| +| 1 | **Curation** | `FileItem` + `ContextPreset` + Fuzzy Anchors | *How to render a file* | Structural File Editor; project TOML | `[Q]` | +| 2 | **Discussion** | `app.disc_entries` + branching + `UISnapshot` | *What was said* | GUI `[Edit]` mode; `[Branch]`; undo/redo | `o==>` | +| 3 | **RAG** | `src/rag_engine.py` (ChromaDB) | *Semantic fingerprints* | (opaque vector store) | `[Q]` | +| 4 | **Knowledge** | `~/.manual_slop/knowledge/*.md` + per-file + digest + ledger | *Durable learnings* | Plain markdown edit | `o==>` | + +**The 4 dimensions are not interchangeable.** Per `conductor/code_styleguides/agent_memory_dimensions.md:244`: "When designing a new feature, ask: which of the 4 dimensions is the natural home? Don't reach for the RAG because 'it's there'; reach for the dimension whose shape matches the data." + +The decision tree (`conductor/code_styleguides/agent_memory_dimensions.md:264-271`): + +``` +Q: What is the *data* (not the operation) the feature needs? + │ + ├── "How to render a file" ──► Curation (FileItem) + ├── "What was said in this chat" ──► Discussion (disc_entries) + ├── "What similar content exists" ──► RAG (RAGEngine.search) + └── "What we learned from past runs" ──► Knowledge (knowledge/digest.md) +``` + +This is the data-oriented contrast to Fable's "one key-value store, call it memory" framing. Manual Slop's model says: **memory is plural**; the wrong shape for the right question is a common mistake; the 4 dims are the named, distinct, user-editable layers. + +### 2.2 Curation memory (per-file structural) + +**The shape** (`conductor/code_styleguides/agent_memory_dimensions.md:22-66` + `src/models.py:523-559`): + +The `FileItem` dataclass at `src/models.py:523` has 10 fields: + +```python +@dataclass +class FileItem: + path: str + auto_aggregate: bool = True + force_full: bool = False + view_mode: str = 'full' + selected: bool = False + ast_signatures: bool = False + ast_definitions: bool = False + ast_mask: dict[str, str] = field(default_factory=dict) + custom_slices: list[dict] = field(default_factory=list) + injected_at: Optional[float] = None +``` + +The 9 explicit fields are all about **how to render a file** — none are about user-derived facts about the file. `view_mode` selects between full / skeleton / summary / sig / def / agg; `ast_signatures` / `ast_definitions` are AST-aware reductions; `custom_slices` are the Fuzzy Anchor slices (`docs/guide_context_curation.md`). The user's edit surface is the Structural File Editor (the GUI modal that lets the user change `view_mode` per file). + +**The storage shape.** Persisted in `manual_slop.toml` (or a project TOML) as `[[discussion.context_files]]` entries via `FileItem.to_dict()` / `from_dict()` (`src/models.py:550-580`). A `ContextPreset` is a named, persisted set of `FileItem`s (`src/models.py:909-937`). + +**No `notes` field.** Per nagent_review_v2_3 §3.9 (`nagent_review_v2_3_20260612.md:2091`): "Manual Slop equivalent. `models.FileItem` (per `src/models.py:510`) has 9 fields… **No `notes` field.** No per-file knowledge notes dimension." This is the load-bearing gap that cluster 8 will surface — the curation dim is *about rendering*, not *about facts*. Fable's `entries:123` pattern (storing user-derived facts keyed by namespace) has no analog in the curation dim; the closest analog is the **knowledge dim** (4th dim), which is the project's structured answer to "remember things I've learned." + +### 2.3 Discussion memory (per-discussion conversational) + +**The shape** (`docs/guide_discussions.md:31-43`): + +```python +{ + "role": str, # "User" | "AI" | "Vendor API" | "System" | + "content": str, # fully editable in GUI + "collapsed": bool, + "ts": str, # ISO timestamp, prefixed with `@` + "thinking_segments": list[dict], # AI entries with blocks + "usage": dict, # {"input_tokens", "output_tokens", "cache_read_input_tokens"} + "read_mode": bool, # render as Markdown vs editable text +} +``` + +The data is a flat list of entry dicts (`app.disc_entries: list[dict]`). The data model is **open**: extra keys are allowed and ignored by the renderer (`docs/guide_discussions.md:43`). The user can add custom metadata via the Hook API or by editing the project TOML directly. + +**The discussion is the source of truth for "what was said."** Per `conductor/code_styleguides/agent_memory_dimensions.md:124`: "The `disc_entries` list is the single source of truth for 'what was said in this discussion.'" + +**The edit surface.** A1-A7 per-entry operations (`docs/guide_discussions.md:72-86`): edit content, toggle read/edit, collapse/expand, change role, insert, delete, branch. Branching creates a new Take named `_take_`; takes are sibling views of the same conversation, not separate conversations. Per-entry edits are undo-able (`src/history.py:71-141`, `HistoryManager`). + +**The persistence shape** (`docs/guide_discussions.md:202-249`): the discussion persists in the project TOML under `project.discussion.discussions[]["history"]`. The persistence is **explicit** (B4 Save button) and **implicit** (on `_switch_discussion` and `_branch_discussion`). The "context_snapshot" (`disc_data["context_snapshot"]`) records the FileItem list at send time; switching back to a discussion restores the file list. This is the project's answer to "remember which files were in context for this discussion." + +**The data model is precise.** Each entry has a structured role, a timestamp, a collapsed flag, optional thinking segments, and optional usage accounting. The model is *not* a flat text log; it is a list of structured records. Fable's `entries:123 → JSON.stringify(entry)` (L195) pattern is roughly equivalent to one Manual Slop discussion entry (each is a structured record), but Manual Slop's record has 7 explicit fields and is open to extension; Fable's is an opaque JSON blob in a key-value store. + +### 2.4 RAG memory (opt-in semantic) + +**The shape** (`conductor/code_styleguides/agent_memory_dimensions.md:128-170`): + +ChromaDB vector store; per-file `FileItem`-like records with embeddings. `RAGEngine.search(query, k=N)` returns the top-N most-similar chunks. Persisted in `tests/artifacts/.slop_cache/chroma_/`. + +**RAG is opt-in, default-off in new projects.** Per `conductor/code_styleguides/rag_integration_discipline.md` (referenced from `agent_memory_dimensions.md:170`): the discipline is opt-in, complement (never replace), provenance (file path + chunk offset), no mutation, feature-gated, graceful failure. + +**RAG is the wrong shape for "what did we learn from past sessions."** Per `conductor/tracks/nagent_review_20260608/nagent_review_v2_3_20260612.md:631`: RAG is fuzzy, opaque, not auditable, not durable across embedding-provider switches. The knowledge dim is the right shape for durable learnings; RAG is the right shape for semantic search at query time. + +### 2.5 Knowledge memory (per-project durable, provenance-aware) + +**The shape** (`conductor/code_styleguides/agent_memory_dimensions.md:174-226` + `docs/guide_knowledge_curation.md`): + +A markdown tree at `~/.manual_slop/knowledge/`: + +| File | Format | What it stores | +|---|---|---| +| `knowledge/facts.md` | `- {statement} {provenance}` | Durable statements about systems, repos, tools | +| `knowledge/decisions.md` | `- {statement, reason} {provenance}` | Decisions that were made | +| `knowledge/questions.md` | `- {question} {provenance}` | Unanswered questions | +| `knowledge/playbooks.md` | `- **{name}**: {steps} {provenance}` | Reusable command sequences | +| `knowledge/tasks.md` | `- {task}` (## Open / ## Done) | Open and done tasks | +| `knowledge/files/{file_id}.md` | `- {note} {provenance}` | Per-file notes (keyed by inode) | +| `knowledge/digest.md` | bounded 4KB | The projected digest (injected as `{knowledge}` block) | +| `knowledge/ledger.json` | `{entries: {sha256: {status, at, items}}}` | The harvest audit log | + +**The provenance string** is `[from: {conversation_name}, {date}]`. The provenance is appended by the harvest; the user can edit any line. The audit log (`ledger.json`) gates deletion on a proven harvest — the user cannot accidentally delete a conversation whose durable knowledge hasn't been distilled (`docs/guide_knowledge_curation.md:146-182`). + +**The 7-category harvest schema** (`docs/guide_knowledge_curation.md:188-234`): the LLM's harvest output is strict JSON with 7 categories (`facts`, `decisions`, `tasks_done`, `tasks_open`, `questions`, `playbooks`, `files`). The category schema is the load-bearing contract: the LLM cannot return prose, cannot omit categories, cannot invent items ("Empty arrays are valid and expected"). The retry budget is 2 attempts (`docs/guide_knowledge_curation.md:236-255`). + +**The size budgets** (`docs/guide_knowledge_curation.md:258-264`): + +| Constant | Value | Why | +|---|---|---| +| `SUMMARIZE_THRESHOLD_BYTES` | 64 KB | Files > 64KB get summarized first | +| `MAX_HARVEST_SOURCE_BYTES` | 1 MB | Files > 1MB are kept (not harvested) | +| `DIGEST_MAX_BYTES` | 4 KB | The bounded digest size | +| `HARVEST_MAX_ATTEMPTS` | 2 | Retry budget on parse failure | + +The 4KB digest is the projected view injected as the `{knowledge}` block in the initial context (`docs/guide_knowledge_curation.md:323-348`). The bounded digest is the cache-friendly answer to "give me the durable knowledge in 4KB or less." + +**The "delete to turn off" pattern** (`docs/guide_knowledge_curation.md:285-306`): the knowledge digest is gated by file presence. `rm ~/.manual_slop/knowledge/digest.md` → no `{knowledge}` block injected. No env var, no config toggle, no GUI checkbox. The file is the switch. Re-enable by running the harvest, which regenerates the digest. + +### 2.6 The contrast with Fable's `window.storage` + +| Aspect | Fable `window.storage` | Manual Slop | +|---|---|---| +| **Scope** | Per-artifact (each artifact is its own KV store) | Per-project (4 dims, project-scoped) | +| **Schema** | None (opaque JSON) | Typed: `FileItem` (curation), entry dict (discussion), ChromaDB record (RAG), 5 category files (knowledge) | +| **Provenance** | None | `[from: conversation, date]` on every knowledge line; sha256 ledger; inode-keyed per-file notes | +| **Audit** | None | `ledger.json` gates deletion on proven harvest | +| **Retention** | Last-write-wins; no retention policy | Append-only category files; bounded 4KB digest; the harvest reclaim lifecycle | +| **User controls** | "consider adding a reset option" (recommendation) | Plain-text edit of every category file; GUI Knowledge panel; per-file notes; dry-run-by-default harvest | +| **Error handling** | `try/catch` around every call | Result-style failure markers (`harvest-failed`, `too-large`, `deleted-unharvested`) in the ledger; graceful failure + visible marker | +| **Concurrency** | Last-write-wins (acknowledged as limitation) | Append-only merge (no contention); per-thread `threading.local()` for transient state | +| **Memory-as-plural** | One KV store | 4 named dimensions with non-interchangeable shapes | + +The contrast is not just *more features*. The contrast is **shape**. Fable's `window.storage` is a flat key-value namespace with no semantics beyond namespace-prefix conventions. Manual Slop's 4 dims are *named* (curation / discussion / RAG / knowledge), *shaped* (each has a distinct data model), *edited* (each has a distinct user surface), and *queried* (each has a distinct query model). Fable's "use a hierarchical key" pattern is the same shape advice Manual Slop gives, but applied to a single KV store rather than to 4 named dimensions. + +### 2.7 UI history (the unrelated `src/history.py`) + +`src/history.py` defines `UISnapshot` (the UI state for undo/redo), `HistoryEntry`, and `HistoryManager` (the stack-based undo/redo). This is **not** memory in the Fable sense — it is in-memory undo state for the current session. The `UISnapshot` dataclass captures 13 fields (ai_input, project_system_prompt, temperature, disc_entries, files, screenshots, etc.); the `HistoryManager` pushes/pops up to 100 snapshots. The snapshots are not persisted to disk; they are in-process only. + +This is mentioned only to head off confusion: when Fable says "memory system," Manual Slop has *both* a `HistoryManager` (in-process undo) *and* the 4 memory dimensions (persistent storage). They serve different purposes. The in-process undo is not a memory dim; the 4 memory dims are. + +### 2.8 Where the 4 dims land in the cache-friendly context (the 12-layer model) + +The 4 memory dims are not just a static classification; they are *injected* into the LLM context at specific layers of the 12-layer cache-friendly model (per `conductor/code_styleguides/cache_friendly_context.md`): + +| Layer | Content | Which dim? | +|---|---|---| +| 1-6 | role, schema, tools, system prompt, persona, project context | (foundational) | +| **7** | **knowledge digest** | **Knowledge (4th dim)** | +| 8-12 | discussion metadata, active preset, per-file details, prior tool results, user message | **Curation (1st dim)** + **Discussion (2nd dim)** | +| (separate) | `{rag-context}` block (opt-in) | **RAG (3rd dim)** | + +The knowledge digest is the *only* memory dim in the stable cache prefix (layer 7). Per `docs/guide_knowledge_curation.md:326-348`: "The digest is injected into the *stable* position of the initial context (layer 7 of the 12-layer model)… The cache can include the digest in the cached prefix; the volatile suffix is not cached." This is the cache-friendly answer to "give me the durable knowledge in 4KB or less — and let me cache it across turns." + +The curation dim is per-file and lands in the *volatile* suffix (layer 10), because each turn may have different files in scope. The discussion dim is the *user's own prior turns* (layers 8-12) and is per-turn. The RAG dim is a separate `{rag-context}` block injected at LLM call time, opt-in (`src/rag_engine.py`). + +**The contrast with Fable.** Fable's `window.storage` does not specify *where* in the context the stored data appears — the artifact author decides. Manual Slop's 4 dims have fixed injection points: layer 7 (knowledge digest), layer 10 (curation per-file details), volatile suffix (discussion prior turns), and the `{rag-context}` block (RAG). The injection points are part of the data model, not a downstream decision. + +The cache byte-comparison test (`tests/test_aggregate_caching.py`, per `conductor/code_styleguides/cache_friendly_context.md` §2) is the design contract: the first N characters of the context are identical across turns of the same discussion. N is `aggregate.stable_prefix_length(ctrl)`; the knowledge digest is one of the load-bearing contributors to the stable prefix. Fable's `window.storage` has no equivalent — there is no "stable prefix" concept in an artifact's KV store. + +### 2.9 The implementation cross-references (file:line map) + +Per `conductor/code_styleguides/agent_memory_dimensions.md:280-294`, the implementation is mostly present: curation lives in `src/models.py:510-559` (`FileItem`) + `src/context_presets.py` + `src/aggregate.py`; discussion lives in `src/gui_2.py:3770-3853` (A1-A7 render) + `src/history.py:8-71` (`UISnapshot`, `HistoryManager`) + `src/project_manager.py:429+` (branching); RAG lives in `src/rag_engine.py:1-384` (ChromaDB). The knowledge store + harvest CLI are "(proposed)" entries — scoped in Candidate 11 of `nagent_review_v2_3_20260612.md:2098`. Fable's `window.storage` is a runtime API exposed by the Claude.ai browser sandbox; the implementation is the artifact host, not the prompt. Manual Slop's codification names file:line for each dim — the implementation is *in the project's own code*. + +--- + +## 3. What nagent does + +nagent's `knowledge harvest` (`nagent-gc`) is the substantive pattern in this cluster. The harvest is the **3rd memory dimension** in nagent's framing (per `nagent_review_v2_3_20260612.md:552-674`); the project then extends nagent's framing to a **4th dimension** (per-file knowledge notes) at §3.9 (L2022-2105). The two are sibling patterns. + +### 3.1 The knowledge harvest (Pattern 8) — `nagent_review_v2_3_20260612.md:552-674` + +**The claim** (`nagent_review_v2_3_20260612.md:554`): "Dead conversations accumulate, and deleting them loses what was learned. Therefore: distill, then delete — and feed the distillate back in." + +**The components** (`nagent_review_v2_3_20260612.md:556-571`): + +| Component | Where | What it does | +|---|---|---| +| `nagent-gc` | `bin/nagent-gc:1-150` | CLI: classify, estimate cost, harvest, reclaim | +| `run_gc(root, ...)` | `bin/helpers/nagent_gc_lib.py:330+` | Library: dry-run or apply; iterates harvest candidates | +| `scan_root(root)` | `bin/helpers/nagent_gc_lib.py:80+` | Classifies artifacts: `live` / `user-kept` / `prune` / `harvest` / `keep` | +| `harvest_conversation(path, ...)` | `bin/helpers/nagent_gc_lib.py:235+` | For files >64KB, summarize first; otherwise use full text; 2 retries on parse failure | +| `merge_harvest(root, name, harvested, date)` | `bin/helpers/nagent_gc_lib.py:245+` | Appends harvested items to category files with provenance | +| `regenerate_digest(root, max_bytes=4096)` | `bin/helpers/nagent_gc_lib.py:380+` | Rebuilds `digest.md` from category files; sections in fixed order; newest first | +| `load_ledger` / `save_ledger` | `bin/helpers/nagent_gc_lib.py:115-130` | sha256-of-content gate; "already harvested" path reclaims without re-distilling | +| `parse_harvest_json(text)` | `bin/helpers/nagent_gc_lib.py:180+` | Strict JSON parser with code-fence tolerance; validates 7 categories | + +**The 7-category schema** (`nagent_review_v2_3_20260612.md:573-583`): facts / decisions / tasks_done / tasks_open / questions / playbooks / files. Each row is `{statement, detail}` (or `{name, steps}` for playbooks, or `{path, note}` for files). The prompt mandates: "Return only JSON in exactly this form (no prose, no markdown fence)." "Empty arrays are valid and expected: most conversations contain nothing durable. Do not invent items to fill categories." + +**The constants** (`nagent_review_v2_3_20260612.md:585-591`): same 4 budgets as Manual Slop (`SUMMARIZE_THRESHOLD_BYTES = 64KB`, `MAX_HARVEST_SOURCE_BYTES = 1MB`, `DIGEST_MAX_BYTES = 4KB`, `HARVEST_MAX_ATTEMPTS = 2`). The Manual Slop implementation borrows these constants directly (`docs/guide_knowledge_curation.md:258-264`). + +**The classification** (`nagent_review_v2_3_20260612.md:600-611`): + +| Class | Trigger | Action | +|---|---|---| +| `live` | `file-index-*`, `index-saved-conversations-*`, per-file conversations whose target still exists, `latest-*` active conversations | KEEP | +| `user-kept` | Path is in the saved-conversations index | KEEP | +| `harvest` | Per-file conversations whose target is gone; archived conversations; delegated sub-conversations | LLM-DISTILL → append → reclaim | +| `prune` | Split directories with no `index.json`; split directories whose source is gone or hash doesn't match | DELETE | +| `keep` | Anything unclassified | KEEP (default safe) | + +**The digest ordering** (`nagent_review_v2_3_20260612.md:613-614`): sections iterated in `(Open tasks, Open questions, Decisions, Facts, Playbooks)` order; within each section, bullets reversed for newest-first. If all sections empty, the digest is *deleted* (the "delete to turn off" pattern). + +### 3.2 The per-file knowledge notes (sub-pattern) — `nagent_review_v2_3_20260612.md:2022-2105` + +**The claim** (`nagent_review_v2_3_20260612.md:2024`): "When you know things about a specific file, those notes should live next to the file's identity (inode), not next to a conversation or a session. Then, the next time the file is in scope, the notes come back automatically." + +**The implementation** (the `merge_harvest` "files" branch, `nagent_review_v2_3_20260612.md:2028-2054`): + +```python +for row in harvested.get("files", []): + if not isinstance(row, dict): + continue + path_text = str(row.get("path") or "").strip() + note = str(row.get("note") or "").strip() + if not note: + continue + target = Path(path_text) if path_text else None + if target is not None and target.is_file(): + try: + file_id = file_id_for_path(target) + except OSError: + file_id = None + if file_id is not None: + _append_bullets( + file_knowledge_path(root, file_id), f"# {target.resolve()}", + [f"{note} {provenance}"], + ) + file_notes += 1 + continue + # Target no longer resolvable: the note survives as a fact. + prefix = f"{path_text}: " if path_text else "" + _append_bullets(knowledge / "facts.md", "# Facts", [f"{prefix}{note} {provenance}"]) + file_notes += 1 +``` + +**The fallback** (`nagent_review_v2_3_20260612.md:2051-2053`): "Target no longer resolvable: the note survives as a fact." The note's path-prefix (`{path}: `) is preserved as a prefix on the fallback fact; the per-file binding is lost but the note survives. + +**The injection point** (`nagent_review_v2_3_20260612.md:2509-2515`): per-file knowledge is injected as part of the file-edit block, in the stable position. When a file is in scope for editing, its knowledge comes back automatically. + +**The verdict for Manual Slop** (`nagent_review_v2_3_20260612.md:2091-2098`): + +> "Manual Slop equivalent. `models.FileItem` (per `src/models.py:510`) has 9 fields: `path, auto_aggregate, force_full, view_mode, selected, ast_signatures, ast_definitions, ast_mask, custom_slices`. **No `notes` field.** No per-file knowledge notes dimension." + +> "Verdict. **GAP.** The per-file notes dimension is absent in Manual Slop. `FileItem` would need a `notes: str = ""` field; the Structural File Editor would need a 'Notes' text area; `aggregate.py:run` would need a `{file-knowledge}` block in the initial context." + +The gap is precisely named. The Manual Slop candidate list includes "Candidate 11.1: per-file knowledge notes — bundle with Candidate 11" (`nagent_review_v2_3_20260612.md:2098`). + +### 3.3 The 4-dim framing in nagent_review_v2_3 + +The v2.3 review explicitly frames the project in terms of the 4 memory dims: + +> "The 4 memory dimensions (the framing):" (`nagent_review_v2_3_20260612.md:4198`) + +The surrounding context (the section header at `nagent_review_v2_3_20260612.md:4187-4202`) is the project's design intent: curation (FileItem) and discussion (disc_entries) are present and strong; RAG is opt-in and is the wrong shape for durable knowledge; knowledge is the missing dim. The Manual Slop codification of the 4 dims (`conductor/code_styleguides/agent_memory_dimensions.md`, `docs/guide_agent_memory_dimensions.md`, `docs/guide_knowledge_curation.md`) is the direct response to nagent's framing — Manual Slop adopts the 4-dim model and adds the knowledge dim, with the digest bounded to 4KB and the harvest pipeline implemented. + +**The note on the spec's section reference.** The track spec (`fable_review_20260617/spec.md:222`) cites nagent §2.1 for "4 memory dimensions." In v2.3 the §2.1 slot is "Pattern 1: Text In, Text Out" (`nagent_review_v2_3_20260612.md:242`); the 4-dim framing moved to §2.8 (Pattern 8: Harvest Knowledge, Reclaim Space) in the v2.3 restructure. The §3.9 reference for per-file knowledge notes is correct in v2.3 (`nagent_review_v2_3_20260612.md:2022`). The substance is unchanged across versions — the v2.1/v2.2 §2.1 is the same content as v2.3 §2.8. Cluster 8 cites v2.3 throughout. + +### 3.4 What Manual Slop adopted from nagent (the load-bearing adoption) + +The Manual Slop codification is not just *inspired by* nagent — it adopts specific patterns and constants directly: + +**The 4 size budgets** are identical (`docs/guide_knowledge_curation.md:258-264` + `nagent_review_v2_3_20260612.md:585-591`): `SUMMARIZE_THRESHOLD_BYTES = 64KB`, `MAX_HARVEST_SOURCE_BYTES = 1MB`, `DIGEST_MAX_BYTES = 4KB`, `HARVEST_MAX_ATTEMPTS = 2`. + +**The 7-category schema** is identical: facts / decisions / tasks_done / tasks_open / questions / playbooks / files. Same shape, same JSON contract, same code-fence tolerance. + +**The retry-suffix pattern** is identical: on retry, append `\nYour previous reply was not valid JSON. Return only the JSON object.\n` to the prompt (`docs/guide_knowledge_curation.md:255`). + +**The provenance format** is identical: `[from: {conversation_name}, {date}]` (`docs/guide_knowledge_curation.md:42`). + +**The "delete to turn off" pattern** is identical: `rm ~/.manual_slop/knowledge/digest.md` → no `{knowledge}` block injected (`docs/guide_knowledge_curation.md:289`). + +**The digest section ordering** is identical: Open tasks, Open questions, Decisions, Facts, Playbooks; within each section, bullets reversed for newest-first (`docs/guide_knowledge_curation.md:137`). + +**The "graceful failure" markers** are identical: `harvest-failed`, `too-large`, `deleted-unharvested` (`docs/guide_knowledge_curation.md:178-181`). + +**The per-file notes pattern** is adopted but not yet implemented: the 4 Manual Slop docs describe the pattern, but `models.FileItem` does not yet have a `notes` field. The implementation is the deferred Candidate 11.1. + +**The dry-run-by-default safety** is the same pattern (`docs/guide_knowledge_curation.md:266-281`): without `--apply`, the CLI classifies, estimates cost, and prints a report. No mutation. + +The adoption is not a 1:1 port. Manual Slop adapts the pattern for its 4-dim model (curation is its own dim, not a "files" category sub-bucket) and for the project's data-oriented conventions (`Result[T]` + `ErrorInfo` instead of exceptions). But the constants, schema, retry pattern, provenance format, section ordering, delete-to-turn-off pattern, and graceful-failure markers are direct ports. nagent's harvest library is the source; Manual Slop's 4 canonical docs are the target. + +--- + +## 4. Verdict + +**Useful + nagent-stronger.** Fable's `window.storage` API + the hierarchical-keys pattern + the single-key-batching rule + the personal-vs-shared scoping + the try-catch-everything rule are genuinely useful engineering guidance. They are the *table-stakes* of any key-value client library: namespace your keys, batch your writes, distinguish personal vs shared scope, handle errors. None of these patterns are Fable's invention; they are the standard pattern for the API surface Fable exposes. + +But Fable's framing is **memory-as-blob-store**: one key-value namespace, opaque JSON, no provenance, no retention, no audit, no schema. Manual Slop's 4 memory dimensions (curation / discussion / RAG / knowledge) are the **stronger, more grounded** version of Fable's "memory" framing. Each dim has a named shape, a user-editable surface, a query model, and (for knowledge) a provenance-aware harvest pipeline with an audit ledger. Fable's 5-line `memory_system` section is a product toggle; Manual Slop's `agent_memory_dimensions.md` is a 306-line canonical styleguide with a decision tree. + +nagent's knowledge harvest + per-file knowledge notes is **the strong version of Fable's "memory" framing**. The 7-category schema, the `[from: conversation, date]` provenance, the sha256-of-content ledger, the 4KB bounded digest, the per-file notes keyed by inode — these are the load-bearing patterns that turn a key-value blob into a *durable memory system*. nagent implements them; the project adopts them. + +### 4.1 Pattern-by-pattern judgment + +**Pattern 1: Hierarchical keys under 200 chars (L206).** **Useful.** This is a real engineering pattern (namespace prefix + record id); the 200-char cap is a backend constraint; the no-whitespace / no-slash / no-quote rule is the parser constraint. Manual Slop's analog is implicit: the `app.disc_entries` list uses index-based addressing; `FileItem` is keyed by path; `knowledge/files/{file_id}.md` is keyed by inode. None of these are flat key-value, but the *underlying principle* (each memory cell has a structured key) is the same. Recommend: document this principle in the project's memory dim styleguide (it already exists in the per-dim "where it lives" column; no new spec needed). + +**Pattern 2: Single-key batching to avoid rate limits (L210).** **Useful.** The example reframes `await set('cards'); await set('benefits'); await set('completion')` as `await set('cards-and-benefits', {cards, benefits, completion})`. This is a rate-limit-driven batching pattern; Manual Slop's analog is the digest: the knowledge dim batches *all 7 categories* into a single 4KB `digest.md` file rather than emitting 7 separate `set` calls. Recommend: no action — Manual Slop already batches. + +**Pattern 3: Personal vs shared data scope (L215-220).** **Useful + Manual Slop-lacking.** The personal/shared distinction is a real product feature; the "inform users when data is visible to others" transparency rule is a good safety practice. Manual Slop has no analog: the knowledge dim is single-user (per-machine, `~/.manual_slop/knowledge/`); the curation dim is per-project (in the project TOML); the discussion dim is per-discussion (in the project TOML). There is no shared-storage concept. Recommend: note as out-of-scope — Manual Slop is a single-user tool; shared storage would be a feature add, not a "memory model" improvement. + +**Pattern 4: try/catch around every storage call (L222).** **Useful + Manual Slop-different.** Fable's try/catch is the standard JS error-handling pattern; Manual Slop's convention is the data-oriented `Result[T]` + `ErrorInfo` dataclass pattern (`conductor/code_styleguides/error_handling.md`). The harvest pipeline uses 4 result markers (`harvested` / `harvest-failed` / `deleted-unharvested` / `too-large`) in `ledger.json` rather than exceptions (`docs/guide_knowledge_curation.md:178-181`). Recommend: no action — the project's convention is the data-oriented one, which is the stronger pattern. + +**Pattern 5: "Claude has a memory system which provides Claude with access to derived information (memories) from past conversations" (L168).** **Useful (the concept) + nagent-stronger (the implementation).** The *concept* of a memory system that derives facts from past conversations is the right product framing. The *implementation* is opaque ("derived information") and has no provenance, no audit, no schema. nagent's knowledge harvest + Manual Slop's knowledge dim are the strong versions: schema (7 categories), provenance (`[from: conversation, date]`), audit (`ledger.json`), retention (4KB digest with truncation marker). Recommend: explicitly reject Fable's "one opaque memory feature" framing; cite nagent + Manual Slop's structured 4-dim model as the alternative. + +**Pattern 6: "No `notes` field on FileItem" (the gap).** **GAP per nagent §3.9.** The project has the 4-dim framing but lacks the per-file notes dimension within the knowledge dim. The fix is named in `nagent_review_v2_3_20260612.md:2096-2098`: add `notes: str = ""` to `FileItem`, add a "Notes" text area to the Structural File Editor, add a `{file-knowledge}` block to `aggregate.py:run`. This is Candidate 11.1 in the nagent review's deferred-rebuild list. Recommend: include in `decisions.md` as a deferred-rebuild recommendation. + +### 4.2 What to reject + +- **The "one opaque KV store = memory" framing.** Fable's `window.storage` is a *storage API*, not a *memory model*. Treating it as a memory model collapses 4 distinct dimensions (curation / discussion / RAG / knowledge) into one flat namespace with no shape. The project should explicitly reject this framing. +- **The "user enables memory in Settings" toggle as a memory model.** Fable's `memory_system` is a 5-line product disclosure, not a memory architecture. The project should not import the toggle framing. +- **The "no schema, namespace via key prefix" pattern.** Keys like `entries:123` are namespace-by-convention, not namespace-by-type. The project's 4-dim model has named types (FileItem, disc_entry, ChromaDB record, knowledge bullet); the Fable pattern has no types. The project should not import the untyped-namespace pattern. + +### 4.3 What to keep + +- **The hierarchical-keys principle** (each memory cell has a structured key) — already implicit in Manual Slop's per-dim shapes. +- **The personal-vs-shared scope distinction** — out-of-scope for Manual Slop (single-user tool), but the principle is sound. +- **The error-handling discipline** — already implemented as `Result[T]` + `ErrorInfo` + ledger status markers. +- **The "consider adding a reset option" transparency** — already implemented as the "delete to turn off" pattern (`docs/guide_knowledge_curation.md:285-306`). + +### 4.4 What to add (deferred-rebuild candidate) + +- **Per-file knowledge notes (Candidate 11.1).** The 4-dim model is incomplete without the per-file notes dimension. The fix is small (add `notes` field + GUI text area + `{file-knowledge}` injection block) but the value is high (durable facts about specific files survive across sessions). Flag in `decisions.md`. + +--- + +## 5. Synthesis notes for the Tier 1 writer + +This cluster feeds `report.md` §10 ("Fable's Memory System & Persistent Storage") directly. Cross-references to §13 ("Genuinely Useful Patterns") and §14 ("Anti-User Watchdog Patterns"). The verdict orientation is **Useful + nagent-stronger** (per `fable_review_20260617/spec.md:182`). + +### 5.1 Key claims to surface in §10 + +1. **Fable's `window.storage` is a useful API reference, not a memory model.** The 4 API methods, the hierarchical-keys rule, the single-key batching, the personal-vs-shared scope, and the try/catch discipline are all genuinely good engineering guidance. None of them are Fable's invention; they are the standard pattern for a key-value client library. Cite L181-184 (API methods), L206-211 (key design), L215-220 (data scope), L222-241 (error handling). + +2. **Fable's `memory_system` is a 5-line product disclosure, not a memory architecture.** L168 and L170 are a setting toggle and a transparency statement, not an implementation. The "derived information" hedge is load-bearing: Fable admits the memories are extracted facts but does not describe the extraction, the audit, the retention, or the user controls. The contrast is Manual Slop's 306-line canonical styleguide + the 358-line user-facing guide + the 4-dim model with decision tree. + +3. **Manual Slop's 4 memory dimensions are the strong version of Fable's "memory" framing.** Each dim has a named shape, a user-editable surface, a query model, and (for knowledge) a provenance-aware harvest pipeline with an audit ledger. Cite `conductor/code_styleguides/agent_memory_dimensions.md:13-18` (the table) + `agent_memory_dimensions.md:244-272` (the boundaries + decision tree). + +4. **nagent's knowledge harvest is the strong version of Fable's "memory" framing.** The 7-category schema, the `[from: conversation, date]` provenance, the sha256-of-content ledger, the 4KB bounded digest, the per-file notes keyed by inode — these are the load-bearing patterns that turn a key-value blob into a durable memory system. Cite `nagent_review_v2_3_20260612.md:552-674` (Pattern 8) + `nagent_review_v2_3_20260612.md:2022-2105` (per-file notes §3.9). + +5. **The per-file notes dimension is the named GAP.** Per `nagent_review_v2_3_20260612.md:2091-2098`: FileItem has 9 fields, no `notes`. The fix is Candidate 11.1 in the nagent deferred-rebuild list. Cite explicitly as a deferred-rebuild recommendation. + +6. **The data-oriented contrast.** Manual Slop's `Result[T]` + `ErrorInfo` + ledger status markers (`harvested` / `harvest-failed` / `deleted-unharvested` / `too-large`) are the data-grounded alternative to Fable's `try/catch` pattern. The harvest pipeline's failure modes are encoded in `ledger.json`, not raised as exceptions. Cite `conductor/code_styleguides/error_handling.md` + `docs/guide_knowledge_curation.md:178-181` (the ledger status values) + `docs/guide_knowledge_curation.md:308-320` (the graceful failure modes). + +### 5.2 Quotes to use in §10 + +- Fable L168: "Claude has a memory system which provides Claude with access to derived information (memories) from past conversations with the user" (≤15 words paraphrased; full quote exceeds) +- Fable L170: "Claude has no memories of the user because the user has not enabled Claude's memory in Settings" (full quote, 15 words) +- Fable L181: "await window.storage.get(key, shared?) - Retrieve a value → {key, value, shared} | null" (paraphrase) +- Fable L206: "Use hierarchical keys under 200 chars: table_name:record_id" (12 words) +- Fable L210: "Combine data that's updated together in the same operation into single keys" (12 words) +- Fable L215: "Personal data (shared: false, default): Only accessible by the current user" (10 words) +- Fable L222: "All storage operations can fail - always use try-catch" (8 words) +- `conductor/code_styleguides/agent_memory_dimensions.md:13`: "Curation | FileItem + ContextPreset + Fuzzy Anchors | How to render a file in the AI's context window" (paraphrase; the table) +- `conductor/code_styleguides/agent_memory_dimensions.md:244`: "When designing a new feature, ask: which of the 4 dimensions is the natural home?" (16 words) +- `docs/guide_knowledge_curation.md:13`: "The LLM harvests past discussions into these files; the user can edit any of them in plain text" (paraphrase) +- `docs/guide_knowledge_curation.md:285-286`: "Feature flags should be data, not config. If a feature is gated by the presence of a file, the user can turn it off by deleting the file" (28 words → split into 2 quotes) +- `docs/guide_knowledge_curation.md:289`: "rm ~/.manual_slop/knowledge/digest.md → no {knowledge} block injected" (paraphrase) +- `nagent_review_v2_3_20260612.md:554`: "Dead conversations accumulate, and deleting them loses what was learned. Therefore: distill, then delete" (paraphrase) +- `nagent_review_v2_3_20260612.md:2024`: "When you know things about a specific file, those notes should live next to the file's identity (inode)" (paraphrase) +- `nagent_review_v2_3_20260612.md:2096`: "No `notes` field. No per-file knowledge notes dimension" (paraphrase of the GAP verdict) + +### 5.3 The §13 / §14 / §15 cross-references + +- **§13 ("Genuinely Useful Patterns").** The hierarchical-keys principle (each memory cell has a structured key) + the personal-vs-shared scope distinction + the error-handling discipline are all genuinely useful. Cite L206 (keys), L215 (scope), L222 (errors). Note that Manual Slop already implements each in the project's own conventions (per-dim shapes, single-user scope, `Result[T]` + ledger markers). The useful pattern is *the principle*, not the Fable framing. +- **§14 ("Anti-User Watchdog Patterns").** The "memory is a Settings toggle" framing (L170) is *not* anti-user in itself — it's a transparency disclosure. But the *combination* of "Claude has a memory system" (L168) + "user has not enabled" (L170) + "consider adding a reset option" (L251, recommendation not requirement) constructs the memory system as opaque + non-user-controlled + lightly-suggested-to-be-resettable. The user can't see what's in memory, can't audit, can't selectively delete. This is anti-user in the *transparency* sense (not the *safety* sense). Recommend: cite as a transparency gap, contrast with the project's `ledger.json` + plain-text-edit + `delete to turn off` pattern. +- **§15 ("Persona Performance Patterns").** None of cluster 8 is persona performance. The `memory_system` section is a product disclosure; the `persistent_storage_for_artifacts` section is an API reference. Neither constructs a persona. Cluster 8 does not feed §15. + +### 5.4 The data-oriented error handling parallel + +Fable's `try/catch` rule (L222) is the JS-idiomatic error handling; Manual Slop's `Result[T]` + `ErrorInfo` + ledger status markers is the data-oriented equivalent. The harvest pipeline uses 4 status markers (`harvested` / `harvest-failed` / `deleted-unharvested` / `too-large`) in `ledger.json` rather than exceptions (`docs/guide_knowledge_curation.md:178-181`). The graceful failure modes table (`docs/guide_knowledge_curation.md:308-320`) lists 6 failure scenarios and their handling, all encoded as data, not control flow. + +The synthesis report should surface this parallel in §10: Fable's storage error handling is persona-free (no "Claude feels bad about the storage failure"); Manual Slop's storage error handling is data-only (status markers, ledger entries, visible UI panels). The contrast is not "Fable has errors, Manual Slop doesn't" — it's "Fable uses control flow, Manual Slop uses data." + +### 5.5 The "memory is plural" framing for the synthesis report's TL;DR + +The single most important claim from cluster 8 is that **memory is plural, not singular**. Fable's framing is "the memory system" (singular, opaque, toggle-controlled). Manual Slop's framing is "the 4 memory dimensions" (plural, named, shaped, user-editable). nagent's framing is "the harvest + the per-file notes" (2 named sub-systems). The synthesis report's §0 TL;DR should surface this distinction as the headline: Fable's `memory_system` section is 5 lines; Manual Slop's 4-dim model is 4 named styleguides (306 + 358 + 278 + canonical knowledge_artifacts.md lines), each with a decision tree, a query model, and a user-editable surface. + +### 5.6 What the §10 verdict should be + +**Verdict: Useful (the API surface) + nagent-stronger (the memory architecture).** Fable's `window.storage` API is a useful engineering reference; the hierarchical-keys + single-key-batching + personal-vs-shared + try/catch rules are the standard pattern for a key-value client library. Manual Slop already implements each in its own conventions (per-dim shapes, digest batching, single-user scope, `Result[T]` + ledger). Fable's `memory_system` section is a product disclosure, not a memory architecture; nagent's knowledge harvest + per-file notes + Manual Slop's knowledge dim are the strong versions of the "memory" framing. The named gap is the per-file notes dimension (Candidate 11.1 per nagent §3.9). + +**The recommended Manual Slop action:** +1. Cite the hierarchical-keys + batching principles in the memory dim styleguide as already-implemented (no change). +2. Cite the personal-vs-shared scope distinction as out-of-scope (single-user tool; no action). +3. Cite the data-oriented error handling contrast (`Result[T]` + ledger markers) in the §10 verdict. +4. Flag the per-file notes dimension (Candidate 11.1) as a deferred-rebuild recommendation in `decisions.md`. +5. Explicitly reject Fable's "one opaque KV store = memory" framing; cite the 4-dim model + the knowledge harvest as the alternative. + +### 5.7 The deferred-rebuild recommendation (for `decisions.md`) + +**Recommendation R8.1: Implement Candidate 11.1 (per-file knowledge notes).** + +- **Source evidence.** `nagent_review_v2_3_20260612.md:2091-2098` (the named GAP verdict); `nagent_review_v2_3_20260612.md:2022-2105` (§3.9 the per-file notes pattern); `nagent_review_v2_3_20260612.md:2492-2515` (§4.4 the per-file notes sub-pattern). +- **What to build.** Add `notes: str = ""` to `FileItem` (`src/models.py:523`); add a "Notes" text area to the Structural File Editor (`docs/guide_context_curation.md`); add a `{file-knowledge}` block to `aggregate.py:run` at the file-edit position (per `nagent_review_v2_3_20260612.md:2509-2515`). +- **Why.** The 4-dim model is incomplete without per-file notes. The fix is small (3 sites, ~50 lines) but the value is high: durable facts about specific files survive across sessions; the notes come back automatically when the file is in scope; the notes are keyed by inode so they survive renames within the same filesystem. +- **Priority.** LOW standalone (small, niche) per `nagent_review_v2_3_20260612.md:2098` — bundle with the main knowledge dim implementation (Candidate 11). +- **Destination.** `conductor/code_styleguides/knowledge_artifacts.md` §? (extend the existing canonical styleguide) + `docs/guide_knowledge_curation.md` §2 (extend the existing per-file notes section). + +**Recommendation R8.2: Document the "memory is plural" framing in the agent-directive corpus.** + +- **Source evidence.** This cluster's §5.5 ("memory is plural, not singular"); Fable L168 ("Claude has a memory system") vs Manual Slop's 4-dim model (`conductor/code_styleguides/agent_memory_dimensions.md:13-18`). +- **What to build.** Add a 1-paragraph "memory is plural" callout to `AGENTS.md` (the top-level agent-facing rules) and to `conductor/product-guidelines.md` §"AI-Optimized Compact Style". The callout: "Manual Slop has 4 memory dimensions, not 1. The dimensions are not interchangeable. Fable-style 'one memory feature' framing collapses 4 distinct shapes into 1 opaque KV store." +- **Why.** The 4-dim model is the project's design intent; the Fable framing is a competing model. The agent-directive corpus should explicitly reject the Fable framing. +- **Priority.** LOW (documentation-only). +- **Destination.** `AGENTS.md` "Critical Anti-Patterns" or "Code Standards & Architecture" section + `conductor/product-guidelines.md` "AI-Optimized Compact Style" section. + +### 5.8 The relationship to cluster 7 (search_instructions) + +Cluster 7 owns the `search_instructions` copyright-quote discipline (L444-446). Cluster 8 references it as a cross-cut but does not feed §10 from it. + +--- + +**Sub-report complete.** This is the evidence base for §10 of `report.md`. \ No newline at end of file