projectstore-codex 0.0.1 → 0.28.2

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 (184) hide show
  1. package/.codex-plugin/plugin.json +48 -0
  2. package/README.md +15 -7
  3. package/bin/projectstore-codex.mjs +88 -0
  4. package/hooks/hooks.json +59 -0
  5. package/node_modules/projectstore/.claude-plugin/marketplace.json +40 -0
  6. package/node_modules/projectstore/.claude-plugin/plugin.json +23 -0
  7. package/node_modules/projectstore/.mcp.json +14 -0
  8. package/node_modules/projectstore/AGENTS.md +26 -0
  9. package/node_modules/projectstore/LICENSE +21 -0
  10. package/node_modules/projectstore/README.md +284 -0
  11. package/node_modules/projectstore/agents/archaeologist.md +76 -0
  12. package/node_modules/projectstore/agents/clerk.md +93 -0
  13. package/node_modules/projectstore/agents/critic.md +94 -0
  14. package/node_modules/projectstore/agents/librarian.md +81 -0
  15. package/node_modules/projectstore/agents/planner.md +80 -0
  16. package/node_modules/projectstore/agents/reviewer.md +98 -0
  17. package/node_modules/projectstore/bin/projectstore.mjs +7 -0
  18. package/node_modules/projectstore/commands/adr.md +57 -0
  19. package/node_modules/projectstore/commands/agents.md +180 -0
  20. package/node_modules/projectstore/commands/bind.md +128 -0
  21. package/node_modules/projectstore/commands/codemap.md +50 -0
  22. package/node_modules/projectstore/commands/concept.md +17 -0
  23. package/node_modules/projectstore/commands/doctor.md +166 -0
  24. package/node_modules/projectstore/commands/epic.md +40 -0
  25. package/node_modules/projectstore/commands/graph.md +56 -0
  26. package/node_modules/projectstore/commands/kanban.md +40 -0
  27. package/node_modules/projectstore/commands/meeting.md +17 -0
  28. package/node_modules/projectstore/commands/reconcile.md +73 -0
  29. package/node_modules/projectstore/commands/research.md +17 -0
  30. package/node_modules/projectstore/commands/review.md +89 -0
  31. package/node_modules/projectstore/commands/runbook.md +17 -0
  32. package/node_modules/projectstore/commands/scaffold.md +23 -0
  33. package/node_modules/projectstore/commands/search.md +22 -0
  34. package/node_modules/projectstore/commands/spec.md +91 -0
  35. package/node_modules/projectstore/commands/status.md +27 -0
  36. package/node_modules/projectstore/commands/statusline.md +46 -0
  37. package/node_modules/projectstore/commands/story.md +113 -0
  38. package/node_modules/projectstore/docs/extending.md +172 -0
  39. package/node_modules/projectstore/docs/getting-started.md +133 -0
  40. package/node_modules/projectstore/docs/harnesses.md +176 -0
  41. package/node_modules/projectstore/docs/how-it-works.md +263 -0
  42. package/node_modules/projectstore/docs/images/loop-light.svg +94 -0
  43. package/node_modules/projectstore/docs/images/loop.svg +93 -0
  44. package/node_modules/projectstore/docs/images/statusline-hud.png +0 -0
  45. package/node_modules/projectstore/docs/images/team-light.svg +79 -0
  46. package/node_modules/projectstore/docs/images/team.svg +79 -0
  47. package/node_modules/projectstore/harnesses/claude-code.json +483 -0
  48. package/node_modules/projectstore/harnesses/codex.json +332 -0
  49. package/node_modules/projectstore/hooks/hooks.json +59 -0
  50. package/node_modules/projectstore/hooks/pre-compact.mjs +121 -0
  51. package/node_modules/projectstore/hooks/session-rules.mjs +63 -0
  52. package/node_modules/projectstore/hooks/session-start.mjs +301 -0
  53. package/node_modules/projectstore/hooks/session-stop.mjs +84 -0
  54. package/node_modules/projectstore/package.json +70 -0
  55. package/node_modules/projectstore/scaffold/checklists.json +88 -0
  56. package/node_modules/projectstore/scaffold/headings.json +171 -0
  57. package/node_modules/projectstore/scaffold/layouts/engineering.json +85 -0
  58. package/node_modules/projectstore/scripts/binding.mjs +165 -0
  59. package/node_modules/projectstore/scripts/build-adapters.mjs +264 -0
  60. package/node_modules/projectstore/scripts/cli.mjs +595 -0
  61. package/node_modules/projectstore/scripts/codemap.mjs +99 -0
  62. package/node_modules/projectstore/scripts/diff-refs.mjs +127 -0
  63. package/node_modules/projectstore/scripts/doctor.mjs +2127 -0
  64. package/node_modules/projectstore/scripts/draft.mjs +261 -0
  65. package/node_modules/projectstore/scripts/graph.mjs +219 -0
  66. package/node_modules/projectstore/scripts/harness.mjs +608 -0
  67. package/node_modules/projectstore/scripts/install-harness.mjs +1387 -0
  68. package/node_modules/projectstore/scripts/kanban.mjs +174 -0
  69. package/node_modules/projectstore/scripts/lib.mjs +3085 -0
  70. package/node_modules/projectstore/scripts/mcp.mjs +391 -0
  71. package/node_modules/projectstore/scripts/portable-registration.mjs +198 -0
  72. package/node_modules/projectstore/scripts/provenance.mjs +375 -0
  73. package/node_modules/projectstore/scripts/query.mjs +490 -0
  74. package/node_modules/projectstore/scripts/reconcile.mjs +422 -0
  75. package/node_modules/projectstore/scripts/statusline-launcher.mjs +141 -0
  76. package/node_modules/projectstore/scripts/statusline.mjs +253 -0
  77. package/node_modules/projectstore/scripts/story-section.mjs +209 -0
  78. package/node_modules/projectstore/scripts/surfaces.mjs +421 -0
  79. package/node_modules/projectstore/scripts/tokens.mjs +449 -0
  80. package/node_modules/projectstore/scripts/touch-session.mjs +336 -0
  81. package/node_modules/projectstore/scripts/version-guard.mjs +261 -0
  82. package/node_modules/projectstore/scripts/worktree.mjs +109 -0
  83. package/node_modules/projectstore/skills/projectstore-decision-detector/SKILL.md +40 -0
  84. package/node_modules/projectstore/skills/projectstore-peer-reviewer/SKILL.md +38 -0
  85. package/node_modules/projectstore/skills/projectstore-story-completion/SKILL.md +50 -0
  86. package/node_modules/projectstore/skills/projectstore-vault-communication/SKILL.md +96 -0
  87. package/node_modules/projectstore/templates/claude-md-block.md.tmpl +26 -0
  88. package/node_modules/projectstore/templates/de/adr.md.tmpl +67 -0
  89. package/node_modules/projectstore/templates/de/concept.md.tmpl +43 -0
  90. package/node_modules/projectstore/templates/de/epic.md.tmpl +59 -0
  91. package/node_modules/projectstore/templates/de/folder-readme.md.tmpl +14 -0
  92. package/node_modules/projectstore/templates/de/kanban.md.tmpl +36 -0
  93. package/node_modules/projectstore/templates/de/meeting.md.tmpl +38 -0
  94. package/node_modules/projectstore/templates/de/research.md.tmpl +47 -0
  95. package/node_modules/projectstore/templates/de/runbook.md.tmpl +53 -0
  96. package/node_modules/projectstore/templates/de/spec.md.tmpl +64 -0
  97. package/node_modules/projectstore/templates/de/story.md.tmpl +76 -0
  98. package/node_modules/projectstore/templates/de/strings.json +6 -0
  99. package/node_modules/projectstore/templates/en/adr.md.tmpl +67 -0
  100. package/node_modules/projectstore/templates/en/concept.md.tmpl +43 -0
  101. package/node_modules/projectstore/templates/en/epic.md.tmpl +59 -0
  102. package/node_modules/projectstore/templates/en/folder-readme.md.tmpl +14 -0
  103. package/node_modules/projectstore/templates/en/kanban.md.tmpl +36 -0
  104. package/node_modules/projectstore/templates/en/meeting.md.tmpl +38 -0
  105. package/node_modules/projectstore/templates/en/research.md.tmpl +47 -0
  106. package/node_modules/projectstore/templates/en/runbook.md.tmpl +53 -0
  107. package/node_modules/projectstore/templates/en/spec.md.tmpl +64 -0
  108. package/node_modules/projectstore/templates/en/story.md.tmpl +76 -0
  109. package/node_modules/projectstore/templates/en/strings.json +6 -0
  110. package/node_modules/projectstore/templates/es/adr.md.tmpl +67 -0
  111. package/node_modules/projectstore/templates/es/concept.md.tmpl +43 -0
  112. package/node_modules/projectstore/templates/es/epic.md.tmpl +59 -0
  113. package/node_modules/projectstore/templates/es/folder-readme.md.tmpl +14 -0
  114. package/node_modules/projectstore/templates/es/kanban.md.tmpl +36 -0
  115. package/node_modules/projectstore/templates/es/meeting.md.tmpl +38 -0
  116. package/node_modules/projectstore/templates/es/research.md.tmpl +47 -0
  117. package/node_modules/projectstore/templates/es/runbook.md.tmpl +53 -0
  118. package/node_modules/projectstore/templates/es/spec.md.tmpl +64 -0
  119. package/node_modules/projectstore/templates/es/story.md.tmpl +76 -0
  120. package/node_modules/projectstore/templates/es/strings.json +6 -0
  121. package/node_modules/projectstore/templates/fr/adr.md.tmpl +67 -0
  122. package/node_modules/projectstore/templates/fr/concept.md.tmpl +43 -0
  123. package/node_modules/projectstore/templates/fr/epic.md.tmpl +59 -0
  124. package/node_modules/projectstore/templates/fr/folder-readme.md.tmpl +14 -0
  125. package/node_modules/projectstore/templates/fr/kanban.md.tmpl +36 -0
  126. package/node_modules/projectstore/templates/fr/meeting.md.tmpl +38 -0
  127. package/node_modules/projectstore/templates/fr/research.md.tmpl +47 -0
  128. package/node_modules/projectstore/templates/fr/runbook.md.tmpl +53 -0
  129. package/node_modules/projectstore/templates/fr/spec.md.tmpl +64 -0
  130. package/node_modules/projectstore/templates/fr/story.md.tmpl +76 -0
  131. package/node_modules/projectstore/templates/fr/strings.json +6 -0
  132. package/node_modules/projectstore/templates/ru/adr.md.tmpl +67 -0
  133. package/node_modules/projectstore/templates/ru/concept.md.tmpl +43 -0
  134. package/node_modules/projectstore/templates/ru/epic.md.tmpl +59 -0
  135. package/node_modules/projectstore/templates/ru/folder-readme.md.tmpl +14 -0
  136. package/node_modules/projectstore/templates/ru/kanban.md.tmpl +36 -0
  137. package/node_modules/projectstore/templates/ru/meeting.md.tmpl +38 -0
  138. package/node_modules/projectstore/templates/ru/research.md.tmpl +47 -0
  139. package/node_modules/projectstore/templates/ru/runbook.md.tmpl +53 -0
  140. package/node_modules/projectstore/templates/ru/spec.md.tmpl +64 -0
  141. package/node_modules/projectstore/templates/ru/story.md.tmpl +76 -0
  142. package/node_modules/projectstore/templates/ru/strings.json +6 -0
  143. package/node_modules/projectstore/templates/zh/adr.md.tmpl +67 -0
  144. package/node_modules/projectstore/templates/zh/concept.md.tmpl +43 -0
  145. package/node_modules/projectstore/templates/zh/epic.md.tmpl +59 -0
  146. package/node_modules/projectstore/templates/zh/folder-readme.md.tmpl +14 -0
  147. package/node_modules/projectstore/templates/zh/kanban.md.tmpl +36 -0
  148. package/node_modules/projectstore/templates/zh/meeting.md.tmpl +38 -0
  149. package/node_modules/projectstore/templates/zh/research.md.tmpl +47 -0
  150. package/node_modules/projectstore/templates/zh/runbook.md.tmpl +53 -0
  151. package/node_modules/projectstore/templates/zh/spec.md.tmpl +64 -0
  152. package/node_modules/projectstore/templates/zh/story.md.tmpl +76 -0
  153. package/node_modules/projectstore/templates/zh/strings.json +6 -0
  154. package/package.json +35 -14
  155. package/skills/projectstore-adr/SKILL.md +76 -0
  156. package/skills/projectstore-agents/SKILL.md +50 -0
  157. package/skills/projectstore-archaeologist/SKILL.md +109 -0
  158. package/skills/projectstore-bind/SKILL.md +44 -0
  159. package/skills/projectstore-clerk/SKILL.md +126 -0
  160. package/skills/projectstore-codemap/SKILL.md +69 -0
  161. package/skills/projectstore-concept/SKILL.md +36 -0
  162. package/skills/projectstore-critic/SKILL.md +127 -0
  163. package/skills/projectstore-decision-detector/SKILL.md +59 -0
  164. package/skills/projectstore-doctor/SKILL.md +33 -0
  165. package/skills/projectstore-epic/SKILL.md +59 -0
  166. package/skills/projectstore-graph/SKILL.md +75 -0
  167. package/skills/projectstore-kanban/SKILL.md +60 -0
  168. package/skills/projectstore-librarian/SKILL.md +114 -0
  169. package/skills/projectstore-meeting/SKILL.md +36 -0
  170. package/skills/projectstore-peer-reviewer/SKILL.md +57 -0
  171. package/skills/projectstore-planner/SKILL.md +113 -0
  172. package/skills/projectstore-reconcile/SKILL.md +92 -0
  173. package/skills/projectstore-research/SKILL.md +36 -0
  174. package/skills/projectstore-review/SKILL.md +108 -0
  175. package/skills/projectstore-reviewer/SKILL.md +131 -0
  176. package/skills/projectstore-runbook/SKILL.md +36 -0
  177. package/skills/projectstore-scaffold/SKILL.md +42 -0
  178. package/skills/projectstore-search/SKILL.md +41 -0
  179. package/skills/projectstore-spec/SKILL.md +110 -0
  180. package/skills/projectstore-status/SKILL.md +47 -0
  181. package/skills/projectstore-statusline/SKILL.md +29 -0
  182. package/skills/projectstore-story/SKILL.md +132 -0
  183. package/skills/projectstore-story-completion/SKILL.md +69 -0
  184. package/skills/projectstore-vault-communication/SKILL.md +115 -0
