@yemi33/minions 0.1.2369 → 0.1.2371

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.
Files changed (79) hide show
  1. package/bin/minions.js +38 -11
  2. package/dashboard/js/memory-search.js +112 -0
  3. package/dashboard/js/render-kb.js +15 -0
  4. package/dashboard/js/render-other.js +2 -30
  5. package/dashboard/js/render-work-items.js +0 -34
  6. package/dashboard/js/settings.js +27 -27
  7. package/dashboard/pages/inbox.html +1 -1
  8. package/dashboard/pages/tools.html +1 -1
  9. package/dashboard.js +148 -258
  10. package/docs/README.md +2 -3
  11. package/docs/architecture.excalidraw +2 -2
  12. package/docs/auto-discovery.md +3 -3
  13. package/docs/completion-reports.md +0 -121
  14. package/docs/copilot-cli-schema.md +9 -17
  15. package/docs/deprecated.json +0 -51
  16. package/docs/design-state-storage.md +4 -4
  17. package/docs/harness-propagation.md +33 -263
  18. package/docs/human-vs-automated.md +1 -1
  19. package/docs/named-agents.md +0 -2
  20. package/docs/plan-lifecycle.md +5 -6
  21. package/docs/qa-runbook-lifecycle.md +10 -25
  22. package/docs/shared-lifecycle-module-map.md +2 -10
  23. package/docs/team-memory.md +18 -4
  24. package/engine/ado-comment.js +5 -11
  25. package/engine/ado.js +10 -5
  26. package/engine/cleanup.js +16 -36
  27. package/engine/cli.js +13 -175
  28. package/engine/comment-format.js +8 -182
  29. package/engine/consolidation.js +72 -1
  30. package/engine/db/index.js +60 -10
  31. package/engine/db/migrations/017-agent-memory.js +208 -0
  32. package/engine/db/migrations/018-sql-only-cutover.js +56 -0
  33. package/engine/dispatch-store.js +0 -114
  34. package/engine/dispatch.js +1 -6
  35. package/engine/features.js +6 -0
  36. package/engine/gh-comment.js +2 -10
  37. package/engine/github.js +8 -6
  38. package/engine/lifecycle.js +141 -173
  39. package/engine/llm.js +9 -11
  40. package/engine/managed-spawn.js +1 -6
  41. package/engine/memory-retrieval.js +165 -0
  42. package/engine/memory-store.js +367 -0
  43. package/engine/metrics-store.js +4 -128
  44. package/engine/pipeline.js +4 -7
  45. package/engine/playbook.js +64 -198
  46. package/engine/prd-store.js +115 -141
  47. package/engine/preflight.js +8 -55
  48. package/engine/projects.js +34 -41
  49. package/engine/pull-requests-store.js +9 -234
  50. package/engine/qa-runs.js +4 -15
  51. package/engine/qa-sessions.js +5 -17
  52. package/engine/queries.js +23 -103
  53. package/engine/routing.js +1 -1
  54. package/engine/runtimes/claude.js +1 -2
  55. package/engine/runtimes/codex.js +0 -2
  56. package/engine/runtimes/copilot.js +1 -4
  57. package/engine/shared-branch-pr-reconcile.js +2 -5
  58. package/engine/shared.js +179 -533
  59. package/engine/small-state-store.js +12 -647
  60. package/engine/spawn-agent.js +5 -96
  61. package/engine/state-operations.js +166 -0
  62. package/engine/supervisor.js +0 -1
  63. package/engine/watch-actions.js +2 -3
  64. package/engine/watches-store.js +2 -128
  65. package/engine/work-items-store.js +3 -254
  66. package/engine.js +102 -334
  67. package/package.json +2 -2
  68. package/playbooks/build-fix-complex.md +0 -4
  69. package/playbooks/fix.md +0 -4
  70. package/playbooks/implement-shared.md +0 -4
  71. package/playbooks/implement.md +0 -4
  72. package/playbooks/plan-to-prd.md +16 -11
  73. package/playbooks/plan.md +0 -4
  74. package/playbooks/review.md +0 -6
  75. package/playbooks/shared-rules.md +0 -65
  76. package/docs/harness-transparency.md +0 -199
  77. package/docs/project-skills.md +0 -193
  78. package/engine/discover-project-skills.js +0 -673
  79. package/engine/playbook-intents.js +0 -76
@@ -1,273 +1,43 @@
1
- # Harness Propagation
1
+ # Native Repository Harness
2
2
 
3
- > Status: contract doc for the **seamless user/repo harness invocation** plan
4
- > (`plans/seamless-user-repo-harness-invocation.md`, PRD
5
- > `prd/minions-opg-2026-06-10-4.json`). This is the foundation item it nails
6
- > down the *current* propagation surfaces and the worktree footgun so the
7
- > rest of the plan can extend them without reinventing the contract. Read this
8
- > before adding a new adapter method, a new asset kind, or a new opt-out flag.
3
+ Minions delegates repository instructions, skills, commands, and MCP discovery
4
+ to the selected CLI runtime. It does not classify repository assets, inject
5
+ selected skill content into prompts, add external harness directories, suppress
6
+ repository instruction files, or attest which harnesses an agent used.
9
7
 
10
- ## What is a "harness"
8
+ ## Runtime contract
11
9
 
12
- In this doc, "harness" means any user-installed or repo-local asset that the
13
- runtime CLI auto-discovers when it starts:
10
+ For each dispatch Minions:
14
11
 
15
- - **Skills** `SKILL.md` files the CLI loads as reusable workflows.
16
- - **Slash commands** `*.md` files the CLI exposes as `/command-name`.
17
- - **MCP servers** — JSON config entries that point at a stdio/HTTP server.
12
+ 1. Creates or selects the checkout.
13
+ 2. Sets the CLI working directory to the repository root or the configured
14
+ `meta.workdir`.
15
+ 3. Invokes the selected runtime adapter with the task prompt and explicit
16
+ orchestration/security flags.
18
17
 
19
- A harness is "propagated" when it reaches a dispatched agent without the user
20
- having to register it again in Minions. The single-line goal: *if it works
21
- when you type the same task into `claude` / `copilot` / `codex` in this repo
22
- on this machine, it works when Minions dispatches the same task here.*
18
+ From that point, Claude, Copilot, or Codex performs its normal native discovery
19
+ from the working directory and user configuration. Running the same CLI in the
20
+ same checkout should expose the same repository harness.
23
21
 
