mind-palace-graph 0.3.0

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 (51) hide show
  1. package/INSTALL.md +387 -0
  2. package/README.md +602 -0
  3. package/dist/api.d.ts +682 -0
  4. package/dist/api.js +660 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/cli.d.ts +95 -0
  7. package/dist/cli.js +856 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/format.d.ts +16 -0
  10. package/dist/format.js +199 -0
  11. package/dist/format.js.map +1 -0
  12. package/dist/fuzzy.d.ts +45 -0
  13. package/dist/fuzzy.js +150 -0
  14. package/dist/fuzzy.js.map +1 -0
  15. package/dist/index.d.ts +9 -0
  16. package/dist/index.js +528 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/mcp-server.d.ts +24 -0
  19. package/dist/mcp-server.js +187 -0
  20. package/dist/mcp-server.js.map +1 -0
  21. package/dist/mind-palace.d.ts +148 -0
  22. package/dist/mind-palace.js +780 -0
  23. package/dist/mind-palace.js.map +1 -0
  24. package/dist/nodes.d.ts +57 -0
  25. package/dist/nodes.js +220 -0
  26. package/dist/nodes.js.map +1 -0
  27. package/dist/pagination.d.ts +41 -0
  28. package/dist/pagination.js +63 -0
  29. package/dist/pagination.js.map +1 -0
  30. package/dist/palace-format.d.ts +30 -0
  31. package/dist/palace-format.js +146 -0
  32. package/dist/palace-format.js.map +1 -0
  33. package/dist/rg.d.ts +34 -0
  34. package/dist/rg.js +288 -0
  35. package/dist/rg.js.map +1 -0
  36. package/dist/sources.d.ts +87 -0
  37. package/dist/sources.js +457 -0
  38. package/dist/sources.js.map +1 -0
  39. package/dist/tokens.d.ts +35 -0
  40. package/dist/tokens.js +95 -0
  41. package/dist/tokens.js.map +1 -0
  42. package/dist/types.d.ts +236 -0
  43. package/dist/types.js +8 -0
  44. package/dist/types.js.map +1 -0
  45. package/package.json +67 -0
  46. package/skills/mpg-context/SKILL.md +556 -0
  47. package/skills/mpg-context/references/anti-patterns.md +133 -0
  48. package/skills/mpg-context/references/integration.md +123 -0
  49. package/skills/mpg-context/references/mind-palace.md +217 -0
  50. package/skills/mpg-context/references/multi-agent.md +147 -0
  51. package/skills/mpg-context/references/sources.md +120 -0