@@ -0,0 +1,263 @@
1
+ # How projectstore works under the hood
2
+
3
+ The README gives the short version; this is the deep dive for the curious. Nothing
4
+ here is required reading to use the plugin.
5
+
6
+ ## The write path: draft → approval → file
7
+
8
+ Every create command follows the same shape:
9
+
10
+ 1. `scripts/draft.mjs` renders the artifact **in memory** (it never touches the
11
+ disk — no writes, no mkdir; declining leaves the vault byte-for-byte unchanged).
12
+ 2. The command shows you the target path + full content via `AskUserQuestion`.
13
+ 3. Only on **Yes** does the file land — and that same Yes covers the folder's
14
+ index. The index row is not appended: the command regenerates the folder's
15
+ managed table through the core (`reconcile --write --only indexes=<folder>`),
16
+ so the row arrives in canonical order, the file is replaced atomically, and
17
+ your own prose around the table is preserved. One consequence worth knowing:
18
+ the regeneration rewrites the whole table, so creating one artifact can also
19
+ repair a stale row for another.
20
+
21
+ **Who holds the pen.** Content and the gate never delegate: the session's main
22
+ agent composes the artifact (it holds the conversation the artifact captures)
23
+ and answers to you at the approval gate. What *can* delegate is everything after
24
+ "yes" — for the long ceremonies (a story close, a multi-target reconcile) the
25
+ command hands execution to the `clerk` agent, which copies the approved content
26
+ from a scratch file (never re-emitting it through a model — fidelity mechanical,
27
+ proven per write by a byte-level diff), runs reconcile and doctor, and returns a one-line report instead
28
+ of pages of tool output. The clerk composes nothing, decides nothing, and asks
29
+ nothing; the advisors (critic, planner, reviewer, librarian, archaeologist)
30
+ stay read-only.
31
+
32
+ Frontmatter scalars are rendered through a JSON-escaping form (`{{title_json}}`),
33
+ so titles containing `"` or `:` cannot corrupt the YAML. Slugs transliterate
34
+ Cyrillic (`Кэширование запросов` → `keshirovanie-zaprosov`) and never come out
35
+ empty.
36
+
37
+ ## Sessions: how parallel Claude instances coexist
38
+
39
+ Each Claude Code session registers itself in the vault under its own
40
+ `session_id`:
41
+
42
+ ```bash
43
+ $ ls <vault>/.projectstore/sessions/
44
+ d9149e0d-9169-43ef-b2c6-3e005a00e133.json # session A — plugin dev
45
+ f05e61c5-f809-46e1-aa3e-b7c3366bc723.json # session B — feature work
46
+
47
+ $ cat <vault>/.projectstore/sessions/f05e61c5….json
48
+ {
49
+ "id": "f05e61c5-f809-46e1-aa3e-b7c3366bc723",
50
+ "project_root": "/Users/me/projects/myapp",
51
+ "started_at": "2026-05-19T13:35:36Z",
52
+ "recent_activity": [
53
+ { "path": "epics/WEB-101/epic.md", "tool": "Edit", "at": "…" },
54
+ { "path": "epics/WEB-101/stories/001-oidc-flow.md", "tool": "Write", "at": "…" }
55
+ ]
56
+ }
57
+ ```
58
+
59
+ When another session starts, SessionStart sees fresh sibling files (mtime < 30
60
+ min) and warns the new agent:
61
+
62
+ > ⚠️ Multi-session warning — 1 other projectstore session active on this vault.
63
+ > Run `/projectstore:search <topic>` before creating new artifacts.
64
+
65
+ Create commands additionally re-check file existence right before writing, so two
66
+ sessions can't silently overwrite each other's new artifact.
67
+
68
+ **The status line is stricter still**: it renders from a *per-session* pointer
69
+ (`<project>/.projectstore/state/sessions/<session_id>.json`, written by the
70
+ PreToolUse hook with denormalized titles) and performs **zero cross-session and
71
+ zero vault reads**. 2–6 parallel sessions each see exactly their own epic/story;
72
+ a fresh session gets a localized "no epic or story in this session yet" line; a
73
+ corrupt pointer shows an error marker, never a false "no work". The renderer also
74
+ drops a `.last-render.json` breadcrumb so `doctor` can detect session-id
75
+ divergence between the status line process and the hooks.
76
+
77
+ ## Surviving `/compact`
78
+
79
+ Continuity across a compaction is delivered by **SessionStart**, not by
80
+ PreCompact — and that is a correction, not a design flourish. PreCompact has no
81
+ model-facing channel: it accepts a top-level `systemMessage` and nothing else,
82
+ so for its whole life the hook assembled a rich packet, printed a red *"Hook
83
+ JSON output validation failed"*, and reached nobody. Worse, the invalid field
84
+ sank the whole object, so the one line PreCompact *does* support never landed
85
+ either.
86
+
87
+ So the two hooks now split the work by what each can actually do.
88
+
89
+ PreCompact prints one line, for you:
90
+
91
+ ```
92
+ PreCompact [...pre-compact.mjs] completed successfully: {
93
+ "systemMessage": "projectstore: compacting — vault myapp-vault,
94
+ in flight: `epics/WEB-101/epic.md`,
95
+ orientation and this session's recent files return
96
+ at session start"
97
+ }
98
+ ```
99
+
100
+ That last clause is conditional on purpose. It appears only when the compaction
101
+ is **manual** and `auto_inject` is on — the two things the hook can check at the
102
+ moment it speaks. On an automatic compaction the plugin makes no claim that
103
+ SessionStart fires at all, and under `auto_inject: false` the claim would be
104
+ flatly false, so the line offers `/projectstore:status` instead. The
105
+ recent-files half needs one more condition — a nonempty activity log — because
106
+ an empty one renders no continuity section at all.
107
+
108
+ SessionStart then fires with `source: "compact"` and renders a **Where this
109
+ session left off** block above the derived-views section: up to five vault files
110
+ the previous conversation touched, newest first, plus the newest structured
111
+ write as the in-flight artifact. Both hooks resolve that artifact through one
112
+ shared function, so the compaction line and the section that follows cannot name
113
+ different files seconds apart.
114
+
115
+ (Fine-grained "where exactly was my cursor" resume is the job of your task list
116
+ and `git status`; the vault answers "where were we going".)
117
+
118
+ ## The status line composition trick
119
+
120
+ `statusLine` in Claude Code is a single slot, not plugin-declarable, and read
121
+ once when the session starts. So `/projectstore:statusline on` runs the
122
+ install verb, which points the project's `.claude/settings.local.json` at a
123
+ launcher it writes (provenance-stamped) at `.projectstore/state/claude-code/statusline.mjs`;
124
+ the SessionStart hook keeps an existing entry current across updates and
125
+ never creates one. Pointing
126
+ it straight at the plugin would pin a versioned cache path
127
+ (`…/projectstore/<version>/scripts/statusline.mjs`) and render the version
128
+ installed at the *previous* session start — every update arrived a restart late.
129
+ The launcher's path never changes, and it resolves the installed plugin from
130
+ Claude Code's own registry on each render. The script then **composes** instead of
131
+ clobbering: it re-runs whatever base statusLine command you already had (e.g.
132
+ oh-my-claudecode's HUD, taken from the project's `.claude/settings.json` if it
133
+ has one, else `~/.claude/settings.json`), prints its output
134
+ verbatim, and adds one `[PS#version] 📚 …` line above it (`statusline.position`:
135
+ `above`/`below`). With no base command it renders a standalone line.
136
+
137
+ ## Doctor and reconcile: the trust layer
138
+
139
+ The vault's guarantees used to depend on everyone using the gated commands —
140
+ but agents (and humans) hand-edit markdown, because it's the path of least
141
+ resistance. v0.13 makes that safe instead of forbidden:
142
+
143
+ - **`doctor`** is a deterministic, no-LLM check engine (`scripts/doctor.mjs`).
144
+ `--install` verifies the wiring (config, vault path, hooks firing, statusline,
145
+ agents block, leftover agent copies, gitignore, marketplace auto-update);
146
+ `--vault` verifies content consistency (status ↔ board ↔ indexes, acceptance
147
+ boxes, dead and ambiguous wiki/relative links — resolved by the same shared
148
+ resolver the link graph uses, so the two can never disagree about a body
149
+ link — `code_refs`,
150
+ code-map and graph staleness). A cheap
151
+ subset runs at session start and prints one line only when something is wrong.
152
+ - **`reconcile`** rebuilds every generated view (kanban, folder indexes,
153
+ code-map, the link graph) from the vault — the repair partner. Idempotent: a clean vault
154
+ yields zero changes. Since v0.19 reconcile applies the write itself
155
+ (`--write`): each approved target is recomputed from the vault state at
156
+ write time and replaced atomically, so a stale preview is never what lands;
157
+ `--write` is also a sanctioned headless entry point (cron/CI repair job).
158
+ - A throttled PreToolUse **nudge** fires when vault files are edited directly:
159
+ "run reconcile afterwards so the derived views stay in sync" (disable with
160
+ `"guard": "off"` in `projectstore.json`).
161
+ - A once-per-session **entry reminder** fires on PostToolUse when a session has
162
+ written to three or more distinct *source* files and no story is `in-progress`:
163
+ "open it in the vault before going further". PostToolUse, not PreToolUse, so
164
+ declined edits are not counted as work that happened; `Stop` is the fallback
165
+ carrier for a session that delegates all its writing to subagents and then
166
+ answers without a tool call of its own. Subagent writes count toward the score
167
+ but never receive the reminder — a subagent cannot open a story. A session
168
+ launched with `--agent` carries an agent identity on every call and is
169
+ therefore never reminded either; for those sessions `doctor`'s
170
+ `work-without-story` is the only coverage. The same
171
+ `"guard": "off"` silences it, and silences both nudges together. Firings are
172
+ appended to `.projectstore/state/entry-log.jsonl` (machine-local, since
173
+ `.claude/` is gitignored) so `doctor` can report whether the prompt was ever
174
+ delivered — a mechanism that cannot say whether it worked is indistinguishable
175
+ from one that does not.
176
+ - A **session-name offer** fires on PreToolUse once a session's vault work has
177
+ settled on an anchor — see below. Same `"guard": "off"` switch, same log, and
178
+ its records carry `kind: "name-offer"` so `doctor`'s reminder count does not
179
+ quietly absorb them.
180
+ - `doctor` carries the after-the-fact half: **work-without-story** warns when the
181
+ project tree is dirty and no story is in progress. It is the only seam that
182
+ sees Bash-mediated writes, which bypass hook path extraction entirely.
183
+
184
+ The division of labor: deterministic checks catch the mechanical 90% for free;
185
+ the LLM critic (`/projectstore:review`) is saved for the judgment calls.
186
+
187
+ ## Naming a session after the work it settled on
188
+
189
+ A Claude Code session has a name, and it is an **address**: live peers route to
190
+ each other by it, which is why names are arbitrated for uniqueness across a
191
+ machine. So a session named after its work is addressable by unit of work.
192
+
193
+ No plugin can set that name. The authoritative value lives in the session
194
+ process's memory behind no reachable API — writing the registry file or the
195
+ transcript record reaches neither the tab nor the arbitration, and only
196
+ desynchronises the three surfaces from each other. What a plugin can do is
197
+ compose the right name and **offer** it:
198
+
199
+ ```
200
+ projectstore: this session looks like "ps-agents-statusline-v2" — /rename ps-agents-statusline-v2
201
+ ```
202
+
203
+ The whole design question is **when to speak**, because an offer is an
204
+ interruption. Replaying nine recorded sessions, the obvious rule — offer on every
205
+ epic/story change — fires **137 times, 37 in the worst session**. Work moves
206
+ between a story, its epic and a sibling many times an hour; that motion is not a
207
+ change of subject.
208
+
209
+ What is stable is the **anchor**: the cluster a session's writes settle on. Seven
210
+ of nine sessions had a dominant epic holding 62–100% of their epic writes. So:
211
+
212
+ - every vault write maps to one anchor key — `epic:<ID>` for anything under an
213
+ epic, or the document itself for an ADR, spec or research note (two of the nine
214
+ sessions touched no epic at all, and an epic-only rule leaves those permanently
215
+ nameless);
216
+ - an anchor **settles** at five writes and is offered;
217
+ - a challenger takes over — and is offered — only at a **ten-write lead**;
218
+ - the name is `<epic-id>-<its most-written story>`, or the document's slug;
219
+ kebab-case, 48 characters, cut on a word boundary.
220
+
221
+ That is **12 offers across nine sessions, at most two in any one**, and six
222
+ sessions naming themselves exactly once. Re-offering is deliberate: in three
223
+ sessions the anchor moved, and the shape is a session that decides in a document
224
+ and then builds in an epic — a name that does not follow that is a name for the
225
+ first hour only.
226
+
227
+ Only **write-family calls by the main agent** count. A review session that greps
228
+ thirty files has not done work worth naming, and a subagent shares the session id
229
+ but cannot accept a name. Neither gate is visible in the fixtures — every
230
+ recorded session is an authoring session — so both are pinned by their own drives
231
+ instead.
232
+
233
+ If a live peer already holds the name it is offered as `-2`, then `-3`; a third
234
+ collision means the name is not discriminating and nothing is said. And a name
235
+ you chose yourself is never overwritten — but note that a session usually
236
+ *arrives* with a harness-assigned name, so "not the name we offered" is not
237
+ enough to infer choice: the plugin treats a name as yours only after it has made
238
+ at least one offer of its own.
239
+
240
+ **The known blind spot**: vault writes made through Bash never reach this seam,
241
+ because the input is tool-call path extraction. The consequence that matters is
242
+ not a missing name but a **wrong** one — if the bulk of a session's work went
243
+ through scripts, the anchor is computed from the visible minority. And the gap is
244
+ correlated with plugin-idiomatic behaviour: the more work goes through
245
+ `/projectstore:*` and `reconcile`, the less nameable the session becomes.
246
+
247
+ The rule is a pure fold in `lib.mjs` with no clock and no disk, so the fixture
248
+ replay and the live hook run one implementation. Its tallies are files that grow
249
+ by one byte — the count is the file size — because the shared session-state file
250
+ is an unguarded read-modify-write, and a tally incremented on every vault write
251
+ would eventually truncate the ADR-006 statusline pointer out from under an
252
+ unrelated feature.
253
+
254
+ ## The epic ↔ code mapping
255
+
256
+ Epics and stories carry a `code_refs` frontmatter list — the paths that feature
257
+ actually owns in the codebase (`["src/auth/", "adapters/csv/"]`). The
258
+ `/projectstore:codemap` command is the only writer (`codemap set`), the
259
+ `planner` agent reads them to keep new epics consistent with how old ones
260
+ landed, the `reviewer` proposes updates when a story completes, and
261
+ `code-map.md` is the generated overview. Doctor checks refs status-aware:
262
+ `planned` work may point at code that doesn't exist yet; `in-progress`/`done`
263
+ refs must resolve.
@@ -0,0 +1,94 @@
1
+ <svg width="960" height="470" viewBox="0 0 960 470" fill="none" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif">
2
+ <defs>
3
+ <marker id="lab" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
4
+ <path d="M0 0 L10 5 L0 10 z" fill="#6B84AD"/>
5
+ </marker>
6
+ <marker id="lar" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
7
+ <path d="M0 0 L10 5 L0 10 z" fill="#E11D48"/>
8
+ </marker>
9
+ <marker id="lag" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
10
+ <path d="M0 0 L10 5 L0 10 z" fill="#059669"/>
11
+ </marker>
12
+ </defs>
13
+
14
+ <rect width="960" height="470" rx="18" fill="#FFFFFF"/>
15
+ <rect x="1" y="1" width="958" height="468" rx="17" stroke="#E2E8F0"/>
16
+
17
+ <text x="44" y="48" fill="#94A3B8" font-size="11" letter-spacing="3">DECIDE</text>
18
+ <text x="104" y="218" fill="#94A3B8" font-size="11" letter-spacing="3">BUILD</text>
19
+
20
+ <!-- ── lane 1: decide ─────────────────────────────────────────── -->
21
+ <rect x="44" y="70" width="104" height="64" rx="11" fill="#F1F5F9" stroke="#CBD5E1"/>
22
+ <text x="96" y="97" fill="#0F172A" font-size="15" font-weight="600" text-anchor="middle">Task</text>
23
+ <text x="96" y="117" fill="#64748B" font-size="11" text-anchor="middle">from you</text>
24
+
25
+ <path d="M152 102 L188 102" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#lab)"/>
26
+
27
+ <rect x="192" y="70" width="232" height="64" rx="11" fill="#F1F5F9" stroke="#CBD5E1"/>
28
+ <text x="308" y="97" fill="#0F172A" font-size="15" font-weight="600" text-anchor="middle">ADR · Spec · Epic · Story</text>
29
+ <text x="308" y="117" fill="#64748B" font-size="11" text-anchor="middle">the why, on disk</text>
30
+
31
+ <path d="M428 102 L462 102" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#lab)"/>
32
+
33
+ <rect x="492" y="44" width="64" height="18" rx="9" fill="#E11D48" fill-opacity="0.10"/>
34
+ <text x="524" y="57" fill="#E11D48" font-size="10" letter-spacing="1.5" text-anchor="middle">VERIFY</text>
35
+ <rect x="466" y="70" width="152" height="64" rx="11" fill="#FFF1F2" stroke="#E11D48"/>
36
+ <text x="542" y="97" fill="#BE123C" font-size="15" font-weight="600" text-anchor="middle">critic</text>
37
+ <text x="542" y="117" fill="#B25B70" font-size="11" text-anchor="middle">fresh context, adversarial</text>
38
+
39
+ <path d="M486 68 C 462 40, 442 40, 416 66" stroke="#E11D48" stroke-width="1.4" stroke-dasharray="4 4" marker-end="url(#lar)"/>
40
+ <text x="446" y="34" fill="#E11D48" font-size="11" text-anchor="middle">revise</text>
41
+
42
+ <path d="M622 102 L656 102" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#lab)"/>
43
+
44
+ <rect x="660" y="70" width="150" height="64" rx="11" fill="#F1F5F9" stroke="#CBD5E1"/>
45
+ <text x="735" y="97" fill="#0F172A" font-size="15" font-weight="600" text-anchor="middle">Backlog</text>
46
+ <text x="735" y="117" fill="#64748B" font-size="11" text-anchor="middle">kanban.md — ready</text>
47
+
48
+ <!-- hand-off to the build lane (lane 2 sits 60px right: a gantt cascade,
49
+ and the return arrow below gets a clear left margin) -->
50
+ <path d="M735 138 C 735 192, 210 168, 164 234" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#lab)"/>
51
+ <text x="470" y="163" fill="#64748B" font-size="12" text-anchor="middle">an agent picks it up</text>
52
+
53
+ <!-- ── lane 2: build ──────────────────────────────────────────── -->
54
+ <rect x="104" y="240" width="116" height="64" rx="11" fill="#F1F5F9" stroke="#CBD5E1"/>
55
+ <text x="162" y="267" fill="#0F172A" font-size="15" font-weight="600" text-anchor="middle">Story</text>
56
+ <text x="162" y="287" fill="#64748B" font-size="11" text-anchor="middle">in-progress</text>
57
+
58
+ <path d="M224 272 L258 272" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#lab)"/>
59
+
60
+ <rect x="262" y="240" width="136" height="64" rx="11" fill="#F5F3FF" stroke="#7C3AED"/>
61
+ <text x="330" y="267" fill="#6D28D9" font-size="15" font-weight="600" text-anchor="middle">planner</text>
62
+ <text x="330" y="287" fill="#8B7BB5" font-size="11" text-anchor="middle">where in the code</text>
63
+
64
+ <path d="M402 272 L436 272" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#lab)"/>
65
+
66
+ <rect x="440" y="240" width="140" height="64" rx="11" fill="#F1F5F9" stroke="#CBD5E1"/>
67
+ <text x="510" y="267" fill="#0F172A" font-size="15" font-weight="600" text-anchor="middle">Implement</text>
68
+ <text x="510" y="287" fill="#64748B" font-size="11" text-anchor="middle">code + tests</text>
69
+
70
+ <path d="M584 272 L618 272" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#lab)"/>
71
+
72
+ <rect x="668" y="214" width="64" height="18" rx="9" fill="#E11D48" fill-opacity="0.10"/>
73
+ <text x="700" y="227" fill="#E11D48" font-size="10" letter-spacing="1.5" text-anchor="middle">VERIFY</text>
74
+ <rect x="622" y="240" width="156" height="64" rx="11" fill="#FFF1F2" stroke="#E11D48"/>
75
+ <text x="700" y="267" fill="#BE123C" font-size="15" font-weight="600" text-anchor="middle">reviewer</text>
76
+ <text x="700" y="287" fill="#B25B70" font-size="11" text-anchor="middle">evidence per criterion</text>
77
+
78
+ <path d="M638 238 C 616 212, 600 212, 576 236" stroke="#E11D48" stroke-width="1.4" stroke-dasharray="4 4" marker-end="url(#lar)"/>
79
+ <text x="606" y="206" fill="#E11D48" font-size="11" text-anchor="middle">fix</text>
80
+
81
+ <path d="M782 272 L816 272" stroke="#059669" stroke-width="1.6" marker-end="url(#lag)"/>
82
+
83
+ <rect x="820" y="240" width="126" height="64" rx="11" fill="#ECFDF5" stroke="#059669"/>
84
+ <text x="883" y="267" fill="#047857" font-size="15" font-weight="600" text-anchor="middle">Done ✓</text>
85
+ <text x="883" y="287" fill="#5F9E86" font-size="11" text-anchor="middle">criteria met</text>
86
+
87
+ <!-- ── the feedback that closes the loop ──────────────────────── -->
88
+ <path d="M883 308 C 883 344, 856 364, 776 370" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#lab)"/>
89
+ <rect x="250" y="352" width="520" height="36" rx="18" fill="#F1F5F9" stroke="#E2E8F0"/>
90
+ <text x="510" y="375" fill="#475569" font-size="12.5" text-anchor="middle">kanban · graph · code-map regenerate — the next session starts oriented</text>
91
+ <path d="M246 370 C 90 370, 48 290, 76 142" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#lab)"/>
92
+
93
+ <text x="480" y="444" fill="#94A3B8" font-size="12.5" text-anchor="middle">gather context → act → verify — the agentic loop, run over the project itself, not just the code</text>
94
+ </svg>
@@ -0,0 +1,93 @@
1
+ <svg width="960" height="470" viewBox="0 0 960 470" fill="none" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif">
2
+ <defs>
3
+ <marker id="ab" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
4
+ <path d="M0 0 L10 5 L0 10 z" fill="#5B7BB4"/>
5
+ </marker>
6
+ <marker id="ar" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
7
+ <path d="M0 0 L10 5 L0 10 z" fill="#F43F5E"/>
8
+ </marker>
9
+ <marker id="ag" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
10
+ <path d="M0 0 L10 5 L0 10 z" fill="#10B981"/>
11
+ </marker>
12
+ </defs>
13
+
14
+ <rect width="960" height="470" rx="18" fill="#0C1322"/>
15
+ <rect x="1" y="1" width="958" height="468" rx="17" stroke="#1E2A44"/>
16
+
17
+ <text x="44" y="48" fill="#64748B" font-size="11" letter-spacing="3">DECIDE</text>
18
+ <text x="104" y="218" fill="#64748B" font-size="11" letter-spacing="3">BUILD</text>
19
+
20
+ <!-- ── lane 1: decide ─────────────────────────────────────────── -->
21
+ <rect x="44" y="70" width="104" height="64" rx="11" fill="#131F36" stroke="#2C3E5F"/>
22
+ <text x="96" y="97" fill="#E2E8F0" font-size="15" font-weight="600" text-anchor="middle">Task</text>
23
+ <text x="96" y="117" fill="#7C8DB0" font-size="11" text-anchor="middle">from you</text>
24
+
25
+ <path d="M152 102 L188 102" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#ab)"/>
26
+
27
+ <rect x="192" y="70" width="232" height="64" rx="11" fill="#131F36" stroke="#2C3E5F"/>
28
+ <text x="308" y="97" fill="#E2E8F0" font-size="15" font-weight="600" text-anchor="middle">ADR · Spec · Epic · Story</text>
29
+ <text x="308" y="117" fill="#7C8DB0" font-size="11" text-anchor="middle">the why, on disk</text>
30
+
31
+ <path d="M428 102 L462 102" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#ab)"/>
32
+
33
+ <rect x="492" y="44" width="64" height="18" rx="9" fill="#F43F5E" fill-opacity="0.14"/>
34
+ <text x="524" y="57" fill="#FB7185" font-size="10" letter-spacing="1.5" text-anchor="middle">VERIFY</text>
35
+ <rect x="466" y="70" width="152" height="64" rx="11" fill="#2A1220" stroke="#F43F5E"/>
36
+ <text x="542" y="97" fill="#FDA4AF" font-size="15" font-weight="600" text-anchor="middle">critic</text>
37
+ <text x="542" y="117" fill="#B07A8C" font-size="11" text-anchor="middle">fresh context, adversarial</text>
38
+
39
+ <path d="M486 68 C 462 40, 442 40, 416 66" stroke="#F43F5E" stroke-width="1.4" stroke-dasharray="4 4" marker-end="url(#ar)"/>
40
+ <text x="446" y="34" fill="#FB7185" font-size="11" text-anchor="middle">revise</text>
41
+
42
+ <path d="M622 102 L656 102" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#ab)"/>
43
+
44
+ <rect x="660" y="70" width="150" height="64" rx="11" fill="#131F36" stroke="#2C3E5F"/>
45
+ <text x="735" y="97" fill="#E2E8F0" font-size="15" font-weight="600" text-anchor="middle">Backlog</text>
46
+ <text x="735" y="117" fill="#7C8DB0" font-size="11" text-anchor="middle">kanban.md — ready</text>
47
+
48
+ <!-- hand-off to the build lane -->
49
+ <path d="M735 138 C 735 192, 210 168, 164 234" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#ab)"/>
50
+ <text x="470" y="163" fill="#7C8DB0" font-size="12" text-anchor="middle">an agent picks it up</text>
51
+
52
+ <!-- ── lane 2: build ──────────────────────────────────────────── -->
53
+ <rect x="104" y="240" width="116" height="64" rx="11" fill="#131F36" stroke="#2C3E5F"/>
54
+ <text x="162" y="267" fill="#E2E8F0" font-size="15" font-weight="600" text-anchor="middle">Story</text>
55
+ <text x="162" y="287" fill="#7C8DB0" font-size="11" text-anchor="middle">in-progress</text>
56
+
57
+ <path d="M224 272 L258 272" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#ab)"/>
58
+
59
+ <rect x="262" y="240" width="136" height="64" rx="11" fill="#1D1733" stroke="#8B5CF6"/>
60
+ <text x="330" y="267" fill="#C4B5FD" font-size="15" font-weight="600" text-anchor="middle">planner</text>
61
+ <text x="330" y="287" fill="#9D8FC7" font-size="11" text-anchor="middle">where in the code</text>
62
+
63
+ <path d="M402 272 L436 272" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#ab)"/>
64
+
65
+ <rect x="440" y="240" width="140" height="64" rx="11" fill="#131F36" stroke="#2C3E5F"/>
66
+ <text x="510" y="267" fill="#E2E8F0" font-size="15" font-weight="600" text-anchor="middle">Implement</text>
67
+ <text x="510" y="287" fill="#7C8DB0" font-size="11" text-anchor="middle">code + tests</text>
68
+
69
+ <path d="M584 272 L618 272" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#ab)"/>
70
+
71
+ <rect x="668" y="214" width="64" height="18" rx="9" fill="#F43F5E" fill-opacity="0.14"/>
72
+ <text x="700" y="227" fill="#FB7185" font-size="10" letter-spacing="1.5" text-anchor="middle">VERIFY</text>
73
+ <rect x="622" y="240" width="156" height="64" rx="11" fill="#2A1220" stroke="#F43F5E"/>
74
+ <text x="700" y="267" fill="#FDA4AF" font-size="15" font-weight="600" text-anchor="middle">reviewer</text>
75
+ <text x="700" y="287" fill="#B07A8C" font-size="11" text-anchor="middle">evidence per criterion</text>
76
+
77
+ <path d="M638 238 C 616 212, 600 212, 576 236" stroke="#F43F5E" stroke-width="1.4" stroke-dasharray="4 4" marker-end="url(#ar)"/>
78
+ <text x="606" y="206" fill="#FB7185" font-size="11" text-anchor="middle">fix</text>
79
+
80
+ <path d="M782 272 L816 272" stroke="#10B981" stroke-width="1.6" marker-end="url(#ag)"/>
81
+
82
+ <rect x="820" y="240" width="126" height="64" rx="11" fill="#0C2A1E" stroke="#10B981"/>
83
+ <text x="883" y="267" fill="#6EE7B7" font-size="15" font-weight="600" text-anchor="middle">Done ✓</text>
84
+ <text x="883" y="287" fill="#79A893" font-size="11" text-anchor="middle">criteria met</text>
85
+
86
+ <!-- ── the feedback that closes the loop ──────────────────────── -->
87
+ <path d="M883 308 C 883 344, 856 364, 776 370" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#ab)"/>
88
+ <rect x="250" y="352" width="520" height="36" rx="18" fill="#101B31" stroke="#24344F"/>
89
+ <text x="510" y="375" fill="#9FB0CE" font-size="12.5" text-anchor="middle">kanban · graph · code-map regenerate — the next session starts oriented</text>
90
+ <path d="M246 370 C 100 370, 56 292, 84 142" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#ab)"/>
91
+
92
+ <text x="480" y="444" fill="#5F6E8C" font-size="12.5" text-anchor="middle">gather context → act → verify — the agentic loop, run over the project itself, not just the code</text>
93
+ </svg>
@@ -0,0 +1,79 @@
1
+ <svg width="960" height="430" viewBox="0 0 960 430" fill="none" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif">
2
+ <defs>
3
+ <marker id="ltb" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
4
+ <path d="M0 0 L10 5 L0 10 z" fill="#6B84AD"/>
5
+ </marker>
6
+ </defs>
7
+
8
+ <rect width="960" height="430" rx="18" fill="#FFFFFF"/>
9
+ <rect x="1" y="1" width="958" height="428" rx="17" stroke="#E2E8F0"/>
10
+
11
+ <!-- ── the people ─────────────────────────────────────────────── -->
12
+ <g>
13
+ <rect x="44" y="56" width="232" height="76" rx="12" fill="#F8FAFC" stroke="#E2E8F0"/>
14
+ <circle cx="78" cy="94" r="15" fill="#E2E8F0" stroke="#CBD5E1"/>
15
+ <circle cx="78" cy="90" r="4.5" fill="#64748B"/>
16
+ <path d="M71 103 a7 5.5 0 0 1 14 0" fill="#64748B"/>
17
+ <text x="106" y="89" fill="#0F172A" font-size="15" font-weight="600">Dev A</text>
18
+ <text x="106" y="109" fill="#64748B" font-size="11.5">Claude Code + projectstore</text>
19
+ </g>
20
+ <g>
21
+ <rect x="44" y="166" width="232" height="76" rx="12" fill="#F8FAFC" stroke="#E2E8F0"/>
22
+ <circle cx="78" cy="204" r="15" fill="#E2E8F0" stroke="#CBD5E1"/>
23
+ <circle cx="78" cy="200" r="4.5" fill="#64748B"/>
24
+ <path d="M71 213 a7 5.5 0 0 1 14 0" fill="#64748B"/>
25
+ <text x="106" y="199" fill="#0F172A" font-size="15" font-weight="600">Dev B</text>
26
+ <text x="106" y="219" fill="#64748B" font-size="11.5">Claude Code + projectstore</text>
27
+ </g>
28
+ <g>
29
+ <rect x="44" y="276" width="232" height="76" rx="12" fill="#F8FAFC" stroke="#E2E8F0"/>
30
+ <circle cx="78" cy="314" r="15" fill="#E2E8F0" stroke="#CBD5E1"/>
31
+ <circle cx="78" cy="310" r="4.5" fill="#64748B"/>
32
+ <path d="M71 323 a7 5.5 0 0 1 14 0" fill="#64748B"/>
33
+ <text x="106" y="309" fill="#0F172A" font-size="15" font-weight="600">Dev C</text>
34
+ <text x="106" y="329" fill="#64748B" font-size="11.5">reviews on GitHub or Obsidian</text>
35
+ </g>
36
+
37
+ <!-- bind arrows -->
38
+ <path d="M280 94 C 320 94, 330 106, 356 116" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#ltb)"/>
39
+ <path d="M280 204 L356 206" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#ltb)"/>
40
+ <path d="M280 314 C 320 314, 330 302, 356 294" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#ltb)"/>
41
+ <text x="308" y="196" fill="#64748B" font-size="10.5" text-anchor="middle">bind</text>
42
+
43
+ <!-- ── the vault ──────────────────────────────────────────────── -->
44
+ <rect x="360" y="52" width="252" height="310" rx="14" fill="#EFF6FF" stroke="#3B82F6"/>
45
+ <text x="486" y="86" fill="#1D4ED8" font-size="16" font-weight="600" text-anchor="middle">The vault</text>
46
+ <text x="486" y="106" fill="#64748B" font-size="11" text-anchor="middle">its own git repo · plain markdown</text>
47
+ <path d="M384 122 L588 122" stroke="#DBEAFE"/>
48
+ <text x="384" y="150" fill="#334155" font-size="12.5">adr/ — decisions, with dates</text>
49
+ <text x="384" y="177" fill="#334155" font-size="12.5">specs/ — the normative how</text>
50
+ <text x="384" y="204" fill="#334155" font-size="12.5">epics/ · stories/ — the backlog</text>
51
+ <text x="384" y="231" fill="#334155" font-size="12.5">kanban.md — the board</text>
52
+ <text x="384" y="258" fill="#334155" font-size="12.5">graph.md — every link, typed</text>
53
+ <text x="384" y="285" fill="#334155" font-size="12.5">code-map.md — epic ↔ code</text>
54
+ <text x="384" y="330" fill="#94A3B8" font-size="11">every write previewed and</text>
55
+ <text x="384" y="346" fill="#94A3B8" font-size="11">approved — by whoever binds</text>
56
+
57
+ <!-- ── the remote ─────────────────────────────────────────────── -->
58
+ <path d="M616 128 L696 128" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#ltb)"/>
59
+ <text x="656" y="118" fill="#64748B" font-size="10.5" text-anchor="middle">push</text>
60
+ <path d="M696 172 L616 172" stroke="#6B84AD" stroke-width="1.6" marker-end="url(#ltb)"/>
61
+ <text x="656" y="192" fill="#64748B" font-size="10.5" text-anchor="middle">review &amp; pull</text>
62
+
63
+ <rect x="700" y="88" width="216" height="132" rx="12" fill="#F8FAFC" stroke="#CBD5E1"/>
64
+ <text x="808" y="118" fill="#0F172A" font-size="15" font-weight="600" text-anchor="middle">git remote</text>
65
+ <text x="808" y="144" fill="#64748B" font-size="11.5" text-anchor="middle">push / pull</text>
66
+ <text x="808" y="164" fill="#64748B" font-size="11.5" text-anchor="middle">ADRs &amp; specs reviewed</text>
67
+ <text x="808" y="182" fill="#64748B" font-size="11.5" text-anchor="middle">as merge requests</text>
68
+
69
+ <text x="806" y="252" fill="#94A3B8" font-size="11.5" text-anchor="middle">the why gets reviewed,</text>
70
+ <text x="806" y="269" fill="#94A3B8" font-size="11.5" text-anchor="middle">not just the code</text>
71
+
72
+ <!-- ── the guarantees ─────────────────────────────────────────── -->
73
+ <rect x="284" y="384" width="86" height="26" rx="13" fill="#F1F5F9" stroke="#E2E8F0"/>
74
+ <text x="327" y="401" fill="#64748B" font-size="11.5" text-anchor="middle">no server</text>
75
+ <rect x="382" y="384" width="122" height="26" rx="13" fill="#F1F5F9" stroke="#E2E8F0"/>
76
+ <text x="443" y="401" fill="#64748B" font-size="11.5" text-anchor="middle">plain markdown</text>
77
+ <rect x="516" y="384" width="160" height="26" rx="13" fill="#F1F5F9" stroke="#E2E8F0"/>
78
+ <text x="596" y="401" fill="#64748B" font-size="11.5" text-anchor="middle">any model can read it</text>
79
+ </svg>
@@ -0,0 +1,79 @@
1
+ <svg width="960" height="430" viewBox="0 0 960 430" fill="none" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif">
2
+ <defs>
3
+ <marker id="tb" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
4
+ <path d="M0 0 L10 5 L0 10 z" fill="#5B7BB4"/>
5
+ </marker>
6
+ </defs>
7
+
8
+ <rect width="960" height="430" rx="18" fill="#0C1322"/>
9
+ <rect x="1" y="1" width="958" height="428" rx="17" stroke="#1E2A44"/>
10
+
11
+ <!-- ── the people ─────────────────────────────────────────────── -->
12
+ <g>
13
+ <rect x="44" y="56" width="232" height="76" rx="12" fill="#101B31" stroke="#24344F"/>
14
+ <circle cx="78" cy="94" r="15" fill="#1B2A45" stroke="#3D5378"/>
15
+ <circle cx="78" cy="90" r="4.5" fill="#93A6C8"/>
16
+ <path d="M71 103 a7 5.5 0 0 1 14 0" fill="#93A6C8"/>
17
+ <text x="106" y="89" fill="#E2E8F0" font-size="15" font-weight="600">Dev A</text>
18
+ <text x="106" y="109" fill="#7C8DB0" font-size="11.5">Claude Code + projectstore</text>
19
+ </g>
20
+ <g>
21
+ <rect x="44" y="166" width="232" height="76" rx="12" fill="#101B31" stroke="#24344F"/>
22
+ <circle cx="78" cy="204" r="15" fill="#1B2A45" stroke="#3D5378"/>
23
+ <circle cx="78" cy="200" r="4.5" fill="#93A6C8"/>
24
+ <path d="M71 213 a7 5.5 0 0 1 14 0" fill="#93A6C8"/>
25
+ <text x="106" y="199" fill="#E2E8F0" font-size="15" font-weight="600">Dev B</text>
26
+ <text x="106" y="219" fill="#7C8DB0" font-size="11.5">Claude Code + projectstore</text>
27
+ </g>
28
+ <g>
29
+ <rect x="44" y="276" width="232" height="76" rx="12" fill="#101B31" stroke="#24344F"/>
30
+ <circle cx="78" cy="314" r="15" fill="#1B2A45" stroke="#3D5378"/>
31
+ <circle cx="78" cy="310" r="4.5" fill="#93A6C8"/>
32
+ <path d="M71 323 a7 5.5 0 0 1 14 0" fill="#93A6C8"/>
33
+ <text x="106" y="309" fill="#E2E8F0" font-size="15" font-weight="600">Dev C</text>
34
+ <text x="106" y="329" fill="#7C8DB0" font-size="11.5">reviews on GitHub or Obsidian</text>
35
+ </g>
36
+
37
+ <!-- bind arrows -->
38
+ <path d="M280 94 C 320 94, 330 106, 356 116" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#tb)"/>
39
+ <path d="M280 204 L356 206" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#tb)"/>
40
+ <path d="M280 314 C 320 314, 330 302, 356 294" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#tb)"/>
41
+ <text x="308" y="196" fill="#7C8DB0" font-size="10.5" text-anchor="middle">bind</text>
42
+
43
+ <!-- ── the vault ──────────────────────────────────────────────── -->
44
+ <rect x="360" y="52" width="252" height="310" rx="14" fill="#0F2038" stroke="#3B82F6"/>
45
+ <text x="486" y="86" fill="#93C5FD" font-size="16" font-weight="600" text-anchor="middle">The vault</text>
46
+ <text x="486" y="106" fill="#7C8DB0" font-size="11" text-anchor="middle">its own git repo · plain markdown</text>
47
+ <path d="M384 122 L588 122" stroke="#1E3355"/>
48
+ <text x="384" y="150" fill="#A9B8D4" font-size="12.5">adr/ — decisions, with dates</text>
49
+ <text x="384" y="177" fill="#A9B8D4" font-size="12.5">specs/ — the normative how</text>
50
+ <text x="384" y="204" fill="#A9B8D4" font-size="12.5">epics/ · stories/ — the backlog</text>
51
+ <text x="384" y="231" fill="#A9B8D4" font-size="12.5">kanban.md — the board</text>
52
+ <text x="384" y="258" fill="#A9B8D4" font-size="12.5">graph.md — every link, typed</text>
53
+ <text x="384" y="285" fill="#A9B8D4" font-size="12.5">code-map.md — epic ↔ code</text>
54
+ <text x="384" y="330" fill="#64748B" font-size="11">every write previewed and</text>
55
+ <text x="384" y="346" fill="#64748B" font-size="11">approved — by whoever binds</text>
56
+
57
+ <!-- ── the remote ─────────────────────────────────────────────── -->
58
+ <path d="M616 128 L696 128" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#tb)"/>
59
+ <text x="656" y="118" fill="#7C8DB0" font-size="10.5" text-anchor="middle">push</text>
60
+ <path d="M696 172 L616 172" stroke="#5B7BB4" stroke-width="1.6" marker-end="url(#tb)"/>
61
+ <text x="656" y="192" fill="#7C8DB0" font-size="10.5" text-anchor="middle">review &amp; pull</text>
62
+
63
+ <rect x="700" y="88" width="216" height="132" rx="12" fill="#131F36" stroke="#2C3E5F"/>
64
+ <text x="808" y="118" fill="#E2E8F0" font-size="15" font-weight="600" text-anchor="middle">git remote</text>
65
+ <text x="808" y="144" fill="#7C8DB0" font-size="11.5" text-anchor="middle">push / pull</text>
66
+ <text x="808" y="164" fill="#7C8DB0" font-size="11.5" text-anchor="middle">ADRs &amp; specs reviewed</text>
67
+ <text x="808" y="182" fill="#7C8DB0" font-size="11.5" text-anchor="middle">as merge requests</text>
68
+
69
+ <text x="806" y="252" fill="#64748B" font-size="11.5" text-anchor="middle">the why gets reviewed,</text>
70
+ <text x="806" y="269" fill="#64748B" font-size="11.5" text-anchor="middle">not just the code</text>
71
+
72
+ <!-- ── the guarantees ─────────────────────────────────────────── -->
73
+ <rect x="284" y="384" width="86" height="26" rx="13" fill="#101B31" stroke="#24344F"/>
74
+ <text x="327" y="401" fill="#8FA1BF" font-size="11.5" text-anchor="middle">no server</text>
75
+ <rect x="382" y="384" width="122" height="26" rx="13" fill="#101B31" stroke="#24344F"/>
76
+ <text x="443" y="401" fill="#8FA1BF" font-size="11.5" text-anchor="middle">plain markdown</text>
77
+ <rect x="516" y="384" width="160" height="26" rx="13" fill="#101B31" stroke="#24344F"/>
78
+ <text x="596" y="401" fill="#8FA1BF" font-size="11.5" text-anchor="middle">any model can read it</text>
79
+ </svg>