24
- ## The four propagation mechanisms
22
+ Repository harness files must exist in the dispatched checkout. In worktree
23
+ mode, uncommitted files from the operator's main checkout are intentionally not
24
+ copied into the dispatch worktree. Commit shared repository harness files, use
25
+ the runtime's user-level configuration for personal assets, or opt into live
26
+ checkout mode when the operator tree itself must be used.
25
27
 
26
- Every asset reaches the agent through exactly one of these mechanisms today.
27
- No engine code branches on `runtime.name` for any of them — the runtime
28
- adapter answers the contract questions and the engine fans out.
28
+ ## Explicit exceptions
29
29
 
30
- ### 1. Runtime native discovery (no engine code involved)
30
+ - `engine.copilotDisableBuiltinMcps` prevents Copilot's bundled GitHub MCP from
31
+ bypassing Minions PR tracking. This is an orchestration safety policy, not a
32
+ repository-harness override.
33
+ - `engine.claudePreApproveWorkspaceMcps` pre-approves the checkout's native
34
+ `.mcp.json` entries so a headless Claude process does not block on an invisible
35
+ trust prompt. It does not alter discovery or server definitions.
36
+ - Workspace manifests may restrict tools and repositories as an explicit
37
+ security boundary.
31
38
 
32
- The CLI reads its own config and indexes its own asset dirs on every spawn.
33
- Minions does not copy, symlink, or aggregate user-level assets — it just
34
- makes sure the relevant dirs are *readable* from the agent's cwd (see
35
- mechanism #3 below). The actual loading is the CLI's job.
39
+ ## Diagnostics
36
40
 
