@yemi33/minions 0.1.2401 → 0.1.2403

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,164 +1,482 @@
1
- # Team Memory
2
-
3
- Minions agents share knowledge through a layered Markdown system plus a structured SQLite retrieval index. Before an agent does any independent research — web searches, broad codebase exploration, external doc reading — it is expected to check what the team already knows.
4
-
5
- This document describes the four-tier read order the engine enforces in dispatch prompts, the rationale, and the constraint that `knowledge/` is **sweep-write-only**.
6
-
7
- ## Why team memory exists
8
-
9
- Multiple agents work the same repository in parallel and across days. Without shared memory:
10
-
11
- - Agents re-research the same questions (cost, time, context churn).
12
- - Agents re-litigate decisions the team already made (drift, regressions).
13
- - Human-flagged warnings (e.g., "do not self-approve PRs") get ignored because they live outside the prompt.
14
-
15
- Team memory closes the loop. The engine always injects pinned context. By default, structured retrieval replaces broad team and personal notes with a bounded task-relevant memory pack. Playbooks still instruct agents to consult `knowledge/` and `notes/inbox/` on disk before going wider.
16
-
17
- ## Structured memory and retrieval
18
-
19
- Migration `017-agent-memory.js` backfills the existing Markdown sources into `memory_records` and an FTS5 index without deleting or rewriting those files. Records carry a type (`semantic`, `episodic`, or `procedural`), scope, trust level, source reference, confidence, importance, validity timestamps, and lifecycle status (`active`, `superseded`, `retracted`, or `expired`).
20
-
21
- `engine/memory-retrieval.js` searches active records in global, current-project, and assigned-agent scopes. It reranks lexical matches by scope, trust, path match, recency, importance, and confidence, then packs the best records into a strict byte budget. Agent- or external-authored procedural records are never recalled as instructions. Every injected pack remains inside an `<UNTRUSTED-INPUT>` fence.
22
-
23
- Structured retrieval is controlled by the default-on `memoryRetrieval` feature flag:
24
-
25
- - **Flag off:** legacy prompt behavior.
26
- - **Flag on + shadow mode off (default):** inject `Relevant Memory` when retrieval finds evidence; otherwise fall back to legacy notes and personal memory.
27
- - **Flag on + `memoryRetrievalShadowMode: true`:** compute retrieval and telemetry, but keep legacy prompts.
28
-
29
- Settings expose the retrieval bounds directly:
30
-
31
- | `engine.*` key | Default | Settings range | Purpose |
32
- |----------------|---------|----------------|---------|
33
- | `memoryRetrievalTopK` | `8` | 1–30 records | Maximum records injected into one dispatch |
34
- | `memoryRetrievalMaxBytes` | `8192` | 1024–65536 bytes | Hard byte budget for the `Relevant Memory` appendix |
35
- | `memoryRetrievalCandidateLimit` | `50` | 5–200 records | FTS5 candidate pool before deterministic reranking |
36
-
37
- The same pane controls shadow mode and episodic capture. The Inbox page's **Memory search** control shows provenance and status and supports explicit pin, retract, and restore actions.
38
-
39
- ## The four-tier read order
40
-
41
- Every agent prompt (built by `engine/playbook.js`) is rendered with the rule: **check team memory first, then look outside.** The canonical read order (from `playbooks/shared-rules.md`) is:
42
-
43
- 1. **`pinned.md`** — critical context flagged by the human teammate. **READ FIRST.**
44
- 2. **`knowledge/`** — categorized KB entries written by the consolidation sweep.
45
- 3. **`notes.md`** — consolidated team knowledge, decisions, and rolling context.
46
- 4. **`notes/inbox/`** recent agent findings not yet consolidated.
47
-
48
- Agents are also encouraged, after the four primary tiers, to skim `agents/*/live-output.log` for related tasks and the `resultSummary` of prior completed work items on the same topic. Only after exhausting team memory should an agent fall back to web search, broad codebase exploration, or external docs.
49
-
50
- ### 1. `pinned.md` — human-flagged critical context
51
-
52
- `pinned.md` lives at the Minions root (`MINIONS_DIR/pinned.md`). It holds notes the human teammate has explicitly pinned through the dashboard — short, high-signal rules and warnings that **must** be visible to every agent on every dispatch.
53
-
54
- The engine injects the file verbatim into the prompt under a "Pinned Context (CRITICAL — READ FIRST)" heading and caps it at 4 KB to protect the prompt budget.
55
-
56
- Typical contents include credential constraints (e.g., shared `gh` auth means PR self-approval is blocked), workflow policies (e.g., merging policy, TDD requirement), and active setup state. If pinned context contradicts a playbook instruction, pinned context wins for the duration of the dispatch.
57
-
58
- ### 2. `knowledge/` — categorized KB entries (sweep-write-only)
59
-
60
- `knowledge/` is the long-form team knowledge base. Files are grouped by category:
61
-
62
- - `knowledge/architecture/` — design decisions, system overviews
63
- - `knowledge/conventions/` — coding patterns, repo norms, standards
64
- - `knowledge/project-notes/` — per-project context and observations
65
- - `knowledge/build-reports/` — CI/build investigation results
66
- - `knowledge/reviews/` — PR review findings and rationale
67
- - `knowledge/agents/<agentId>.md` — per-agent personal memory (curated by the sweep)
68
-
69
- Filenames follow the `YYYY-MM-DD-<agent>-<slug>.md` convention so chronological sorting just works.
70
-
71
- Agents read `knowledge/` directly with grep/glob/view. They **do not** write to it. See [Sweep-write-only constraint](#sweep-write-only-constraint) below.
72
-
73
- **Full-body copies vs. condensed stubs.** When `engine/consolidation.js#classifyToKnowledgeBase` promotes an inbox note into `knowledge/`, it only copies the full body when the note is routed to `conventions/` or its content carries a reusable-knowledge section header (`## Patterns`, `## Conventions`, `## Gotchas`, `## Dependencies`, `## Best Practices`, etc.). Narrowly-scoped, one-off notes (a single PR review, a task status update) instead get a deterministic, LLM-free condensed stub — title + first-paragraph abstract + a link back to the archived inbox note — to avoid duplicating disk usage and prompt-token cost on every future dispatch. Every promoted entry's frontmatter gains a `reusable: true|false` field so this can be audited (issue #604).
74
-
75
- ### 3. `notes.md` — consolidated team notes
76
-
77
- `notes.md` is the rolling team notebook. The consolidation sweep periodically merges high-signal findings from the inbox into dated `### YYYY-MM-DD` sections at the top of `notes.md`, with links back to the underlying KB entries.
78
-
79
- In legacy and shadow modes, the engine injects `notes.md` under a "Team Notes (MUST READ)" heading. When active retrieval returns evidence, the bounded `Relevant Memory` pack replaces this broad appendix. When the file exceeds `ENGINE_DEFAULTS.maxNotesPromptBytes`, the legacy path keeps the most recent 10 sections, truncates the body, and appends a footer noting how many older sections live on disk.
80
-
81
- ### 4. `notes/inbox/` — fresh agent findings
82
-
83
- `notes/inbox/` holds the freshest, un-consolidated learnings. Every agent writes exactly one inbox file at the end of a successful task to a path the playbook supplies (`notes/inbox/<agent>-<work-item-id>-<date>-<time>.md`). The file always begins with a YAML frontmatter block (`id`, `agent`, `date`) so the consolidation sweep can track it.
84
-
85
- Inbox notes are the canonical place to record:
86
-
87
- - New patterns or conventions discovered while implementing.
88
- - Gotchas, warnings, or repro recipes for future agents.
89
- - File-and-line citations supporting any claims.
90
-
91
- The consolidation sweep is what eventually promotes inbox notes into `knowledge/` and `notes.md`. Until then, agents should grep the inbox themselves for very recent findings on adjacent work.
92
-
93
- #### Failure-path exception
94
-
95
- Inbox writes are **success artifacts only**. If a task fails, is blocked, is cancelled, or ends partial, the agent must **not** create an inbox note. When `memoryEpisodicCapture` is enabled, lifecycle records a compact structured outcome for successful, partial, and failed dispatches; it never stores transcripts or chain-of-thought and skips nonce-mismatched or injection-flagged reports.
1
+ # Minions Memory System
2
+
3
+ Minions long-term memory is a hybrid system:
4
+
5
+ - Markdown files preserve operator-readable context, the inbox workflow, and the
6
+ legacy prompt path.
7
+ - SQLite records provide scoped lifecycle state, FTS5 search, deterministic
8
+ task-aware retrieval, and retrieval telemetry.
9
+
10
+ Neither layer replaces the other. Active retrieval reads structured records from
11
+ SQLite; pinned context and the legacy fallback still read Markdown directly.
12
+ This document covers knowledge memory, not the engine/dashboard process-memory
13
+ diagnostics described in [diagnostics-memory.md](diagnostics-memory.md).
14
+
15
+ ## End-to-end data flow
16
+
17
+ ```text
18
+ Human Quick Note / agent finding / engine note
19
+ |
20
+ v
21
+ notes/inbox/*.md
22
+ |
23
+ engine tick: consolidateInbox() (threshold-gated)
24
+ |
25
+ +------------+-------------+----------------+
26
+ | | |
27
+ v v v
28
+ notes.md knowledge/<category>/ knowledge/agents/
29
+ team digest readable KB entry <agent>.md
30
+ | | |
31
+ +--------------------------+----------------+
32
+ |
33
+ v
34
+ memory_records + FTS5
35
+ (semantic SQL records)
36
+
37
+ Human pinned note ------------------------------> pinned.md
38
+ Human direct KB authoring ---------> knowledge/ + memory_records
39
+ Dispatch completion (opt-in) -------------------> memory_records
40
+ (episodic record)
41
+
42
+ At the next dispatch:
43
+
44
+ pinned.md
45
+ +
46
+ active retrieval: task query -> scoped FTS5 -> rerank -> bounded Relevant Memory
47
+ OR
48
+ legacy fallback: bounded notes.md + bounded knowledge/agents/<agent>.md
49
+ ```
96
50
 
97
- ## Rationale
51
+ The playbook also tells agents to inspect `knowledge/` and `notes/inbox/` on
52
+ disk. Those directories are not injected wholesale into every prompt.
53
+
54
+ ## Storage and authority
55
+
56
+ There is no single store that is authoritative for every kind of memory.
57
+ Authority is divided by responsibility:
58
+
59
+ | Store | Authoritative for | Notes |
60
+ |-------|-------------------|-------|
61
+ | `engine/state.db` tables `memory_records` and `memory_retrieval_runs` | Structured record bodies and metadata used by active retrieval; record lifecycle (`active`, `superseded`, `retracted`, `expired`); retrieval telemetry | `memory_records_fts` is a derived external-content FTS5 index maintained by SQLite triggers. |
62
+ | `pinned.md` | Operator-authored pinned context | Read directly for every rendered agent prompt. It is not a structured-memory pin. |
63
+ | `notes/inbox/*.md` | Pending human, agent, and engine-authored findings awaiting consolidation | Successful agent tasks write one note to the assigned path; failures do not. |
64
+ | `notes.md` | Operator-readable team digest and legacy team-notes prompt input | Consolidation appends here under a file lock. |
65
+ | `knowledge/<category>/*.md` | Curated, browsable knowledge-base content | Written by engine-managed consolidation/sweep paths and explicit human dashboard actions. Agents must not edit it directly. |
66
+ | `knowledge/agents/<agentId>.md` | Legacy per-agent notebook | Written only by the consolidation pipeline; never by the agent itself. |
67
+ | `notes/archive/` and `knowledge/_swept/` | Recovery copies and retired source artifacts | Retention differs by subsystem; see [kb-sweep.md](kb-sweep.md). |
68
+ | `config.json` | Feature and engine settings | Intentionally file-backed configuration, not memory content. |
69
+
70
+ SQLite is the sole authority for runtime state generally; see
71
+ [design-state-storage.md](design-state-storage.md). The Markdown paths above
72
+ remain intentionally file-backed documents and coordination artifacts.
73
+
74
+ ### Synchronization is directional, not a two-way mirror
75
+
76
+ The normal ingestion paths often write both layers, but they are not one
77
+ cross-store transaction:
78
+
79
+ - Migration 017 performed a one-time backfill from existing knowledge files,
80
+ team-note sections, and per-agent sections into SQL.
81
+ - Inbox consolidation writes knowledge and per-agent files and then upserts
82
+ corresponding semantic records.
83
+ - `POST /api/knowledge` writes both the knowledge file and a human-trusted SQL
84
+ record.
85
+ - Episodic capture writes SQL only.
86
+ - Pinned context writes `pinned.md` only.
87
+
88
+ Direct filesystem edits, `POST /api/notes-save`, manual inbox **Persist** or
89
+ **Promote to KB**, and KB sweep rewrites/moves do not perform a general SQL
90
+ reindex. File pruning also does not expire an already-created structured
91
+ record. Consequently, Markdown and SQL can diverge; active retrieval follows
92
+ SQL, while the legacy path follows the files. There is currently no full
93
+ reindex API or CLI command. The SQL layer also has no scheduled record expiry;
94
+ the `expired` status exists, but the KB sweep's file TTL does not apply it.
95
+
96
+ ## Inputs
97
+
98
+ ### Human inputs
99
+
100
+ Humans can add memory through the dashboard or its localhost API:
101
+
102
+ - **Quick Notes** calls `POST /api/notes`, creating a Markdown file in
103
+ `notes/inbox/` for normal consolidation.
104
+ - **Pinned Notes** calls `POST /api/pinned` (plus `/remove` and `/update`) and
105
+ edits `pinned.md`.
106
+ - **New Knowledge Base Entry** calls `POST /api/knowledge`, writing a category
107
+ file and a global, human-trusted semantic record.
108
+ - Inbox actions can persist an item into `notes.md` or promote it directly to a
109
+ knowledge category.
110
+ - Feedback actions and several engine/operator workflows also create inbox
111
+ notes, so not every inbox file originates from the Quick Notes form.
112
+
113
+ ### Agent inputs
114
+
115
+ An agent records durable findings in exactly one assigned
116
+ `notes/inbox/<agent>-<work-item>-<timestamp>.md` file after a successful task.
117
+ The required YAML `agent:` field is the preferred author identity; the filename
118
+ prefix is a fallback. An optional `project:` frontmatter value makes the
119
+ knowledge SQL record project-scoped.
120
+
121
+ Agents read but never delete, move, or overwrite `knowledge/`, including their
122
+ own `knowledge/agents/<agentId>.md`. Corrections go through a new inbox note so
123
+ the pipeline can preserve provenance and reconcile the old material.
124
+
125
+ ### Completion outcomes
126
+
127
+ When `engine.memoryEpisodicCapture` is enabled, post-completion lifecycle writes
128
+ a compact SQL episode for successful, partial, and failed dispatches. This path
129
+ is independent of the agent's success-only inbox note.
130
+
131
+ ## Inbox consolidation
132
+
133
+ `engine.js` calls `consolidateInbox(config)` on every tick. Consolidation starts
134
+ when the number of eligible Markdown files reaches
135
+ `engine.inboxConsolidateThreshold` (default `5`). It excludes inbox items pinned
136
+ against consolidation and files already in the in-flight set.
137
+
138
+ Before the normal pipeline, a content-hash circuit breaker checks each note's
139
+ first 200 characters plus its full length. If one hash represents more than 80%
140
+ of the eligible batch, consolidation skips the LLM and all `notes.md`, KB/SQL,
141
+ and personal-memory writes, then archives the entire batch, including any
142
+ minority notes. This means a note from a duplicate-heavy batch may exist only
143
+ under `notes/archive/`.
144
+
145
+ The normal pipeline is:
146
+
147
+ 1. Read the eligible inbox files and the current `notes.md`.
148
+ 2. Ask the configured direct LLM (Haiku, one turn) for a concise deduplicated
149
+ digest. Each inbox body is fenced as untrusted input in the consolidation
150
+ prompt.
151
+ 3. If the runtime is unavailable, output is malformed, the call fails, or the
152
+ three-minute deadline is exceeded, use the deterministic regex fallback.
153
+ 4. Append the digest to `notes.md` under its file lock. If the existing file is
154
+ present but unreadable, defer the whole batch rather than risk clobbering it.
155
+ 5. Classify every source note into a knowledge category. Reusable notes retain
156
+ their full body; narrow one-off notes become a short stub linked to the
157
+ archived original.
158
+ 6. Upsert a global or project-scoped semantic record for the KB representation.
159
+ 7. Route eligible notes into the configured author's personal file and upsert an
160
+ agent-scoped semantic record.
161
+ 8. Archive processed inbox files. If an eligible personal-memory write fails,
162
+ leave that source note in the inbox so a later tick can retry it.
163
+
164
+ `notes.md` is kept near 50,000 JavaScript code units by trimming at section
165
+ boundaries and appending removed sections to
166
+ `notes/archive/notes-overflow.md`. If the overflow archive cannot be written,
167
+ the engine leaves the oversized file intact rather than lose content.
168
+
169
+ ### Per-agent memory
170
+
171
+ Personal files use dated `### YYYY-MM-DD: <title>` sections. Routing requires:
172
+
173
+ - a valid agent id from frontmatter or the filename;
174
+ - a matching key in `config.agents`;
175
+ - a non-empty body; and
176
+ - a non-`temp-*` agent.
177
+
178
+ Each appended inbox body is fenced on disk with source provenance. Canonical
179
+ sectioned files are pruned oldest-first to at most
180
+ `engine.agentMemoryMaxEntries` (default `300`) and a 25,000-byte UTF-8 budget.
181
+ The newest section is retained even if it alone exceeds the byte target.
182
+
183
+ For correction-shaped notes, the conditional reconciliation pass can ask Haiku to
184
+ rewrite stale facts before appending. Failures fall back to a plain append. A
185
+ separate default-off summary pass can fold older sections into a fenced
186
+ `Earlier learnings summary`; it rechecks the file after the LLM call so a
187
+ concurrent append cannot be overwritten.
188
+
189
+ The heavier KB sweep is a separate process. `engine.autoConsolidateMemory`
190
+ controls its four-hour automatic cadence; it does not control inbox-to-notes
191
+ consolidation. See [kb-sweep.md](kb-sweep.md) for deduplication, TTL, rewrite,
192
+ and archive behavior.
193
+
194
+ ## Structured records and FTS5 retrieval
195
+
196
+ Migration `017-agent-memory.js` created:
197
+
198
+ - `memory_records`, including content, type, scope, trust, confidence,
199
+ importance, provenance, validity, and supersession fields;
200
+ - the external-content `memory_records_fts` table over title, body, tags, and
201
+ source path, with insert/update/delete triggers; and
202
+ - `memory_retrieval_runs`, capped by the store to the newest 5,000 runs.
203
+
204
+ Supported record values are:
205
+
206
+ | Dimension | Values |
207
+ |-----------|--------|
208
+ | Type | `semantic`, `episodic`, `procedural` |
209
+ | Scope | `global`, `project:<key>`, `agent:<key>` |
210
+ | Trust | `human`, `system`, `agent`, `external` |
211
+ | Status | `active`, `superseded`, `retracted`, `expired` |
212
+
213
+ Built-in file ingestion creates semantic records. Completion capture creates
214
+ episodic records. The schema supports procedural records, but recall excludes
215
+ procedural content unless its trust is `human` or `system`.
216
+
217
+ Record ids are deterministic from source type, source reference, and content
218
+ hash. Re-upserting identical content is idempotent. New content for the same
219
+ source type/reference automatically supersedes the previous active row unless
220
+ the caller supplies an explicit supersession.
221
+
222
+ ### Query construction and selection
223
+
224
+ `engine/memory-retrieval.js` builds a task query from the work-item title,
225
+ description, work type, failure class, source plan, references, PR title, and PR
226
+ branch. The SQL store:
227
+
228
+ 1. tokenizes at most 32 unique non-stop-word terms;
229
+ 2. issues an OR query against FTS5;
230
+ 3. limits dispatch recall to active records in global, current-project, and
231
+ assigned-agent scopes; and
232
+ 4. rejects agent/external procedural records.
233
+
234
+ FTS5 lexical rank supplies the initial order. A deterministic reranker then
235
+ boosts matching project/agent scope, human/system trust, task-mentioned source
236
+ paths, recency, importance, and confidence. Results are deduplicated by content
237
+ hash, then packed in rank order.
238
+
239
+ Every packed record includes:
240
+
241
+ - record id;
242
+ - type and scope;
243
+ - effective date; and
244
+ - `sourcePath` or `sourceRef`.
245
+
246
+ That provenance is visible both in the `Relevant Memory` appendix and the
247
+ dashboard's structured-memory search.
248
+
249
+ ## Prompt assembly and fallback
250
+
251
+ `engine/playbook.js` renders the requested playbook, adds shared rules, then
252
+ appends memory context in this order:
253
+
254
+ 1. **Pinned Context**, when `pinned.md` is non-empty.
255
+ 2. Either **Relevant Memory** or the legacy pair:
256
+ - **Team Notes** from `notes.md`;
257
+ - **Personal Memory** from `knowledge/agents/<agentId>.md`.
258
+ 3. Later non-memory appendices such as propagated project instructions.
259
+
260
+ Pinned context is independent of retrieval and remains present in every mode.
98
261
 
99
- The read order is deliberately ordered by **trust × recency**:
262
+ | Mode | Prompt behavior |
263
+ |------|-----------------|
264
+ | `memoryRetrieval` off | Pinned context plus legacy team/personal memory. No retrieval telemetry. |
265
+ | Flag on, `memoryRetrievalShadowMode: true` | Compute retrieval and store telemetry, but inject the legacy team/personal memory. |
266
+ | Flag on, shadow off, non-empty result | Inject pinned context plus `Relevant Memory`; suppress broad team/personal appendices. |
267
+ | Flag on, shadow off, empty query/no matches | Fall back to legacy team/personal memory. |
268
+ | Retrieval or telemetry write throws | Log a warning and fall back to legacy team/personal memory. |
100
269
 
101
- - `pinned.md` is human-curated and authoritative it overrides everything else.
102
- - `knowledge/` is curated by the sweep from validated agent findings — high-signal but slightly delayed.
103
- - `notes.md` mirrors `knowledge/` in summarized form, optimized for prompt injection.
104
- - `notes/inbox/` is raw, recent, and unverified — useful for the freshest context but treat critically.
270
+ Missing legacy files are skipped without failing dispatch.
271
+
272
+ ### Bounds
273
+
274
+ | Content | Default bound | Enforcement |
275
+ |---------|---------------|-------------|
276
+ | Pinned input | 4,096 JavaScript UTF-16 code units, then a truncation notice | `String.length`/`slice`, despite the historical "4 KB" comment. |
277
+ | Retrieved pack | 8,192 UTF-8 bytes (`memoryRetrievalMaxBytes`) | Clamped to 1,024-65,536 bytes; includes record blocks and separators, but not the outer heading/fence. |
278
+ | Selected records | 8 (`memoryRetrievalTopK`) | Clamped to 1-30. |
279
+ | FTS candidate pool | 50 (`memoryRetrievalCandidateLimit`) | Dashboard accepts 5-200; the store hard-caps at 200. |
280
+ | Legacy team notes | 8,192 UTF-8 bytes (`ENGINE_DEFAULTS.maxNotesPromptBytes`) | Prefer the newest ten sections, then UTF-8-safe truncation and an older-entry footer. |
281
+ | Legacy personal memory | 8,192 UTF-8 bytes | Prefer the newest ten sections, then UTF-8-safe truncation. |
282
+ | Any untrusted fence body | 65,536 UTF-8 bytes (`ENGINE_DEFAULTS.untrustedFenceMaxBytes`) | UTF-8-safe truncation inside the fence with a remaining-byte marker. |
283
+
284
+ The configured retrieval budget controls selected record text, not all Markdown
285
+ or XML-like wrapper overhead added to the final prompt.
105
286
 
106
- Following this order achieves two goals:
287
+ ## Security and trust boundaries
107
288
 
108
- 1. **Avoid re-research.** If another agent already investigated the same module, fix, or design question, the answer is somewhere in this stack. Reading saves a costly round-trip.
109
- 2. **Respect team decisions.** Pinned policies and consolidated conventions encode prior agreement (often with the human). Skipping team memory and acting on first principles is how regressions and drift happen.
289
+ Memory content is evidence, not a new instruction channel. Before prompt
290
+ insertion, pinned context, retrieved records, team notes, and personal memory
291
+ are wrapped with `engine/untrusted-fence.js`:
110
292
 
111
- ## Sweep-write-only constraint
293
+ ```text
294
+ <UNTRUSTED-INPUT source="kind:provenance">...</UNTRUSTED-INPUT>
295
+ ```
112
296
 
113
- **Agents must never delete, move, or overwrite files in `knowledge/`.** Only the consolidation sweep (`engine/consolidation.js`) writes to `knowledge/`. This rule is restated in every dispatch prompt under a "Knowledge Base Rules" heading.
297
+ The wrapper sanitizes the source attribute, escapes attempted closing tags, and
298
+ truncates by UTF-8 bytes. The consolidation LLM receives separately fenced
299
+ inbox bodies plus an explicit instruction to summarize rather than execute
300
+ their contents.
301
+
302
+ Additional defenses are:
303
+
304
+ - agent- or external-trusted procedural records are ineligible for recall;
305
+ - active retrieval includes record provenance and tells the agent to verify
306
+ claims against live code;
307
+ - a completion report flagged with
308
+ `failure_class: "injection-flagged"` is not captured as an episode; and
309
+ - nonce-mismatched completion reports never reach episodic capture.
310
+
311
+ `task_description` is deliberately not fenced because it is the instruction
312
+ being executed, not recalled evidence. For the broader threat model and the
313
+ required policy for new splice sites, see [security.md](security.md).
314
+
315
+ ## Episodic capture
316
+
317
+ `captureTaskMemoryEpisode()` runs after completion status and nonce validation.
318
+ When enabled, it records:
319
+
320
+ - outcome, work item/type, agent, and project;
321
+ - PR reference and failure class;
322
+ - bounded affected-file and test lists; and
323
+ - a bounded result summary.
324
+
325
+ Secret-shaped values are redacted, and lists and strings are capped before
326
+ storage. The preferred summary comes from parsed runtime output or the structured
327
+ completion report. If neither supplies one and stdout has no `"type":` marker,
328
+ lifecycle falls back to raw stdout; that value is redacted and truncated to
329
+ 4,000 characters, but transcript- or tool-log-like text is not filtered out.
330
+ Project scope is preferred, then agent scope, then global scope. Records use
331
+ system trust, with higher importance for failed and partial outcomes.
332
+
333
+ Capture is best-effort: a write failure is logged but does not change the task's
334
+ completion result.
335
+
336
+ ## Configuration and feature flags
337
+
338
+ | Key | Default | Purpose |
339
+ |-----|---------|---------|
340
+ | `features.memoryRetrieval` | `true` | Enables structured retrieval. Feature resolution is environment override, then `config.features`, then registry default. |
341
+ | `engine.memoryRetrievalShadowMode` | `false` | Measure retrieval without replacing legacy prompt memory. |
342
+ | `engine.memoryRetrievalTopK` | `8` | Maximum selected records. |
343
+ | `engine.memoryRetrievalMaxBytes` | `8192` | UTF-8 byte budget for selected record blocks. |
344
+ | `engine.memoryRetrievalCandidateLimit` | `50` | FTS5 candidates before reranking. |
345
+ | `engine.memoryEpisodicCapture` | `false` | Store compact completion episodes. |
346
+ | `engine.inboxConsolidateThreshold` | `5` | Eligible inbox files required before consolidation starts. |
347
+ | `engine.autoConsolidateMemory` | `false` | Automatically run the separate KB sweep every four hours. |
348
+ | `engine.agentMemoryMaxEntries` | `300` | Non-summary section window in a personal file. |
349
+ | `engine.agentMemorySummaryEnabled` | `false` | Enable LLM compression of older personal sections. |
350
+ | `engine.agentMemorySummaryThreshold` | `30` | Oldest-section batch size for a summary. |
351
+ | `engine.agentMemorySummaryDays` | `30` | Age trigger for summary eligibility. |
352
+
353
+ The feature flag is exposed by the generic Dashboard **Settings > Features**
354
+ surface. Settings also exposes shadow mode, episodic capture, retrieval bounds,
355
+ the inbox threshold, and automatic KB sweep. The personal-file window and
356
+ summary keys are configuration-only today.
357
+
358
+ The environment override for the feature is
359
+ `MINIONS_FEATURE_MEMORYRETRIEVAL=1|0`.
360
+
361
+ ## Dashboard and API surfaces
362
+
363
+ The classic Dashboard **Inbox** page combines Quick Notes, Team Notes,
364
+ Knowledge Base, the manual KB sweep, and **Memory search**; pinned context is
365
+ managed from the Home page. Structured search operates across all scopes and can
366
+ inspect each lifecycle status.
367
+
368
+ | Method | Path | Role |
369
+ |--------|------|------|
370
+ | `POST` | `/api/notes` | Create an inbox note for consolidation. |
371
+ | `GET` / `POST` | `/api/pinned` | Read or add pinned context. |
372
+ | `POST` | `/api/pinned/update` | Replace a pinned entry. |
373
+ | `POST` | `/api/pinned/remove` | Remove a pinned entry. |
374
+ | `GET` | `/api/knowledge` | List readable knowledge entries. |
375
+ | `POST` | `/api/knowledge` | Create a human-authored KB file and SQL record. |
376
+ | `GET` | `/api/knowledge/<category>/<file>` | Read one KB file. |
377
+ | `POST` | `/api/knowledge/sweep` | Start the asynchronous KB sweep. |
378
+ | `GET` | `/api/knowledge/sweep/status` | Poll sweep status. |
379
+ | `GET` | `/api/memory-records/search?q=...&status=active&limit=20` | Search structured records with provenance. |
380
+ | `POST` | `/api/memory-records/<id>/action` | Apply `pin`, `retract`, or `restore`. |
381
+ | `POST` | `/api/inbox/persist` | Copy an inbox excerpt into `notes.md`, then archive the source. |
382
+ | `POST` | `/api/inbox/promote-kb` | Move an inbox item into a selected KB category. |
383
+
384
+ A structured-record `pin` is not the same as a pinned note. It changes that SQL
385
+ row to human trust, importance `1`, confidence of at least `0.9`, and active
386
+ status. It does not write `pinned.md`, bypass lexical matching, or guarantee
387
+ selection within the top-K/budget.
388
+
389
+ ## Operations and troubleshooting
390
+
391
+ ### Inspect the active surfaces
392
+
393
+ ```bash
394
+ curl http://localhost:7331/api/pinned
395
+ curl http://localhost:7331/api/knowledge
396
+ curl "http://localhost:7331/api/memory-records/search?q=worktree&status=active&limit=20"
397
+ ```
114
398
 
115
- Why:
399
+ Use `GET /api/routes` to confirm the live route catalog.
116
400
 
117
- - The sweep classifies inbox notes into the right category, generates the canonical filename, and updates the `notes.md` summary in lockstep with the `knowledge/` write. Manual edits skip those side effects and desync the system.
118
- - Agents work in parallel. Letting any agent rewrite `knowledge/` entries makes consolidation non-deterministic and makes blame attribution impossible.
119
- - Knowledge base entries are referenced by date-stamped path. Rewriting an entry breaks every existing link.
401
+ ### Expected legacy fallback
120
402
 
121
- If an agent thinks a `knowledge/` file is wrong, the correct response is to **note the disagreement in the agent's own inbox file**. The sweep will pick up the note on the next consolidation cycle and either supersede or correct the existing entry.
403
+ Seeing **Team Notes** and **Personal Memory** instead of **Relevant Memory** is
404
+ expected when the feature is off, shadow mode is on, the task query has no
405
+ indexable terms, FTS5 finds no eligible rows, or retrieval/telemetry throws.
406
+ Check engine logs for `Memory retrieval failed ...; using legacy memory`.
122
407
 
123
- The same constraint applies to `knowledge/agents/<agentId>.md` — those are curated by the sweep and should not be hand-edited.
408
+ ### A note remains in the inbox
124
409
 
125
- ## Session State vs. Persistent Memory
410
+ Check:
126
411
 
127
- The PRD that introduced sliding-window memory (W-mq07b8do000nc86a) referenced two distinct write paths — `update_session_state()` and `update_memory()` — borrowed from agent frameworks that model agents as long-lived in-process objects. Minions has neither method because it doesn't model agents that way; understanding the mapping prevents fruitless searches for non-existent APIs.
412
+ 1. The eligible file count has reached `engine.inboxConsolidateThreshold`.
413
+ 2. The note is not pinned against consolidation.
414
+ 3. A prior consolidation is not still in flight.
415
+ 4. `notes.md` and its lock are writable.
416
+ 5. For a configured agent, the personal-memory write did not fail and defer
417
+ archiving.
128
418
 
129
- **Session state** = the dispatch's worktree + child process. Each Minions agent runs as a fresh OS process spawned by `engine.js → engine/spawn-agent.js` inside a per-work-item git worktree (`work/<wi-id>` by default; see `shared.deriveWorkItemBranchName`). When the dispatch ends, the engine deletes the worktree and the child exits. There is no persistent "session" object to update — ephemeral state lives in process memory and disk paths under the worktree, both of which are reclaimed automatically. No code is needed to "clear" session state; it never persists in the first place.
419
+ An LLM failure alone should not strand the batch; the regex fallback handles it.
130
420
 
131
- **Persistent memory** = `knowledge/agents/<agentId>.md`, the per-agent file appended to by `engine/consolidation.js` during the inbox sweep. This is the analog of `update_memory()` in PRD terms. It is written only by the consolidation sweep (the [sweep-write-only constraint](#sweep-write-only-constraint) applies), is injected into every subsequent dispatch's prompt for that same agent ID via `engine/playbook.js`, and is bounded by two complementary cuts plus an optional summary pass:
421
+ ### Personal memory is missing
132
422
 
133
- | Tunable (under `engine.*` in `config.json`) | Default | Behavior |
134
- |---------------------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------|
135
- | `agentMemoryMaxEntries` | `300` | Sliding-window entry-count cap. Older non-summary sections evicted oldest-first when exceeded. |
136
- | (built-in) `AGENT_MEMORY_BUDGET_BYTES` | `25000` | Hard byte ceiling for prompt-injection safety. Always wins when both caps bind. Sticky summary sections obey it. |
137
- | `agentMemorySummaryEnabled` | `false` | Master switch for the LLM-driven compression pass. Off by default to avoid surprise Haiku spend. |
138
- | `agentMemorySummaryThreshold` | `30` | When the summary pass fires, fold this many oldest entries into one summary section. |
139
- | `agentMemorySummaryDays` | `30` | Age trigger: if the oldest entry is older than this, fold even when under the entry cap. |
423
+ Confirm that `agent:` exactly matches a configured `config.agents` key, or that
424
+ the filename starts with that id. `temp-*`, unknown, invalid, and empty authors
425
+ are intentionally skipped. Also remember that successful active retrieval
426
+ suppresses the broad personal-file appendix; the relevant agent-scoped record
427
+ may instead appear inside **Relevant Memory**.
140
428
 
141
- The summary pass runs fire-and-forget after every successful `appendToAgentMemory` write inside `classifyToKnowledgeBase`. It re-reads outside the lock, calls Haiku, then re-acquires the lock and verifies the same oldest sections are still in place (stale-candidate guard) before swapping. Any failure — disabled, no trigger, LLM unavailable, race detected — is a silent no-op; the consolidation pipeline is never blocked on the LLM.
429
+ ### Search misses known Markdown
142
430
 
143
- The compressed summary is wrapped in an `<UNTRUSTED-INPUT source="agent-memory-summary:agent=...">` fence on disk. The source material was the inbox bodies of evicted entries, which are themselves untrusted; without the fence, any imperative laundered through summarization could later be executed by an agent reading its own memory.
431
+ Structured search is lexical FTS5, not semantic embeddings. It ignores common
432
+ task stop words, searches only the requested lifecycle status, and dispatch
433
+ retrieval uses exact project/agent scope keys. More importantly, out-of-band
434
+ file edits and file-only mutation paths are not automatically reindexed. Compare
435
+ the record's `sourcePath`/`sourceRef` with the current file before trusting it.
144
436
 
145
- Summary sections are **sticky** under the entry-count cap — they represent compressed knowledge that should outlive ordinary inbox entries. They are detected by their title prefix `Earlier learnings summary` and only the byte budget can evict them.
437
+ Do not repair divergence by editing `engine/state.db` directly. Use the supported
438
+ dashboard/API ingestion paths or fix the originating writer so provenance and
439
+ events remain intact.
146
440
 
147
- **Default-off rationale.** `agentMemorySummaryEnabled` defaults to `false` (intentional deviation from PRD wording that implies "always on"). Enabling it commits operators to per-agent Haiku spend on every consolidation cycle; the entry-count cap on its own already prevents unbounded growth. Operators who have weighed the cost set `engine.agentMemorySummaryEnabled: true` in `config.json` to opt in.
441
+ ## Agent read and write rules
148
442
 
149
- ## Quick reference for agents
443
+ The shared playbook's on-disk lookup order remains:
150
444
 
151
- ```
152
- 1. pinned.md critical, human-flagged. Read first.
153
- 2. knowledge/ categorized KB. Read, never write.
154
- 3. notes.md consolidated team notes. Read.
155
- 4. notes/inbox/ fresh, unconsolidated findings. Read; write one on success.
445
+ ```text
446
+ 1. pinned.md - critical operator context; read first
447
+ 2. knowledge/ - categorized KB; read, never write
448
+ 3. notes.md - consolidated team digest
449
+ 4. notes/inbox/ - newest unconsolidated findings
450
+ 5. related live output and prior work-item summaries
451
+ 6. external research
156
452
  ```
157
453
 
158
- After the four tiers, optionally consult `agents/*/live-output.log` and prior `resultSummary` fields. Only then go outside.
454
+ This lookup discipline complements prompt retrieval; it does not mean all four
455
+ Markdown tiers are automatically copied into the prompt.
456
+
457
+ ## Implementation map
458
+
459
+ - [`engine/consolidation.js`](../engine/consolidation.js) - inbox digest,
460
+ classification, personal routing, pruning, reconciliation, and summary.
461
+ - [`engine/db/migrations/017-agent-memory.js`](../engine/db/migrations/017-agent-memory.js)
462
+ - schema and one-time Markdown backfill.
463
+ - [`engine/memory-store.js`](../engine/memory-store.js) - record validation,
464
+ lifecycle, FTS5 query, and telemetry persistence.
465
+ - [`engine/memory-retrieval.js`](../engine/memory-retrieval.js) - task query,
466
+ reranking, deduplication, provenance formatting, and byte packing.
467
+ - [`engine/playbook.js`](../engine/playbook.js) - pinned/retrieved/legacy prompt
468
+ assembly and fallback.
469
+ - [`engine/lifecycle.js`](../engine/lifecycle.js) - episodic completion capture.
470
+ - [`engine/untrusted-fence.js`](../engine/untrusted-fence.js) - provenance fence
471
+ and UTF-8-safe truncation.
472
+ - [`dashboard.js`](../dashboard.js) and
473
+ [`dashboard/js/memory-search.js`](../dashboard/js/memory-search.js) -
474
+ operator APIs and UI.
159
475
 
160
476
  ## See also
161
477
 
162
- - `playbooks/shared-rules.md` the canonical instruction injected into every dispatch.
163
- - `engine/playbook.js` — where `pinned.md`, `notes.md`, and per-agent memory get spliced into prompts.
164
- - `engine/consolidation.js` the sweep that promotes inbox notes into `knowledge/` and `notes.md`.
478
+ - [kb-sweep.md](kb-sweep.md) - curation and retention of readable KB files.
479
+ - [named-agents.md](named-agents.md) - agent configuration and routing.
480
+ - [security.md](security.md) - trust boundaries and prompt-injection policy.
481
+ - [self-improvement.md](self-improvement.md) - memory within the broader
482
+ feedback loop.