@@ -0,0 +1,133 @@
1
+ # Anti-patterns
2
+
3
+ What NOT to do with mpg, and why.
4
+
5
+ ## Search
6
+
7
+ **Don't use `deep` effort for a quick scan.**
8
+ `quick` is 1/10th the cost. Reserve `deep` (2000-token windows, 100
9
+ nodes) for the final answer-grounding pass, not initial recon.
10
+
11
+ **Don't search the same pattern twice without stashing.**
12
+ If you'll need a pattern more than once, `mpg_stash` it the first
13
+ time. Stash storage is cheap; re-running a search is not.
14
+
15
+ **Don't use mpg to read a single file.**
16
+ The host's `read` tool is faster and clearer. mpg is for *searching*
17
+ across multiple files, not retrieving one.
18
+
19
+ **Don't forget `page: 1` when paginating.**
20
+ Without an explicit page, mpg returns everything (up to `max_nodes`).
21
+ For results expected to exceed 10 hits, pass `page: 1, page_size: 5`
22
+ so you can decide whether to keep going.
23
+
24
+ **Don't omit `--in` for non-trivial searches.**
25
+ With no source, mpg reads stdin or errors out. Always be explicit
26
+ about where you're searching.
27
+
28
+ ## Mind palace
29
+
30
+ **Don't stash and immediately drop.**
31
+ If you knew you weren't going to need it, you shouldn't have stashed.
32
+ The cost is the stash decision, not the storage.
33
+
34
+ **Don't reuse stash names across unrelated investigations.**
35
+ A new investigation = a new task = a new palace (`--mp-path` /
36
+ `MPG_MIND_PALACE`) OR a name-prefixed stash. Reusing names silently
37
+ overwrites or merges, which surprises the model later.
38
+
39
+ **Don't use comma-separated stash names in `compose` without quoting.**
40
+ The shell may split them. Prefer space-separated:
41
+ `--mp-compose auth-todos perf-hotspots`.
42
+
43
+ **Don't create stashes with names that look like flags.**
44
+ Avoid names like `--help`, `-v`, etc. mpg handles these but the LLM
45
+ should not have to disambiguate.
46
+
47
+ **Don't skip `--mp-prune-dry-run`.**
48
+ Always preview a prune before committing. Stash mistakes are
49
+ permanent — the JSON is overwritten in place.
50
+
51
+ **Don't pound a shared palace with hundreds of near-simultaneous writes.**
52
+ v0.2.4 added a `.lock` + atomic-rename write path, so concurrent
53
+ writers no longer lose data — but they *do* serialize. A swarm of
54
+ agents stashing in tight loops over one palace turns into a
55
+ single-writer queue. For high-write fan-out, give each agent its own
56
+ palace (Layout A in `multi-agent.md`) and compose at the end.
57
+
58
+ **Don't ignore a "WARNING — mind palace is corrupt" stderr line.**
59
+ When mpg encounters an unparseable palace it copies the file aside as
60
+ `<palace>.corrupt.<timestamp>`, taints the in-memory copy, and
61
+ **refuses to save** for the rest of that process. If you press on
62
+ without inspecting the backup, the next process will start from a
63
+ real empty palace once `MPG_FORCE_RESET=1` is set — you'll lose every
64
+ stash that wasn't already on disk in a parseable state. Read the
65
+ backup file first; recover by hand-merging or by deleting the
66
+ corrupt original.
67
+
68
+ **Don't ignore `result.errors[]`.**
69
+ When some sources error and others succeed, `status` is `"partial"`
70
+ and the per-source failures land in `errors: [{source, message}]`.
71
+ Treating a partial result as a clean "no matches" leads the agent
72
+ into wrong conclusions about the corpus. Always check the array;
73
+ if it's non-empty, decide explicitly whether to retry, fall back,
74
+ or surface the failure.
75
+
76
+ **Don't stash without tags.**
77
+ At >10 stashes, untagged ones become impossible to filter or prune.
78
+ Tag every stash with at least one topic word.
79
+
80
+ **Don't forget TTL on transient findings.**
81
+ Use `--mp-ttl 2h` (or similar) on scratch / exploratory stashes so
82
+ they auto-reap. Manual pruning at agent shutdown is fine too, but TTL
83
+ is the cheaper default. Combined with `--mp-prune-expired` at the
84
+ start of a session, this is how a long-running palace stays small
85
+ without manual gardening.
86
+
87
+ **Don't let the palace grow unbounded across a long-context task.**
88
+ Stash count creep is the silent killer of multi-hour agent loops:
89
+ `--mp-from` over a 200-stash palace gets noisy fast. Set a budget
90
+ (say 20–30 active stashes) and run `--mp-prune-keep 30` or
91
+ `--mp-prune-older-than 6h` every few major turns. Always
92
+ `--mp-prune-dry-run` first.
93
+
94
+ **Don't build dense relationship graphs you won't traverse.**
95
+ Edges are cheap, but a graph nobody walks is noise. Only `--mp-link`
96
+ when you actually plan to `--mp-related` or `--mp-graph` later.
97
+
98
+ ## Sources
99
+
100
+ **Don't pipe gigabyte files to `--cmd` or `--stdin`.**
101
+ mpg buffers in memory (64 MB cap for `--cmd`). For huge corpora, use
102
+ `--in` with file paths so ripgrep streams.
103
+
104
+ **Don't `--url` against SPA-rendered pages.**
105
+ mpg fetches raw HTML — no JS execution. SPAs return empty shells.
106
+ Use a real HTTP client + render step upstream if you need it.
107
+
108
+ **Don't combine `--cmd` and `--stdin` for the same content.**
109
+ Pick one. Combining them is legal but confusing — they coexist for
110
+ **different** content sources, not the same one.
111
+
112
+ ## Output format
113
+
114
+ **Don't parse `llm` format programmatically.**
115
+ It's designed for the model to read. For machine consumption use
116
+ `--format json`.
117
+
118
+ **Don't strip the `<mpg result ...>` wrapper before showing the
119
+ result to the model.**
120
+ The header carries the pattern, effort, status, pagination state, and
121
+ token budget — load-bearing for the model's reasoning. Keep it.
122
+
123
+ ## Integration
124
+
125
+ **Don't register the MCP server at project scope when you want it
126
+ everywhere.**
127
+ Use `--scope user` so it's available across all projects:
128
+ `claude mcp add --scope user mpg -- node <path>`.
129
+
130
+ **Don't shell out from inside an MCP host for the five core tools.**
131
+ That's what the MCP server exists for. Shell-out only for the wider
132
+ mind-palace surface (relationships, prune, intersect, except) that
133
+ isn't exposed via MCP yet.
@@ -0,0 +1,123 @@
1
+ # mpg Integration Paths
2
+
3
+ mpg works through three different surfaces. Pick the one that matches
4
+ how your agent calls tools.
5
+
6
+ | Path | Best for | Cost |
7
+ | :--- | :--- | :--- |
8
+ | **MCP server** | Claude Desktop, Claude Code, Cline, Windsurf, Continue.dev | One-time config; auto-discovery of tools |
9
+ | **CLI shell-out** | Any agent that can `Bash`/`exec` | One-time `npm install -g`; no tool registration |
10
+ | **Programmatic import** | Custom Anthropic / Google SDK agents | TS/JS import; full type safety |
11
+
12
+ ## MCP server
13
+
14
+ mpg ships an MCP server that exposes the five core tools over stdio.
15
+ No network ports, no extra config beyond the launch command.
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "mpg": {
21
+ "command": "node",
22
+ "args": ["<path-to-global-install>/dist/mcp-server.js"]
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ For Claude Code, register via the CLI (recommended, user scope makes it
29
+ available across all projects):
30
+
31
+ ```bash
32
+ claude mcp add --scope user mpg -- node "<global-install>/dist/mcp-server.js"
33
+ ```
34
+
35
+ The exposed tools are `mpg_search`, `mpg_stash`, `mpg_list_stashes`,
36
+ `mpg_get_stash`, `mpg_drop_stash`. The wider mind-palace surface
37
+ (relationships, prune, intersect, etc.) is **not** available through
38
+ MCP today — drop to CLI for those.
39
+
40
+ ## CLI shell-out
41
+
42
+ Any agent that can run a shell command can use mpg directly:
43
+
44
+ ```bash
45
+ mpg "TODO" --in src/ --effort quick --format json
46
+ ```
47
+
48
+ The `--format json` output is designed for machine consumption: it
49
+ includes `status`, `nodes[]`, `pagination`, `total_nodes`, etc. — feed
50
+ it back into the agent as the tool result.
51
+
52
+ When to prefer CLI over MCP:
53
+
54
+ - You need a flag that isn't exposed as an MCP tool (relationships,
55
+ prune, intersect, except, TTL).
56
+ - You're chaining mpg with other shell tools (e.g.
57
+ `mpg "errors" --cmd "git log -100" --mp-stash recent`).
58
+ - You want a quick recon and the MCP roundtrip is overkill.
59
+
60
+ When to prefer MCP over CLI:
61
+
62
+ - You want the tool name visible to the model in its tool list (helps
63
+ with tool selection).
64
+ - You're running in a host that doesn't auto-allow `mpg` in Bash
65
+ permissions (MCP bypasses Bash permission prompts).
66
+
67
+ ## Programmatic import
68
+
69
+ For TS/Node agents that embed mpg directly:
70
+
71
+ ```ts
72
+ import {
73
+ search,
74
+ stash,
75
+ listStashes,
76
+ getStash,
77
+ dropStash,
78
+ claudeTools,
79
+ geminiTools,
80
+ } from "mpg-cli";
81
+
82
+ // Run a search
83
+ const result = await search({
84
+ pattern: "TODO",
85
+ in: ["src/"],
86
+ effort: "quick",
87
+ page: 1,
88
+ pageSize: 5,
89
+ });
90
+
91
+ // Stash it for later composition
92
+ await stash(result, {
93
+ name: "auth-todos",
94
+ note: "Auth TODOs to review",
95
+ tags: ["auth", "p0"],
96
+ });
97
+
98
+ // Register with Anthropic SDK
99
+ const response = await anthropic.messages.create({
100
+ model: "claude-sonnet-4-6",
101
+ tools: [...claudeTools],
102
+ // ...
103
+ });
104
+
105
+ // Or Google SDK
106
+ const model = genAI.getGenerativeModel({
107
+ model: "gemini-2.5-pro",
108
+ tools: [{ functionDeclarations: geminiTools }],
109
+ });
110
+ ```
111
+
112
+ Pre-built tool schemas are exported as `claudeTools` and `geminiTools`.
113
+ Each entry is shaped for its respective provider — no manual schema
114
+ authoring needed.
115
+
116
+ ## Quick decision
117
+
118
+ ```
119
+ Claude Desktop / Code / Cline / Windsurf / Continue? → MCP
120
+ Custom Anthropic / Google SDK agent in TS/Node? → import
121
+ Pi agent, Aider, Cursor, shell-only agent? → CLI
122
+ Need relationships / prune / intersect right now? → CLI (even from inside an MCP host)
123
+ ```
@@ -0,0 +1,217 @@
1
+ # Mind Palace — full surface
2
+
3
+ The palace is a JSON file (default `./.mpg/mind-palace.json`) holding
4
+ named **stashes** of search results. Stashes are addressable: future
5
+ searches can use them as inputs, compose them, intersect them, link
6
+ them into a graph, and prune them by age/tag/count.
7
+
8
+ ## Lifecycle
9
+
10
+ ```
11
+ SEARCH → STASH → COMPOSE / FROM / INTERSECT / EXCEPT
12
+
13
+ LIST ← TAG, FILTER, PAGINATE
14
+
15
+ LINK → RELATED → GRAPH (traverse)
16
+
17
+ PRUNE (by age / count / tag / TTL / all) → DROP
18
+ ```
19
+
20
+ ## Stash operations
21
+
22
+ | CLI flag | What it does |
23
+ | :--- | :--- |
24
+ | `--mp-stash <name> <note>` | Save current search's results. Merges into existing (dedup by file:line) unless `--mp-replace`. |
25
+ | `--mp-stash-note <note>` | Set note separately from the stash flag. |
26
+ | `--mp-stash-tag <tag>` / `--mp-tag <tag>` | Tag the stash (repeatable). |
27
+ | `--mp-replace` | Overwrite an existing stash outright. |
28
+ | `--mp-ttl <duration>` | Auto-expiry (`30m`, `2h`, `7d`). Expired stashes are reaped on next `--mp-list` / `--mp-get`. |
29
+ | `--mp-stash-locations` | Save only file:line pointers (no context text). Use for lean stashes when you'll re-search later. |
30
+
31
+ ## Recall operations
32
+
33
+ | CLI flag | What it does |
34
+ | :--- | :--- |
35
+ | `--mp-list` | List all stashes with relative timestamps (`3m ago`, `2d ago`). |
36
+ | `--mp-list-tag <tag>` | Filter list by tag (repeatable). |
37
+ | `--mp-get <name>` | Show full contents of one stash. |
38
+ | `--mp-drop <name>` | Remove a stash. |
39
+
40
+ ## Set operations over file lists
41
+
42
+ These all run a **fresh search**, scoped to a derived file list.
43
+
44
+ | Operation | CLI | Files searched |
45
+ | :--- | :--- | :--- |
46
+ | **Scope** | `--mp-from <name>` | Files in the named stash |
47
+ | **Union** | `--mp-compose <a> <b> ...` | Files in **any** of the named stashes |
48
+ | **Intersection** | `--mp-intersect <a> <b> ...` | Files in **all** of the named stashes |
49
+ | **Difference** | `--mp-except <a>` or `--mp-except <a> <b> ...` | Files **not** in the named stash(es) |
50
+
51
+ Examples:
52
+
53
+ ```bash
54
+ # Re-search a single stash's files
55
+ mpg "rate.limit" --mp-from auth-todos
56
+
57
+ # Files mentioned in any of two stashes
58
+ mpg "TODO" --mp-compose auth-todos perf-hotspots
59
+
60
+ # Files mentioned in BOTH stashes
61
+ mpg "TODO" --mp-intersect auth-todos perf-hotspots
62
+
63
+ # Files in auth-todos but NOT in deprecated
64
+ mpg "TODO" --mp-except deprecated
65
+ ```
66
+
67
+ The MCP tools today expose `from` and `compose` only — drop to CLI for
68
+ `intersect` and `except`.
69
+
70
+ ## Relationships (the graph in mind-palace-graph)
71
+
72
+ Stashes can be linked into a directed graph. The graph is what lets
73
+ you **traverse the investigation by intent** instead of by remembering
74
+ stash names — `--mp-graph <root> 3` reconstructs an entire thread
75
+ topology in one CLI call, which is the lifeline when a conversation
76
+ gets compacted away mid-task.
77
+
78
+ | CLI flag | What it does |
79
+ | :--- | :--- |
80
+ | `--mp-link <from> <to> <type> [note]` | Create a directed edge. |
81
+ | `--mp-unlink <from> <to>` | Remove an edge. |
82
+ | `--mp-related <name>` | Show all inbound + outbound neighbors of `name`. |
83
+ | `--mp-graph <name> [depth]` | BFS traversal from `name` up to `[depth]` levels (default 3). |
84
+
85
+ ### Edge type conventions
86
+
87
+ Edge types are unenforced strings, but consistent vocabulary makes the
88
+ graph readable later. Common types:
89
+
90
+ | Type | Meaning |
91
+ | :--- | :--- |
92
+ | `depends-on` | "Reading B is a prerequisite for reading A." |
93
+ | `supersedes` | "A is the current view; B is the old one. Ignore B." |
94
+ | `see-also` | "B is a related thread worth surfacing alongside A." |
95
+ | `parent-of` / `child-of` | Hierarchical decomposition of one investigation into subtopics. |
96
+ | `blocks` | "A can't ship until B is resolved." |
97
+ | `contradicts` | "A and B disagree — reconciliation needed." |
98
+
99
+ Pick a vocabulary at the start of an investigation and stay consistent
100
+ within it. Mixing `depends-on` with `requires` for the same concept
101
+ makes `--mp-graph` output noisy without adding signal.
102
+
103
+ ### Workflow: tracking a multi-thread investigation
104
+
105
+ The pattern that pays off: build stashes as you investigate (always
106
+ with TTLs + tags), link them the moment you notice a relationship,
107
+ then traverse by intent in future sessions.
108
+
109
+ ```bash
110
+ # Session 1 — building the topology
111
+ mpg "JWT" --in src/auth/ --mp-stash auth-jwt --mp-tag rewrite --mp-ttl 24h
112
+ mpg "JWT" --in docs/spec/ --mp-stash spec-jwt --mp-tag rewrite --mp-ttl 24h
113
+ mpg "JWT" --in src/legacy/ --mp-stash legacy-jwt --mp-tag rewrite --mp-ttl 24h
114
+
115
+ mpg --mp-link auth-jwt spec-jwt see-also "implementation of the spec"
116
+ mpg --mp-link auth-jwt legacy-jwt supersedes "post-rewrite, legacy goes away"
117
+
118
+ # Session 2 — navigation, no need to remember names
119
+ mpg --mp-related auth-jwt # one-hop neighbors with edge labels
120
+ mpg --mp-graph auth-jwt 3 # full BFS, three hops out
121
+
122
+ # When the conversation gets compacted and you've lost the thread:
123
+ mpg --mp-graph <known-root> 3
124
+ # → reconstructs the whole investigation topology, with edge types
125
+ # that tell you what's current, what's superseded, what blocks what.
126
+ ```
127
+
128
+ ### When to link (and when not to)
129
+
130
+ 1. **Only link what you'll traverse.** Edges are cheap to make and
131
+ cheap to store, but a graph nobody walks is just noise on
132
+ `--mp-related`. If you wouldn't run `--mp-graph` later, skip the
133
+ link.
134
+ 2. **Link when discovery is fresh.** The right moment is when you
135
+ notice the relationship. Three sessions later you won't remember
136
+ why two stashes mattered together.
137
+ 3. **Don't link across unrelated tasks.** If you maintain one palace
138
+ per task (`MPG_MIND_PALACE=.mpg/<task>.json`), this is automatic —
139
+ cross-task links can't even be expressed.
140
+ 4. **Relink with confidence — it's atomic.** `--mp-unlink` then
141
+ `--mp-link` is a no-op-loss operation; the diff-based save in
142
+ v0.2.5 ensures both edits land cleanly even under concurrent
143
+ writers.
144
+
145
+ ### Quick examples by use case
146
+
147
+ | Situation | Linkage |
148
+ | :--- | :--- |
149
+ | Refactor that obsoletes an old subsystem | `--mp-link new-impl old-impl supersedes "after-rewrite"` |
150
+ | Implementation depends on a shared library you've already mapped | `--mp-link feature-x lib-y depends-on "uses Y's session API"` |
151
+ | Spec and code drift you need to reconcile | `--mp-link spec-claim impl-reality contradicts "spec says X, code does Y"` |
152
+ | Decomposing an epic into discrete threads | `--mp-link epic-payments stripe-webhooks child-of` then `--mp-graph epic-payments 2` to walk the whole epic |
153
+ | Cross-referencing parallel tracks (impl + tests + docs) | Three stashes, two `see-also` edges between them |
154
+
155
+ ## Pruning
156
+
157
+ A palace grows unbounded unless pruned. Always preview first.
158
+
159
+ | CLI flag | Effect |
160
+ | :--- | :--- |
161
+ | `--mp-prune-older-than <dur>` | Stashes not updated within duration. |
162
+ | `--mp-prune-keep <n>` | Keep only the N most recently updated. |
163
+ | `--mp-prune-tag <tag>` | All stashes carrying the tag. |
164
+ | `--mp-prune-expired` | All TTL-expired stashes. |
165
+ | `--mp-prune-all` | Entire palace. Requires `--mp-prune-confirm`. |
166
+ | `--mp-prune-dry-run` | Preview only — do not delete. **Use this first.** |
167
+
168
+ ```bash
169
+ mpg --mp-prune-older-than 7d --mp-prune-dry-run # preview
170
+ mpg --mp-prune-older-than 7d # commit
171
+ ```
172
+
173
+ TTL-tagged stashes are *also* auto-reaped on every `--mp-list` or
174
+ `--mp-get` — no explicit prune required for the TTL case.
175
+
176
+ ## Multi-palace isolation
177
+
178
+ Use a separate palace per task to avoid context bleed.
179
+
180
+ ```bash
181
+ # Explicit per-invocation:
182
+ mpg "TODO" --in src/ --mp-stash t42 "..." --mp-path .mpg/task-42.json
183
+
184
+ # Or via env, so all subsequent calls use the same palace:
185
+ export MPG_MIND_PALACE=.mpg/task-42.json
186
+ mpg "TODO" --in src/ --mp-stash t42 "..."
187
+ mpg --mp-list
188
+ ```
189
+
190
+ The default `./.mpg/mind-palace.json` is searched walking up from CWD
191
+ (like `.gitignore`), so monorepo subdirs share a project palace by
192
+ default.
193
+
194
+ ## Storage format
195
+
196
+ Each stash holds:
197
+
198
+ ```jsonc
199
+ {
200
+ "name": "auth-todos",
201
+ "note": "Auth TODOs to review",
202
+ "tags": ["auth", "p0"],
203
+ "pattern": "TODO",
204
+ "effort": "quick",
205
+ "nodes": [ /* file:line + context */ ],
206
+ "sources": [ /* canonical file paths */ ],
207
+ "created_at": "2025-...",
208
+ "updated_at": "2025-...",
209
+ "expires_at": "2025-..." // if --mp-ttl was set
210
+ }
211
+ ```
212
+
213
+ Plus a top-level `relationships: [{from, to, type, note, created_at}]`
214
+ array for edges.
215
+
216
+ The file is plain JSON — safe to inspect, version-control, or hand-edit
217
+ for one-off corrections.
@@ -0,0 +1,147 @@
1
+ # Multi-agent palace patterns
2
+
3
+ The mind palace is a single JSON file. Multiple processes / agents
4
+ sharing it coordinate via a per-palace `.lock` file plus an atomic
5
+ write path (tmp file + rename + on-disk re-read-and-merge under the
6
+ lock). The race conditions that earlier versions had — silently lost
7
+ stashes when two agents stashed in the same second — no longer apply.
8
+
9
+ You still pick a layout based on **what kind of context you want
10
+ agents to share**, not based on data-loss risk.
11
+
12
+ ## Recommended layouts
13
+
14
+ ### Layout A: per-agent isolated palaces
15
+
16
+ Each agent gets its own palace file. Use when agents are pursuing
17
+ genuinely independent investigations and you don't want context bleed
18
+ between them.
19
+
20
+ ```bash
21
+ # Agent A
22
+ MPG_MIND_PALACE=.mpg/agent-a.json mpg "TODO" --in src/ --mp-stash mine "..."
23
+
24
+ # Agent B
25
+ MPG_MIND_PALACE=.mpg/agent-b.json mpg "TODO" --in src/ --mp-stash mine "..."
26
+ ```
27
+
28
+ Pros: zero lock contention, easy to garbage-collect (just `rm` the
29
+ file when the agent finishes), one task = one palace = one mental model.
30
+ Cons: agents can't see each other's findings — you have to manually
31
+ copy or merge if context-sharing turns out to matter.
32
+
33
+ ### Layout B: shared read-only palace + per-agent scratch
34
+
35
+ One canonical palace, populated by a coordinator agent. Worker agents
36
+ **read** from it (`--mp-from`, `--mp-compose`, `--mp-list`,
37
+ `--mp-get`) but write to their own scratch palaces.
38
+
39
+ ```bash
40
+ # Coordinator stashes the project's key contexts:
41
+ MPG_MIND_PALACE=.mpg/shared.json mpg "TODO" --in src/auth/ \
42
+ --mp-stash auth-overview "Auth subsystem"
43
+
44
+ # Workers read from shared, write to their own scratch:
45
+ MPG_MIND_PALACE=.mpg/worker-1.json \
46
+ mpg "rate.limit" --mp-from auth-overview --mp-stash w1-findings "..."
47
+ ```
48
+
49
+ Pros: shared context with a clear authorship model — coordinator
50
+ owns the canonical map, workers own their own findings.
51
+ Cons: workers can't contribute back to the shared palace without an
52
+ explicit merge step.
53
+
54
+ ### Layout C: shared read-write palace
55
+
56
+ All agents share one palace. As of v0.2.4 this is safe under
57
+ concurrent writes: each save acquires a `.lock` file, re-reads the
58
+ on-disk palace under the lock, merges any stashes the other writer
59
+ added, then renames atomically into place.
60
+
61
+ ```bash
62
+ # All agents
63
+ export MPG_MIND_PALACE=.mpg/team.json
64
+ ```
65
+
66
+ Pros: maximum visibility — everyone sees everyone's findings as
67
+ soon as they're written.
68
+ Cons: lock contention if many agents stash in a tight loop. For
69
+ extreme write loads (autopilot/swarm patterns with hundreds of
70
+ near-simultaneous stashes), still prefer Layout A or B — the lock
71
+ will serialize you, and serialization at high write rates means
72
+ the slowest agent's stash latency becomes everyone's stash latency.
73
+
74
+ ## Concurrency model (what the lock actually does)
75
+
76
+ mpg's write path is:
77
+
78
+ 1. Acquire `<palace>.lock` (sibling file, `O_EXCL`). Backs off with
79
+ jitter for up to 2s; force-breaks a stale lock older than 30s.
80
+ 2. Read the on-disk palace JSON (the version *some other process*
81
+ may have written since we last loaded).
82
+ 3. Merge stashes the on-disk version has that our in-memory copy
83
+ doesn't (last-write-wins on collision by stash name).
84
+ 4. Write to `<palace>.tmp.<pid>.<rand>` then atomically rename onto
85
+ the real path.
86
+ 5. Release the lock.
87
+
88
+ Consequence for agent design:
89
+
90
+ - **Two agents stashing different stash names in parallel: both land.**
91
+ The merge step in (3) ensures it.
92
+ - **Two agents stashing the same name in parallel: last writer wins
93
+ for that stash.** This is intentional — `mpg_stash` with `replace`
94
+ semantics is supposed to overwrite. If you want a merge of two
95
+ parallel updates to the same stash, model them as two different
96
+ names and compose them later.
97
+ - **A crashed agent that died mid-write leaves a stale lock.** Other
98
+ agents will detect this (mtime > 30s) and break it. No manual
99
+ cleanup needed unless something is very wrong (in which case
100
+ `rm <palace>.lock`).
101
+ - **A corrupted palace file is preserved, not overwritten.** mpg
102
+ copies it aside as `<palace>.corrupt.<timestamp>` and **refuses
103
+ to save** for the rest of that process unless `MPG_FORCE_RESET=1`
104
+ is set. Inspect the backup before forcing.
105
+
106
+ The palace file is still plain JSON. If you suspect divergence between
107
+ two agents' worldviews, diff the file directly — there's no opaque
108
+ binary state.
109
+
110
+ ## Naming conventions for shared palaces
111
+
112
+ When multiple agents share a palace, name stashes with a prefix to
113
+ avoid collisions and make ownership obvious:
114
+
115
+ ```
116
+ <agent-id>-<topic> e.g. coordinator-auth, worker1-perf
117
+ <task-id>-<topic> e.g. t42-auth, t42-perf
118
+ <phase>-<topic> e.g. grounding-auth, planning-auth
119
+ ```
120
+
121
+ Tag with the agent or task ID too:
122
+
123
+ ```bash
124
+ mpg "TODO" --in src/ --mp-stash w1-auth "Worker 1 auth findings" \
125
+ --mp-tag worker1 --mp-tag task-42
126
+ ```
127
+
128
+ That makes `--mp-list-tag worker1` or `--mp-prune-tag worker1` work
129
+ cleanly when a worker is done.
130
+
131
+ ## Lifecycle hygiene
132
+
133
+ - **Use TTL on transient findings**: `--mp-ttl 2h` on a worker's
134
+ exploratory stashes so the palace self-cleans.
135
+ - **Prune by tag on agent shutdown**: when a worker finishes, run
136
+ `mpg --mp-prune-tag <worker-id>` to drop its scratch.
137
+ - **Snapshot before destructive ops**: `cp .mpg/shared.json .mpg/shared.bak`
138
+ before any `--mp-prune-*`.
139
+
140
+ ## Anti-patterns
141
+
142
+ - **Multiple concurrent writers to one palace with high frequency.**
143
+ Will lose stashes. Use Layout A or serialize writes.
144
+ - **Sharing a palace across unrelated tasks.** Context bleed makes
145
+ stashes harder to find and prune. One palace per task.
146
+ - **Stashing tool outputs with no TTL in a long-running agent.**
147
+ The palace grows unbounded.