37
- | Runtime | User skills | User commands | User MCP servers |
38
- |---------|-------------|---------------|------------------|
39
- | Claude (`engine/runtimes/claude.js`) | `~/.claude/skills`, `~/.agents/skills`, plus `~/.claude/plugins/<…>/skills` | `~/.claude/commands`, plugin `commands/` | `~/.claude.json → mcpServers` |
40
- | Copilot (`engine/runtimes/copilot.js`) | `~/.copilot/skills`, `~/.agents/skills`, `~/.copilot/installed-plugins/<…>/skills` | `~/.copilot/commands` *(probe)* | `~/.copilot/mcp-config.json → mcpServers` |
41
- | Codex (`engine/runtimes/codex.js`) | `~/.agents/skills`, `/etc/codex/skills` | not yet supported by CLI | not yet a stable contract |
42
-
43
- The first column is the source of truth for "where does the CLI look on
44
- this machine?". The dashboard's tooling page mirrors this via
45
- `engine/queries.js → collectSkillFiles / collectCommandFiles`.
46
-
47
- ### 2. Cwd routing (`shared.resolveSpawnPaths`)
48
-
49
- `engine/shared.js → resolveSpawnPaths(project, type, MINIONS_DIR)` decides
50
- what the agent's `cwd` is:
51
-
52
- - **Read-only types** (`meeting`, `ask`, `explore`, `plan-to-prd`, `plan`)
53
- → `cwd = project.localPath` (the operator's main checkout — sees
54
- uncommitted files).
55
- - **Code-mutating types** (`implement`, `fix`, `review`, `test`, `verify`,
56
- `decompose`, `docs`) → `cwd = <worktree>` (a fresh `git worktree add`
57
- checked out at branch tip — only sees committed files).
58
-
59
- Cwd routing matters because every CLI's native discovery is **rooted at
60
- cwd**. Project-scope skills like `<repo>/.claude/skills/foo/SKILL.md` are
61
- loaded by the CLI only if `<cwd>/.claude/skills/foo/SKILL.md` exists at
62
- spawn time. See *The worktree-uncommitted footgun* below.
63
-
64
- Live-checkout mode (`project.checkoutMode: 'live'`) collapses both branches
65
- to `cwd = project.localPath` for every dispatch type, so the agent sees
66
- the operator's working tree as-is (including uncommitted assets). The
67
- tradeoff is single-mutating-dispatch concurrency per project — see
68
- `docs/live-checkout-mode.md`.
69
-
70
- ### 3. `--add-dir` (`engine/spawn-agent.js → computeAddDirs`)
71
-
72
- `computeAddDirs({ runtime, minionsDir, homeDir })` builds the list of
73
- absolute dirs the CLI is allowed to read **outside** its cwd. The current
74
- list:
75
-
76
- 1. `minionsDir` — always first, so playbooks, system prompt, and skill
77
- index are reachable from any worktree.
78
- 2. Every existing dir returned by `runtime.getUserAssetDirs({ homeDir })`
79
- for the resolved runtime — e.g. `~/.claude` + `~/.agents` for Claude,
80
- `~/.copilot` + `~/.agents` for Copilot, `~/.codex` + `~/.agents` for
81
- Codex.
82
-
83
- Non-existent dirs are dropped (Claude CLI rejects unknown `--add-dir`
84
- entries) and the list is deduped by resolved path.
85
-
86
- `--add-dir` is the **only** mechanism today that crosses the worktree
87
- boundary. It does not (yet) cover project-local-but-uncommitted assets —
88
- that's plan item #5 (`harnessPropagateProjectLocal`).
89
-
90
- ### 4. MCP suppression flags
91
-
92
- Two flags strip otherwise-inherited MCP / instruction surfaces; everything
93
- else flows through implicitly because the CLI reads its native config on
94
- every spawn:
95
-
96
- - `engine.copilotDisableBuiltinMcps` (default `true`) → `--disable-builtin-mcps`
97
- on Copilot. Strips Copilot's built-in `github-mcp-server` so dispatched
98
- agents do not try to open a parallel PR through the bundled MCP while
99
- Minions is already managing the PR via the host integration.
100
- - `engine.copilotSuppressAgentsMd` (default `true`) → `--no-custom-instructions`
101
- on Copilot. Strips project `AGENTS.md` auto-load so Minions playbook
102
- prompts are not silently overridden by repo-local instructions.
103
-
104
- There is no equivalent flag for stripping user-level skills, user-level
105
- slash commands, or user-level MCPs. The current posture is "always inherit"
106
- — `engine.hermeticHarness` (P-49e1c8b7, default `false`) is the per-fleet /
107
- per-agent opt-out. When TRUE:
108
-
109
- - `--add-dir` collapses to exactly `[minionsDir]` — every dir from
110
- `runtime.getUserAssetDirs({ homeDir })` is dropped.
111
- - `harnessPropagateProjectLocal` is skipped — no `--project-harness-dir`
112
- flags emitted.
113
- - `claudePreApproveWorkspaceMcps` is skipped — workspace `.mcp.json`
114
- servers are not pre-approved in `~/.claude.json`.
115
-
116
- The flag is **independent** of `copilotDisableBuiltinMcps` and
117
- `copilotSuppressAgentsMd` — those keep their existing semantics so an
118
- operator can run hermetic Claude *and* keep Copilot's built-in MCPs off
119
- or AGENTS.md auto-load suppressed without re-thinking each lever. Per-agent
120
- override at `agent.hermeticHarness` (resolved via
121
- `shared.resolveAgentHermeticHarness(agent, engine)`, mirrors
122
- `shared.resolveAgentBareMode`).
123
-
124
- ## The worktree-uncommitted footgun
125
-
126
- The single biggest behavioral wrinkle for new contributors:
127
-
128
- 1. The operator drops an experimental skill at
129
- `<repo>/.claude/skills/bar/SKILL.md` on their main checkout.
130
- 2. The skill works fine when invoked by hand:
131
- `cd <repo>; claude "use the bar skill"` — Claude's auto-discovery
132
- sees it because cwd is the main checkout.
133
- 3. The operator dispatches an `implement` work item against the same repo.
134
- 4. The engine runs `git worktree add <wt> origin/<branch>` (uncommitted
135
- files do not appear in a fresh worktree), then spawns the agent with
136
- `cwd = <wt>`.
137
- 5. Claude's discovery now happens at `<wt>` — `.claude/skills/bar/` is
138
- absent. The agent silently underperforms with no error message.
139
-
140
- Read-only dispatch types (`meeting`, `ask`, `explore`, `plan-to-prd`,
141
- `plan`) do **not** hit this footgun, because their cwd is
142
- `project.localPath`. Mutating types do.
143
-
144
- `--add-dir` is not currently extended to cover this case (plan item #5),
145
- so the only workarounds today are:
146
-
147
- - **Commit it.** The skill becomes visible to every dispatch and every
148
- teammate, with the usual reviewability tradeoff.
149
- - **Move it user-scope.** Drop it under `~/.claude/skills/bar/` instead —
150
- the CLI's user-skill discovery still works inside a worktree because
151
- `--add-dir` attaches `~/.claude`.
152
- - **Flip to live-checkout mode.** Set `project.checkoutMode = 'live'` on
153
- the project so every dispatch runs in `project.localPath`. Caveats in
154
- `docs/live-checkout-mode.md`.
155
-
156
- The dashboard tooling page does not yet surface this gap; that's plan
157
- item #8 ("Harness diagnostics" panel). Until then, run
158
- `minions doctor --harness` to get the per-runtime view of which dirs the
159
- engine actually surfaces, and check that flagged "missing" entries
160
- actually match the assets you expected to inherit.
161
-
162
- ## `minions doctor --harness`
163
-
164
- One-shot CLI diagnostic that prints, per registered runtime, every dir
165
- or file the engine would surface to a spawned agent under its scope label.
166
- Implemented in `engine/preflight.js → runHarnessDoctor(minionsHome)` and
167
- wired into `bin/minions.js` and `engine/cli.js` as the `--harness` mode
168
- of the existing `minions doctor` command.
169
-
170
- ### What it prints
171
-
172
- ```
173
- Minions Harness Propagation
174
- Runtime: claude
175
- User asset dirs (--add-dir to agents):
176
- ✓ /home/you/.claude [user]
177
- ✓ /home/you/.agents [user]
178
- Skill roots (CLI native discovery):
179
- ✓ /home/you/.claude/skills [user]
180
- ✓ /home/you/.agents/skills [user]
181
- ⚠ /repo/.claude/skills [project:myrepo] (missing on disk)
182
- ✓ /repo/.agents/skills [project:myrepo]
183
- Skill write targets (auto-extract destinations):
184
- ✓ /home/you/.claude/skills [personal]
185
- ✓ /repo/.claude/skills [project:myrepo]
186
- Runtime: copilot
187
- User asset dirs (--add-dir to agents):
188
- ✓ /home/you/.copilot [user]
189
- ✓ /home/you/.agents [user]
190
-
191
- Worktree --add-dir snapshot (engine fleet default: copilot):
192
- ✓ /home/you/.minions [minions]
193
- ✓ /home/you/.copilot [user]
194
- ✓ /home/you/.agents [user]
195
- All harness paths surveyed. Missing on-disk paths are warnings, not failures —
196
- they're listed so you can decide whether to create the dir, populate it, or
197
- ignore it for this host.
198
- ```
199
-
200
- ### Exit behavior
201
-
202
- The command exits **0 on a healthy host** even if some scoped paths do
203
- not exist on disk. The whole point is to surface "what would the engine
204
- attach today?" — a user that does not run Codex may legitimately have
205
- no `~/.codex` dir, and that is not a failure. Exits non-zero only on a
206
- true runtime/config error (e.g. registry refuses to resolve a known
207
- runtime, or `config.json` is unparseable).
208
-
209
- ### When to run it
210
-
211
- - After installing a new skill / command / MCP and "it didn't take" on
212
- the next dispatched agent — confirm Minions actually surfaces that dir
213
- for the runtime you're dispatching with.
214
- - After changing `engine.defaultCli` — different runtimes have different
215
- asset dirs; the `Worktree --add-dir snapshot` block reflects the fleet
216
- default.
217
- - After re-cloning a repo with `<repo>/.claude/skills/` — the diagnostic
218
- shows whether the project-scope path is on disk.
219
- - Before reporting "agent ignored my skill" — half the time the answer is
220
- visible in the diagnostic output.
221
-
222
- It does **not** check whether the assets *load successfully* inside the
223
- CLI (no parse of `SKILL.md`, no MCP handshake). For loadability,
224
- inspect the dashboard tooling page or run an `ask` dispatch and watch
225
- the live output.
226
-
227
- ## Adapter contract summary
228
-
229
- The runtime adapters answer these questions. The engine never special-cases
230
- a runtime by name — it only calls these methods on the resolved adapter.
231
-
232
- | Method | Signature | Used by |
233
- |--------|-----------|---------|
234
- | `getUserAssetDirs({ homeDir })` | → `string[]` | `engine/spawn-agent.js → computeAddDirs`; `runHarnessDoctor` |
235
- | `getSkillRoots({ homeDir, project? })` | → `[{ scope, dir, projectName? }]` | `engine/queries.js → collectSkillFiles`; `runHarnessDoctor` |
236
- | `getSkillWriteTargets({ homeDir, project? })` | → `{ personal, project? }` | `engine/lifecycle.js → extractSkillsFromOutput`; `runHarnessDoctor` |
237
- | `getCommandRoots({ homeDir, project? })` | → `[{ scope, dir, projectName? }]` | `engine/queries.js → collectCommandFiles` (via `getProjectHarnesses` for project scope); `runHarnessDoctor` |
238
- | `getMcpConfigPaths({ homeDir, project? })` | → `[{ scope, file, projectName? }]` | `runHarnessDoctor`; planned: `engine/queries.js → getStatusSlowStateMtimePaths` |
239
-
240
- `runHarnessDoctor` renders all five surfaces as "Slash commands" and "MCP
241
- config files" sections alongside the existing User asset dirs / Skill
242
- roots / Skill write targets blocks. Adapters that don't yet have a stable
243
- CLI contract for a surface (today: Codex for both commands and MCP,
244
- Copilot for project-scope commands) return `[]` so consumers can iterate
245
- runtimes generically without per-runtime branching.
246
-
247
- `engine/queries.js` exposes two shared helpers — `getUserHarnesses(homeDir)`
248
- and `getProjectHarnesses(project)` — that iterate every registered runtime
249
- via `engine/runtimes.listRuntimes()` + `resolveRuntime()` and union the
250
- `getSkillRoots` / `getCommandRoots` / `getMcpConfigPaths` contributions,
251
- deduped by absolute path. The same physical dir contributed by multiple
252
- adapters (e.g. `~/.agents/skills` exposed by Claude, Copilot, and Codex)
253
- surfaces once with a `runtimes: [name, ...]` provenance array. Use them
254
- from dashboard / diagnostics consumers instead of re-walking adapters by
255
- hand; `collectSkillFiles` / `collectCommandFiles` already do.
256
-
257
- > **Follow-up.** `getStatusSlowStateMtimePaths` still hardcodes the
258
- > per-runtime MCP file paths; refactoring it to consume
259
- > `getUserHarnesses(...).mcps` + `getProjectHarnesses(...).mcps` is
260
- > tracked separately so the slow-state mtime tracker automatically picks
261
- > up new adapters.
262
-
263
- ## Related docs
264
-
265
- - `docs/runtime-adapters.md` — full adapter interface table.
266
- - `docs/live-checkout-mode.md` — `project.checkoutMode: 'live'`
267
- contract (alternative to the worktree-add-dir story for repos where
268
- worktrees are unworkable).
269
- - `docs/skills.md` — skill block format and auto-extraction targets.
270
- - `plans/seamless-user-repo-harness-invocation.md` — full multi-item plan
271
- this doc is the foundation of.
272
- - `CLAUDE.md` → "Agent Spawn" + "Adapter Contract" — load-bearing
273
- invariants the propagation contract piggy-backs on.
41
+ `minions doctor --harness` and the dashboard Tools view show adapter-declared
42
+ native locations as read-only inventory. They do not influence spawning and do
43
+ not claim that a runtime loaded or used an asset.
@@ -15,7 +15,7 @@
15
15
  | Metrics | Engine (auto-collect) | Engine | You (view) | — |
16
16
  | Error recovery | Engine (detect) | — | You (retry/delete) | You |
17
17
  | Project linking | You (`minions add/scan`) | — | — | — |
18
- | MCP servers / Skills / Commands | You (native CLI config) | Inherited by agents | — | — (opt-out via `hermeticHarness`) |
18
+ | MCP servers / Skills / Commands | You (native CLI config) | Discovered natively by the selected runtime | — | — |
19
19
 
20
20
  ## The Two Human Gates
21
21
 
@@ -43,7 +43,6 @@ The full per-agent config shape supported under `config.agents.<id>`:
43
43
  | `model` | string | Per-agent model override (omitted → runtime picks its own default). |
44
44
  | `maxBudgetUsd` | number | Per-spawn USD cap. **`0` is a valid cap** (read-only / dry-run agents), not "unlimited". |
45
45
  | `bareMode` | boolean | Run Claude in `--bare` mode (Claude-runtime knob). |
46
- | `hermeticHarness` | boolean | Strip user-scope skill/command/MCP roots from the worktree; `--add-dir` resolves to `[minionsDir]` only. |
47
46
  | `workspace_manifest` | object | Optional permission scoping — see below. |
48
47
 
49
48
  ### Three-tier resolution chain
@@ -57,7 +56,6 @@ The resolver helpers live in [`engine/shared.js`](../engine/shared.js):
57
56
  | `resolveAgentModel(agent, engine)` | `agent.model` → `engine.defaultModel` → `undefined` (adapter omits `--model`, CLI uses its own default). |
58
57
  | `resolveAgentMaxBudget(agent, engine)` | `agent.maxBudgetUsd` → `engine.maxBudgetUsd` → `undefined` (no cap). Nullish-coalesced so `0` is honored. |
59
58
  | `resolveAgentBareMode(agent, engine)` | `agent.bareMode` → `engine.claudeBareMode` → `false`. Strict null check so a per-agent `false` overrides an engine `true`. |
60
- | `resolveAgentHermeticHarness(agent, engine)` | `agent.hermeticHarness` → `engine.hermeticHarness` → `false`. Same strict-override semantics. |
61
59
 
62
60
  Empty string / `null` / `undefined` all count as "unset" for the string knobs, so an agent
63
61
  inherits the fleet default rather than blanking the value.
@@ -116,18 +116,18 @@ The verify agent and the user can then build and run from these paths.
116
116
 
117
117
  ## Manual Archive Lifecycle
118
118
 
119
- Archiving is a deliberate dashboard action, not a post-verify side effect. When a PRD completes, `checkPlanCompletion()` writes the completion summary, persists `status: "completed"` plus `_completionNotified`, and creates the verify work item. The PRD remains in `prd/` and its source plan remains in `plans/` so humans can review the guide, inspect the final state, resume stale work, or reopen items before hiding the plan from active views.
119
+ Archiving is a deliberate dashboard action, not a post-verify side effect. When a PRD completes, `checkPlanCompletion()` writes the completion summary, persists `status: "completed"` plus `_completionNotified` in SQL, and creates the verify work item. The PRD remains active and its source plan remains in `plans/` so humans can review the guide, inspect the final state, resume stale work, or reopen items before hiding the plan from active views.
120
120
 
121
121
  The verify agent does not call `archivePlan()`. After the testing guide and any follow-up PR work are satisfactory, use the dashboard archive action, which calls `POST /api/plans/archive` with the active PRD or plan filename.
122
122
 
123
123
  Manual archive behavior:
124
124
 
125
- 1. For PRD JSON files, the dashboard archives them **in-place** in `prd/` — the file is not moved. The JSON is stamped with `archived: true`, `status: "archived"`, and `archivedAt` (Phase 10 step 4.2b). **No `.backup` sidecar is written or restored for any `prd/*.json`** — Phase 10 step 4.3 dropped the PRD backup/restore lifecycle entirely (root-level PRDs used to get one to survive the now-retired rename race; SQL is canonical, so a stale `.backup` is pure resurrection fuel, not a safety net).
125
+ 1. The dashboard updates the SQL PRD row with `archived: true`, `status: "archived"`, and `archivedAt`.
126
126
  2. If the PRD has `source_plan`, the matching `plans/<source>.md` is moved to `plans/archive/<source>.md`.
127
127
  3. Pending or queued work items linked to the archived PRD are cancelled with `_cancelledBy: "plan-archived"`; completed work items remain as history.
128
128
  4. Plan worktrees are cleaned up by the archive handler/lifecycle cleanup path.
129
129
 
130
- Use unarchive only when the archived PRD/plan should reappear in active views. Reopening completed work should happen before archive, or after unarchive, so the materializer can see the active PRD file.
130
+ Use unarchive only when the archived PRD/plan should reappear in active views. Reopening completed work should happen before archive, or after unarchive, so the materializer can see the active SQL PRD.
131
131
 
132
132
  ## Human Feedback on PRs
133
133
 
@@ -151,10 +151,9 @@ After PRs are created, humans can leave comments containing `@minions` to trigge
151
151
  | File | Purpose |
152
152
  |------|---------|
153
153
  | `plans/*.md` | Source plans awaiting review or linked to active PRDs |
154
- | `prd/*.json` | Active PRDs with materializable items |
154
+ | SQL `prds` + `prd_items` | Active and archived PRDs with materializable items |
155
155
  | `plans/archive/` | Manually archived plan Markdown files |
156
- | `prd/*.json` (with `archived: true`) | PRDs archived in-place (Phase 10 step 4.2b — not moved; flag + `archivedAt` stamped on the file) |
157
- | `prd/archive/` | Legacy: PRDs archived before Phase 10 (still scanned for filename-collision detection) |
156
+ | `prd/` | Legacy upgrade input only; migration 018 imports and retires these files |
158
157
  | `playbooks/verify.md` | Verification task playbook |
159
158
  | `playbooks/implement.md` | Implementation playbook |
160
159
  | `playbooks/plan-to-prd.md` | Plan → PRD conversion playbook |
@@ -17,7 +17,7 @@ engine-assigned `id`). `target` is a name from `/api/managed-processes` or
17
17
 
