- [x] The prompt lists server names, not schemas (`src/prompt.ts`: `mcpServers` names-only, direct schemas omitted under phi)
- [x] Calls go through the same permission rules and guard as a built-in (`permission mcp_call` + `bindMcpGuard`, intra-turn suppressed, ask-to-approve otherwise)
- [x] Keep per-tool registration as an option: a two-tool server is cheaper registered directly (`mcpExpose=direct` / `mcpExpose=auto`)
- [x] Test: a configured server contributes no schema to the request until `mcp_call` (`test/mcp.test.ts` phi vs direct)
- [x] Mark each tool as mutating where it is defined, not in a list beside it (`src/tool-utils.ts``withMeta({ set, mutating })`, each tool file wraps its `tool({` definitions — 41 tools across `tools.ts`/`tools-extra.ts`/`tools-git.ts`/`tools-net.ts`)
- [x]`TOOL_SETS` covers every registered tool, checked rather than assumed (`setsFrom(tools)` derives from `_meta`, `TOOL_SETS`/`MUTATING_TOOLS` are derived, `DEFAULT_PERMISSIONS` covers all mutating)
- [x] Test: a tool in no set, or a mutating tool outside `MUTATING_TOOLS`, fails the suite (`test/tool-derive.test.ts`: 5 tests — `_meta` present, `TOOL_SETS` derived + exact-once + coverage, `MUTATING_TOOLS` derived, permissions coverage, mutating consistency)
- [x]`task` accepts several investigations and runs them together (`src/subagent.ts`: `tasks: TaskSpec[]` union, `runOne` + `Promise.all` fan-out up to 8, panel emits start/step/result/end per subagent)
- [x] Test: two delegated searches overlap in time rather than queueing (`test/subagent-parallel.test.ts`: delayed greps overlap < 2*delay, both headings in one tool result)
- [x] Snapshot files before each prompt, capped at the 100 most recent (`src/snapshot.ts` hook + `session.ts` per-turn capture, cap 100 via `SnapshotStack`)
- [x] Say plainly what is not covered: a `bash` command's effects cannot be snapshotted (notice in undo/redo output + `src/snapshot.ts` doc)
- [x] Test: an edit is reverted, and the model's own record of it goes with it (`test/undo.test.ts`: undo file+messages, redo file+messages, bash-not-snapshotted, cap 100)
- [x] Pricing table needs a source note and a date; rates drift and ours are hand-entered (`src/pricing.ts``PRICING_VERIFIED_AT='2026-09-09'` + source URLs in doc, `/cost` shows `pricing verified: 2026-09-09 (est., verify before billing)`)
- [x]`estimateTokens` divides JSON length by four. Good enough for a compaction threshold,
real tokenizer (`src/prune.ts` doc now says heuristic + `(est.)` label, `src/session.ts`/`src/ui/panel-bodies.ts`/`src/ui/App.tsx` all display `~N tokens (est.)` / `~N est. in context`)
- [x]`listPaths` walks up to 5000 files once per session. Fine for a repo, wasteful in a
monorepo, and it never notices a file created after the first `@` (`src/session.ts``fileChangeSeq` bumped on `recordBeforeWrite` + `restoreFiles`, `src/ui/App.tsx` invalidates `paths` on seq change so next `@` re-walks)
- [x]`MUTATING_TOOLS` is now only used by tests and docs; the permission defaults are what
actually gate a write. Either delete it or make the defaults derive from it (`src/permission.ts``BASE_PERMISSIONS` + `buildDefaults()` derives mutating entries from `tools.ts``MUTATING_TOOLS` via `require('./tools')` — `_meta.mutating` single source, fallback list if require fails)