18
18
  ## Run-record path
19
19
 
20
- `qa-runs.json` (newest-first, capped). Each run:
20
+ The SQL `qa_runs` table stores newest-first, capped run records. Each run:
21
21
  `{ id, runbookId, runbookName, target, status, startedAt, completedAt, workItemId, agentId, artifacts }`.
22
22
  `status` ∈ `pending|dispatched|running|passed|failed|error`. Created by
23
23
  `POST /api/qa/runbooks/run` (`{ id }`), which dispatches a `qa-validate`
@@ -27,29 +27,14 @@ and clears the interval on page navigation via the `switchPage` wrapper in
27
27
  `dashboard/js/qa.js` (matches `_stopPlanPoll`/`_stopMeetingPoll` pattern in
28
28
  `dashboard/js/state.js`).
29
29
 
30
- ### Persistence (Phase 8: SQLite source of truth)
31
-
32
- Since the Phase 8 migration (`migrate-qa-state-to-sqlite`), both
33
- `qa-runs.json` and `qa-sessions.json` are dual-written sidecars and SQLite
34
- (`engine/state.db`, tables `qa_runs` + `qa_sessions`) is the source of
35
- truth. Every mutation goes through `shared.mutateQaRuns` /
36
- `shared.mutateQaSessions`, which apply inside a SQLite transaction
37
- (`BEGIN IMMEDIATE`) then best-effort mirror to JSON under a `withFileLock`
38
- on the sidecar path so cross-process writers serialize. Reads go through
39
- `small-state-store.readQaRuns` / `readQaSessions`, which compare the SQL
40
- content-hash to the JSON sidecar and rehydrate SQL from JSON when an
41
- external editor (operator hand-edit, manual rollback) has diverged.
42
-
43
- The JSON mirror is gated by `engine.qaDualWriteJson` (default true). Once
44
- operators trust SQL as the source of truth and have migrated external
45
- tooling off the JSON files, flip it false via Dashboard → Settings →
46
- Advanced → "QA: dual-write JSON sidecars" to halve write I/O. With the
47
- toggle off, `qa-runs.json` / `qa-sessions.json` stop receiving updates
48
- (stale snapshot at the moment of the flip), so don't disable until any
49
- external readers have moved to the SQL tables or the dashboard `/api/qa/*`
50
- endpoints. The qa-runs cap (`QA_RUNS_MAX_RECORDS=2000`) and qa-sessions
51
- cap (`QA_SESSIONS_MAX_RECORDS=500`) still apply, enforced in-memory at
52
- createRun/createSession time.
30
+ ### Persistence
31
+
32
+ SQLite (`engine/state.db`, tables `qa_runs` and `qa_sessions`) is the sole
33
+ source of truth. Every mutation goes through `shared.mutateQaRuns` or
34
+ `shared.mutateQaSessions` inside a transaction. Legacy JSON is imported only
35
+ during upgrade or through the explicit offline recovery command. The qa-runs
36
+ cap (`QA_RUNS_MAX_RECORDS=2000`) and qa-sessions cap
37
+ (`QA_SESSIONS_MAX_RECORDS=500`) remain enforced at creation time.
53
38
 
54
39
  ### Deleting runs
55
40
 
@@ -253,7 +238,7 @@ errors are mapped to HTTP via `_qaSessionsErrorToStatus`:
253
238
  DRAFT agent. Path is sandboxed by `_isSafeSessionId` (kebab-case ≤ 64
254
239
  chars, no `..`, no separators) before every read/write that maps id →
255
240
  path. Gitignored.
256
- - **Managed-spawn**: `engine/managed-processes.json` entry with
241
+ - **Managed-spawn**: SQL managed-process entry with
257
242
  `name='qa-session-<sessionId>'`. Cleaned up by `/kill` or by the
258
243
  session's `cancelSession` if the operator opts for the cancel-without-
259
244
  spawn-kill path.
@@ -105,7 +105,6 @@ because either is a bug:
105
105
  |---|---|---|
106
106
  | minions root pointer | `readMinionsRootPointer`, `saveMinionsRootPointer` | shared.js |
107
107
  | dashboard port file | `readDashboardPortFile`, `writeDashboardPortFile` | shared.js |
108
- | agent Copilot home dir | `resolveAgentCopilotHome`, `ensureAgentCopilotHome` | shared.js |
109
108
  | canonical PR id | `parseCanonicalPrId`, `getCanonicalPrId` | shared.js |
110
109
  | PR record | `findPrRecord`, `normalizePrRecord` | shared.js |
111
110
  | worktree owner marker | `writeWorktreeOwnerMarker`, `hasWorktreeOwnerMarker` | shared.js |
@@ -214,7 +213,7 @@ means zero such files were found), **Proposed target module**.
214
213
  | `redactPromptDescription` | 447-458 | Redactor applied to WI description text before it is injected into an agent // prompt. Applies the standard log redactor (_redactString) first, then catches // any remaining Authorization: Bearer header — including alrea | yes | `engine.js` | engine/shared.js (keep) |
215
214
  | `log` | 459-531 | Log. | yes | `engine/abandoned-pr-reconciliation.js`, `engine/ado-git-auth.js`, `engine/ado-status.js`, `engine/ado.js`, `engine/cc-worker-pool.js`, `engine/check-status.js` +72 more | engine/shared.js (keep) — logging/timestamp primitives |
216
215
  | `_currentLogPath` | 532-547 | Resolve the log file path at write time, not at module load time. `LOG_PATH` is captured eagerly when shared.js first loads. In production this is fine — there's a single shared.js instance per process. In tests the requ | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
217
- | `_currentLogDbPath` | 548-559 | Resolve the SQL state.db path at write time same isolation contract // as _currentLogPath. MINIONS_LOG_PATH only redirects the JSON sidecar; // the SQL store stays attached to MINIONS_TEST_DIR / MINIONS_HOME because // | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
216
+ | `_currentLogDbPath` | 548-559 | Resolve the SQL state.db path at write time with test/home isolation. | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
218
217
  | `resolveEngineCacheDir` | 560-593 | MINIONS_TEST_DIR-aware engine dir for cache files (runtime caps, models, // spawn-debug.log). Returns `<MINIONS_TEST_DIR>/engine` under tests; otherwise // the caller's source-adjacent fallback (typically `__dirname`-der | yes | `engine/runtimes/claude.js`, `engine/runtimes/codex.js`, `engine/runtimes/copilot.js` | engine/shared.js (keep) |
219
218
  | `openUrlInBrowser` | 594-645 | Cross-platform URL opener. Uses execSync so failures fall through the // try/catch and the caller sees them. Dashboard self-open and `minions dash` // / `minions restart` post-health open all funnel through here. // // E | yes | `test/unit/dashboard-port-resolution.test.js`, `dashboard.js`, `engine.js` | engine/shared.js (keep) |
220
219
  | `_bumpBrowserOpenMetric` | 646-662 | Bump per-reason counter under metrics._engine.browserOpens. Best-effort — // metrics.json writes must never throw out of openUrlInBrowser. Schema: // _engine.browserOpens.<reason> = { count, suppressedCount, lastAt } | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
@@ -324,7 +323,6 @@ means zero such files were found), **Proposed target module**.
324
323
  | `resolveAgentBareMode` | 3974-3991 | Resolve whether this agent should run in Claude `--bare` mode. Priority: 1. `agent.bareMode` — per-agent override (boolean) 2. `engine.claudeBareMode` — fleet default 3. `false` — hardcoded fallback Strict undefined/null | yes | `test/unit.test.js`, `engine.js` | engine/shared.js (keep) |
325
324
  | `_normalizeMcpServerList` | 3992-4005 | P-mcp-storm — Resolve the list of MCP server names to disable for spawned // Copilot agents. The engine FILTERS these out of the agent's seeded // COPILOT_HOME mcp-config (engine.js `_applyAgentCopilotHome`) — a server a | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
326
325
  | `resolveCopilotAgentDisabledMcpServers` | 4006-4030 | Resolve copilot agent disabled mcp servers. | yes | `test/unit/agent-copilot-home.test.js` | engine/shared.js (keep) |
327
- | `resolveAgentHermeticHarness` | 4031-4062 | P-49e1c8b7 — Resolve whether this agent should run with a hermetic harness. Priority (mirrors `resolveAgentBareMode`): 1. `agent.hermeticHarness` — per-agent override (boolean) 2. `engine.hermeticHarness` — fleet default | yes | `engine/spawn-agent.js`, `engine.js` | engine/shared.js (keep) |
328
326
  | `applyLegacyCcModelMigration` | 4063-4082 | If `config.engine.ccModel` is set but `config.engine.defaultModel` is unset, mutate the in-memory `config.engine` so `defaultModel` mirrors `ccModel` and log a one-time deprecation notice. Does NOT write to disk. Returns | yes | `engine/cli.js`, `test/unit.test.js` | engine/shared.js (keep) |
329
327
  | `_resetLegacyCcModelMigrationFlag` | 4083-4113 | Test helper: reset the dedup flag so repeated tests can re-trigger the log. | yes | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
330
328
  | `runtimeConfigWarnings` | 4114-4194 | Inspect runtime fleet config and return warning entries for misconfiguration. Warnings emitted: - Unknown CLI: any `cli` value (per-agent, ccCli, defaultCli) not in `registeredRuntimes`. Each unknown value produces one e | yes | `engine/preflight.js` | engine/process-utils.js (new) — process spawn/pid/tmp-dir lifecycle helpers |
@@ -337,13 +335,11 @@ means zero such files were found), **Proposed target module**.
337
335
  | `_qaDualWriteEnabled` | 4673-4681 | Phase 8 — QA state mutators. Both qa-runs and qa-sessions are top-level JSON arrays (mutator receives the array; mutates in place or returns a replacement). The store diffs by `id`. Falls back to mutateJsonFileLocked on | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
338
336
  | `_qaMutator` | 4682-4720 | Qa mutator. | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
339
337
  | `mutateWatches` | 4721-4741 | Route a watches mutation through the SQL store. Same shape as mutateWorkItems / mutatePullRequests: mutator receives the watches array, mutates in place or returns a replacement, and the store diffs by id. SQL-canonical | yes | `engine/watches.js` | engine/shared.js (keep) |
340
- | `mutateMetrics` | 4742-4763 | Route a metrics mutation through the SQL store with a JSON dual-write mirror. Same shape as mutateWorkItems / mutatePullRequests: mutator receives the full legacy-shape metrics object, mutates in place or returns a repla | yes | `engine/cleanup.js`, `engine/llm.js`, `test/unit/auto-recovery.test.js`, `engine.js` | engine/shared.js (keep) |
338
+ | `mutateMetrics` | 4742-4763 | Route a metrics mutation through the SQL store. | yes | `engine/cleanup.js`, `engine/llm.js`, `test/unit/auto-recovery.test.js`, `engine.js` | engine/shared.js (keep) |
341
339
  | `trackReviewMetric` | 4764-4784 | Update per-agent review metrics (prsApproved/prsRejected). Only writes for configured agents. | yes | `engine/ado.js`, `engine/github.js`, `engine/queries.js`, `test/unit.test.js` | engine/shared.js (keep) |
342
340
  | `queuePlanToPrd` | 4785-4822 | Queue a plan-to-prd work item with dedup check inside lock. Returns { queued, alreadyQueued, id, item } so callers (e.g. POST /api/plans/execute) can surface the new or existing work item id back to the dashboard. Return | yes | `test/unit.test.js`, `dashboard.js` | engine/shared.js (keep) |
343
341
  | `extractPlanDeclaredProject` | 4823-4845 | Extract plan declared project. | yes | `dashboard.js`, `engine.js` | engine/shared.js (keep) |
344
342
  | `extractPlanTargetProjects` | 4846-4974 | P-7a3f1c08: Parse a cross-repo plan's list of target projects. // Primary signal: `<!-- minions:targetProjects=a, b, c -->` HTML comment. // Fallback (human): ` Projects: a, b \| c` markdown line in the first 80 lines. // | yes | `dashboard.js`, `engine.js` | engine/shared.js (keep) |
345
- | `normalizeHarnessUsed` | 4975-5027 | normalizeHarnessUsed(raw) — validate/clamp an untrusted harnessUsed-shaped // object into the canonical { skills, mcpServers, commands, docs } form. // Drops malformed entries (non-objects, or missing the kind's required | yes | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
346
- | `groundHarnessUsed` | 5028-5099 | groundHarnessUsed(harnessUsed, propagated) — Stage 2 grounding cross-check // (P-c6f5e8b3). Normalizes an agent's self-reported `harnessUsed` and annotates // every surviving entry with a strict-boolean `grounded` field | yes | `engine/cli.js` | engine/shared.js (keep) |
347
343
  | `_bumpPruneDefaultClaudeStrip` | 5100-5114 | Bump rolling-daily strip counter for docs/deprecated.json#prune-default-claude-config // removal gate (>=30 consecutive days of zero strips). Best-effort; mirrors // engine/cleanup.js:1200-1213 _engine.legacyStatusMigrat | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
348
344
  | `pruneDefaultClaudeConfig` | 5115-5185 | Prune default claude config. | yes | `test/unit.test.js`, `dashboard.js`, `minions.js` | engine/shared.js (keep) |
349
345
  | `_isStringArrayOrNull` | 5186-5202 | Is string array or null. | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
@@ -398,14 +394,11 @@ means zero such files were found), **Proposed target module**.
398
394
  | `parseWorktreePorcelain` | 6139-6169 | Parse `git worktree list --porcelain` output. Pure — callers run the subprocess (sync `execSilent` or async `execAsync`) and feed stdout in. Returns `[{path, head, branch, bare, detached, locked, prunable}]`. `branch` st | yes | `engine/cleanup.js`, `engine/worktree-gc.js`, `test/unit.test.js`, `engine.js` | engine/worktree-utils.js (new) — general worktree path/branch/owner-marker helpers (worktree-pool.js/worktree-gc.js already own pool sizing + GC sweep; this new module would take the shared low-level helpers those two currently import from shared.js) |
399
395
  | `assertWorktreeOutsideProject` | 6170-6203 | Throws when `worktreePath` would land inside (or equal) `projectRoot`. Called by the engine spawn path before `git worktree add`, and by the cleanup sweep that audits already-registered worktrees per linked project. The | yes | `test/unit.test.js`, `dashboard.js`, `engine.js` | engine/worktree-utils.js (new) — general worktree path/branch/owner-marker helpers (worktree-pool.js/worktree-gc.js already own pool sizing + GC sweep; this new module would take the shared low-level helpers those two currently import from shared.js) |
400
396
  | `resolveProjectRootDir` | 6204-6259 | Resolve the project root directory used as the parent for git worktree paths during dispatch. Centralizes the fallback that engine spawnAgent used to do inline (`project.localPath ? path.resolve(project.localPath) : path | yes | `engine/create-pr-worktree.js`, `test/unit.test.js`, `engine.js` | engine/shared.js (keep) |
401
- | `resolveAgentCopilotHome` | 6260-6286 | Resolve a stable, repo-EXTERNAL COPILOT_HOME for spawned copilot agents. Copilot loads `$COPILOT_HOME/mcp-config.json` and SPAWNS every server in it on every process start. `--disable-mcp-server <name>` only hides a serv | yes | `test/unit/agent-copilot-home.test.js`, `engine.js` | engine/shared.js (keep) |
402
397
  | `resolveAgentTempBaseDir` | 6287-6308 | Derive a scratch/temp base dir on the same volume agent work already lives on — the configured worktree location (engine.worktreeRoot, default `../worktrees`). Agents (copilot/node/git + JVM-based MCP servers) write heav | yes | `test/unit/agent-temp-dir.test.js`, `engine.js` | engine/shared.js (keep) |
403
- | `ensureAgentCopilotHome` | 6309-6412 | Seed the agent COPILOT_HOME's mcp-config (copy of `~/.copilot/mcp-config.json` // MINUS `engine.copilotAgentDisabledMcpServers`) and return the home path. This // is the single source of truth for "what MCP servers may a | yes | `test/unit/agent-copilot-home.test.js`, `dashboard.js`, `engine.js` | engine/shared.js (keep) |
404
398
  | `resolveSpawnPaths` | 6413-6505 | Resolve the agent's working directory and (when needed) the parent dir for git worktree placement. Decouples the two concerns that spawnAgent used to conflate (W-mp73x32w000l143d): 1. cwd — where the agent process actual | yes | `engine/live-checkout.js`, `test/unit.test.js`, `engine.js` | engine/shared.js (keep) |
405
399
  | `_applyWorkdirOrThrow` | 6506-6518 | ── meta.workdir helpers (P-714ef144) // // Per-WI `meta.workdir` is a relative POSIX subpath under the spawn base // (project.localPath for read-only / live; worktree root for mutating). // When set, the engine lands the | no | _internal-only (no callers found outside shared.js)_ | engine/shared.js (keep) |
406
400
  | `validateWorkItemWorkdir` | 6519-6561 | Validate work item workdir. | yes | `dashboard.js`, `engine.js` | engine/shared.js (keep) |
407
401
  | `applyWorkdir` | 6562-6586 | Apply workdir. | yes | `engine.js` | engine/shared.js (keep) |
408
- | `filterProjectHarnessDirsForWorkdir` | 6587-6650 | Filter project harness dirs for workdir. | yes | `engine.js` | engine/shared.js (keep) |
409
402
  | `isAllowedOrigin` | 6651-6686 | Returns true if the origin-like header value (either an `Origin` header value such as `http://localhost:7331` or a full `Referer` URL) belongs to the local dashboard allowlist. Port-agnostic. Returns false for null/undef | yes | `test/unit.test.js`, `dashboard.js` | engine/shared.js (keep) |
410
403
  | `isAllowedDashboardOrigin` | 6687-6705 | Returns true if the given `Origin` header value should receive an `Access-Control-Allow-Origin` echo on GET/HEAD responses. STRICTER than `isAllowedOrigin` — that helper is the mutating-request gate (allows any localhost | yes | `test/unit.test.js`, `dashboard.js` | engine/shared.js (keep) |
411
404
  | `buildSecurityHeaders` | 6706-6721 | Returns the baseline set of security response headers to apply on every HTTP response from the dashboard. Values match OWASP defaults for a same-origin SPA served from 127.0.0.1. The HTML entry-point response intentional | yes | `test/unit.test.js`, `dashboard.js` | engine/shared.js (keep) |
@@ -609,7 +602,6 @@ Same columns as above, relative to `lifecycle.js`.
609
602
  | `parseAgentOutput` | 4020-4024 | Agent Output Parsing | yes | `engine/timeout.js`, `test/unit.test.js`, `engine.js` | engine/lifecycle.js (keep) |
610
603
  | `hasActionableFailureClass` | 4025-4047 | Has actionable failure class. | no | _internal-only (no callers found outside lifecycle.js)_ | engine/lifecycle.js (keep) |
611
604
  | `handleInjectionFlag` | 4048-4134 | F5 (W-mpeklod3000we69c): handle agent-reported injection attempts. The agent set `securityFlags.injectionAttempt: true` in its completion report after spotting attacker-controlled instructions inside an `<UNTRUSTED-INPUT | no | _internal-only (no callers found outside lifecycle.js)_ | engine/lifecycle.js (keep) |
612
- | `resolveHarnessPropagated` | 4135-4149 | resolveHarnessPropagated(dispatchItem) — P-c6f5e8b3. Locate the spawn-time // `_harnessPropagated` manifest for a dispatch. The in-memory dispatchItem // (spawnAgent's closure object) normally carries it directly; after | yes | `engine.js` | engine/lifecycle.js (keep) |
613
605
  | `parseCompletionReportFile` | 4150-4172 | Parse completion report file. | yes | `engine/timeout.js`, `test/unit.test.js` | engine/lifecycle.js (keep) |
614
606
  | `normalizeCompletionArtifacts` | 4173-4198 | Normalize completion artifacts. | yes | _internal-only (no callers found outside lifecycle.js)_ | engine/lifecycle.js (keep) |
615
607
  | `normalizeArtifactPath` | 4199-4212 | Normalize artifact path. | no | _internal-only (no callers found outside lifecycle.js)_ | engine/lifecycle.js (keep) |
@@ -1,6 +1,6 @@
1
1
  # Team Memory
2
2
 
3
- Minions agents share knowledge through a layered memory system. 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.
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
4
 
5
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
6
 
@@ -12,7 +12,21 @@ Multiple agents work the same repository in parallel and across days. Without sh
12
12
  - Agents re-litigate decisions the team already made (drift, regressions).
13
13
  - Human-flagged warnings (e.g., "do not self-approve PRs") get ignored because they live outside the prompt.
14
14
 
15
- Team memory closes the loop. The engine injects pinned context and recent team notes into every dispatch prompt automatically, and the playbooks instruct agents to consult `knowledge/` and `notes/inbox/` on disk before going wider. This keeps work cheap, decisions sticky, and prior learnings reusable.
15
+ Team memory closes the loop. The engine always injects pinned context. By default it also injects recent team and personal notes; when structured retrieval is enabled outside shadow mode, it replaces those broad appendices 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
+ Rollout is controlled by the default-off `memoryRetrieval` feature flag:
24
+
25
+ - **Flag off:** legacy prompt behavior.
26
+ - **Flag on + `memoryRetrievalShadowMode: true` (default):** compute retrieval and telemetry, but keep legacy prompts.
27
+ - **Flag on + shadow mode off:** inject `Relevant Memory` when retrieval finds evidence; otherwise fall back to legacy notes and personal memory.
28
+
29
+ Settings expose top-k, candidate count, byte budget, shadow mode, and episodic capture. The Inbox page's **Memory search** control shows provenance and status and supports explicit pin, retract, and restore actions.
16
30
 
17
31
  ## The four-tier read order
18
32
 
@@ -54,7 +68,7 @@ Agents read `knowledge/` directly with grep/glob/view. They **do not** write to
54
68
 
55
69
  `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.
56
70
 
57
- The engine injects `notes.md` into every prompt under a "Team Notes (MUST READ)" heading. When the file exceeds `ENGINE_DEFAULTS.maxNotesPromptBytes`, the engine keeps the most recent 10 sections, truncates the body, and appends a footer noting how many older sections live on disk so agents can read the full file when they need depth.
71
+ 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.
58
72
 
59
73
  ### 4. `notes/inbox/` — fresh agent findings
60
74
 
@@ -70,7 +84,7 @@ The consolidation sweep is what eventually promotes inbox notes into `knowledge/
70
84
 
71
85
  #### Failure-path exception
72
86
 
73
- 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 the failure is reported through the completion JSON, the PR/work-item comment, or the task-specific failure channel instead. This keeps the inbox a clean signal and prevents noise from drowning out durable findings.
87
+ 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.
74
88
 
75
89
  ## Rationale
76
90