geneseed 3.20.0 → 4.1.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 (125) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +9 -1
  3. package/SHIPPED.md +1 -1
  4. package/adapters/bob/README.md +31 -12
  5. package/adapters/claude-code/README.md +24 -17
  6. package/adapters/claude-code/settings.json +4 -6
  7. package/adapters/openclaude/README.md +13 -5
  8. package/adapters/opencode/HOW-OPENCODE-LOADS.md +35 -0
  9. package/adapters/opencode/plugins/geneseed-activity.js +105 -2
  10. package/adapters/opencode/plugins/geneseed-context.js +96 -1
  11. package/adapters/opencode/plugins/geneseed-guard.js +324 -175
  12. package/adapters/opencode/plugins/geneseed-learn.js +127 -14
  13. package/adapters/opencode/plugins/geneseed-notify.js +46 -1
  14. package/adapters/opencode/plugins/geneseed-ponytail.js +44 -2
  15. package/adapters/opencode/plugins/geneseed-workflow.js +124 -24
  16. package/bin/geneseed-hook.mjs +36 -5
  17. package/docs/README.md +3 -1
  18. package/docs/candidate-hosts.md +0 -8
  19. package/docs/concepts/enforcement-table.md +66 -0
  20. package/docs/concepts/hook-gates.md +2 -2
  21. package/docs/concepts/hooks.md +6 -6
  22. package/docs/concepts/host-matrix.md +4 -3
  23. package/docs/concepts/loop-branch.md +6 -1
  24. package/docs/concepts/rules.md +1 -1
  25. package/docs/design-history.md +59 -0
  26. package/docs/extending.md +11 -3
  27. package/docs/guides/install-opencode.md +1 -1
  28. package/docs/guides/loop-running.md +1 -1
  29. package/docs/guides/mcp-filesystem.md +2 -2
  30. package/docs/guides/mcp-gitlab.md +2 -2
  31. package/docs/guides/mcp-markitdown.md +3 -3
  32. package/docs/guides/mcp.md +7 -7
  33. package/docs/guides/project-context.md +1 -1
  34. package/docs/reference/env-context.md +3 -2
  35. package/docs/reference/environment.md +2 -1
  36. package/docs/reference/opencode-plugins.md +9 -0
  37. package/docs/reference/plugin-guard.md +3 -3
  38. package/docs/reference/troubleshoot-hooks.md +34 -0
  39. package/docs/understand/design-choices-engineering.md +29 -0
  40. package/docs/understand/design-choices-gates.md +23 -0
  41. package/docs/understand/design-choices-hosts.md +21 -0
  42. package/docs/understand/design-choices-install.md +19 -0
  43. package/docs/understand/design-choices-shape.md +21 -0
  44. package/docs/understand/design-choices.md +34 -0
  45. package/docs/understand/enforced-vs-asked.md +5 -4
  46. package/docs/understand/machine-bob.md +1 -1
  47. package/docs/understand/machine-opencode.md +2 -2
  48. package/harness.config.json +1 -1
  49. package/js/build/driver.mjs +42 -16
  50. package/js/build/emit-claude.mjs +24 -8
  51. package/js/build/emit-opencode.mjs +33 -15
  52. package/js/build/generate.mjs +38 -9
  53. package/js/build/source.mjs +39 -0
  54. package/js/cli-table.json +7 -7
  55. package/js/hosts/gitref.mjs +3 -3
  56. package/js/hosts/hooks-context.mjs +11 -30
  57. package/js/hosts/hooks-learn.mjs +89 -8
  58. package/js/hosts/hooks.mjs +212 -18
  59. package/js/hosts/hosts.mjs +217 -13
  60. package/js/hosts/installs.mjs +259 -46
  61. package/js/hosts/mcp.mjs +23 -152
  62. package/js/hosts/native.mjs +48 -12
  63. package/js/hosts/opencode.mjs +48 -18
  64. package/js/hosts/settings.mjs +366 -42
  65. package/js/hosts/shim.mjs +37 -0
  66. package/js/inspect/checks-authoring.mjs +81 -1
  67. package/js/inspect/checks-constitution.mjs +2 -1
  68. package/js/inspect/checks-repo.mjs +68 -4
  69. package/js/inspect/doctor.mjs +40 -9
  70. package/js/inspect/scan.mjs +5 -4
  71. package/js/inspect/status.mjs +62 -12
  72. package/js/lib/text.mjs +23 -0
  73. package/js/loop/catalog.mjs +7 -2
  74. package/js/maintain/migrate.mjs +42 -5
  75. package/js/maintain/uninstall.mjs +150 -38
  76. package/js/maintain/update.mjs +22 -2
  77. package/js/web/actions.mjs +43 -87
  78. package/js/web/api.mjs +13 -4
  79. package/js/web/handler.mjs +2 -2
  80. package/js/web/routes.mjs +1 -2
  81. package/js/web/state.mjs +28 -15
  82. package/package.json +1 -1
  83. package/src/agents/_template.md +3 -2
  84. package/src/agents/researcher.md +2 -1
  85. package/src/agents/reviewer.md +9 -4
  86. package/src/agents/tester.md +3 -3
  87. package/src/bricks/_template.md +1 -1
  88. package/src/doctrines/ops.md +9 -7
  89. package/src/doctrines/rigor.md +8 -7
  90. package/src/laws/universal.md +1 -1
  91. package/src/skills/_template.md +3 -3
  92. package/src/skills/brainstorm/builder-check.md +16 -0
  93. package/src/skills/brainstorm.md +19 -11
  94. package/src/skills/daydream/SKILL.md +1 -1
  95. package/src/skills/daydream/VENDOR.md +4 -0
  96. package/src/skills/debug/ci.md +4 -4
  97. package/src/skills/debug.md +4 -4
  98. package/src/skills/develop.md +6 -4
  99. package/src/skills/fresh-eyes.md +1 -1
  100. package/src/skills/geneseed-code-review.md +8 -2
  101. package/src/skills/handoff.md +1 -1
  102. package/src/skills/herdr.md +1 -1
  103. package/src/skills/parallel-agents.md +4 -4
  104. package/src/skills/pipeline.md +3 -3
  105. package/src/skills/plan.md +18 -6
  106. package/src/skills/review-response.md +5 -2
  107. package/src/skills/ship.md +2 -1
  108. package/src/skills/worktree.md +2 -0
  109. package/web/dist/assets/{Activity-IbwdHHyx.js → Activity-CqVt2u9s.js} +1 -1
  110. package/web/dist/assets/{ActivityDetail-B5uivdWI.js → ActivityDetail-Bui5Gomc.js} +1 -1
  111. package/web/dist/assets/{Dashboard-v6WQ37bD.js → Dashboard-VlIx1-At.js} +2 -2
  112. package/web/dist/assets/{Docs-CypIfDBX.js → Docs-6cYYkVeA.js} +1 -1
  113. package/web/dist/assets/{GreenhouseView-ClxP0hQG.js → GreenhouseView-DgNY8QKU.js} +1 -1
  114. package/web/dist/assets/{Installs-CPkQeFjO.js → Installs-DZwZOUZJ.js} +1 -1
  115. package/web/dist/assets/{JournalView-BeEInhUf.js → JournalView-DXpFbKOG.js} +1 -1
  116. package/web/dist/assets/{Laws-Ds6U5xBQ.js → Laws-Bh7ztekk.js} +1 -1
  117. package/web/dist/assets/{Library-BraUKPyQ.js → Library-DBLX-1SD.js} +1 -1
  118. package/web/dist/assets/{Loops-DWcy3mwK.js → Loops-CvFMZ7_P.js} +1 -1
  119. package/web/dist/assets/{OperatorHudView-CQyrJQ9m.js → OperatorHudView-FPTUH1CB.js} +1 -1
  120. package/web/dist/assets/{Personal-YWqs6qFL.js → Personal-DcVA502F.js} +1 -1
  121. package/web/dist/assets/{Tabs-EhzEC-Xh.js → Tabs-DtyR4h75.js} +1 -1
  122. package/web/dist/assets/{api-CxgfYu18.js → api-CRqWld2H.js} +1 -1
  123. package/web/dist/assets/{drawable-CEKPNV7X.js → drawable-CNSPVZxX.js} +1 -1
  124. package/web/dist/assets/{index-CPi22b7y.js → index-BmjcLIOr.js} +2 -2
  125. package/web/dist/index.html +2 -2
@@ -16,10 +16,9 @@ Geneseed writes its hooks into the host's settings file. Every hook calls one pr
16
16
 
17
17
  | Event | When | Command | What it does | Conditional? |
18
18
  | --- | --- | --- | --- | --- |
19
- | `SessionStart` | `startup`, `clear` | `geneseed-hook context` | injects your harness files and the repo's docs | no |
20
- | `SessionStart` | `resume`, `compact` | `geneseed-hook context` | re-injects them after a resume or an auto-compaction summarised them away | no |
21
- | `PreToolUse` | `Bash` | `geneseed-hook git-gate` | asks before destructive git; asks before every commit and push | the commit/push question only |
22
- | `PreToolUse` | `Write`, `Edit`, `MultiEdit`, `NotebookEdit` | `geneseed-hook rule-gate` | asks before a secret lands in a file, or a write to your rules or memory | no |
19
+ | `SessionStart` | `startup`, `resume`, `clear`, `compact`, `fork` | `geneseed-hook context` | injects your harness files and the repo's docs, or re-injects them after a resume, a fork, or an auto-compaction summarised them away | no |
20
+ | `PreToolUse` | `Bash`, `PowerShell` | `geneseed-hook git-gate` | asks before destructive git, and before a recursive scan of a whole filesystem; asks before every commit and push | the commit/push question only |
21
+ | `PreToolUse` | `Write`, `Edit`, `NotebookEdit` | `geneseed-hook rule-gate` | asks before a secret lands in a file, or a write to your rules or memory | no |
23
22
  | `Stop` | the agent finishes a reply | `geneseed-hook learn` | distils durable facts into `memory/` | needs `GENESEED_LLM` |
24
23
  | `SubagentStop` | a subagent finishes | `geneseed-hook learn` | records a lesson for that agent in `memory/agents/<name>.md` | needs `GENESEED_LLM` |
25
24
  | `PreCompact` | before auto-compaction | `geneseed-hook learn` | captures memory before the transcript is summarised | needs `GENESEED_LLM` |
@@ -28,9 +27,10 @@ Geneseed writes its hooks into the host's settings file. Every hook calls one pr
28
27
 
29
28
  **context.** Before your first message, the agent receives your own harness files — `user-rules.md`, `PROFILE.md`, the memory index, the notebook index, your wiki declaration — skipping any still untouched since install, then the repo's docs, discovered by convention or listed in a `context.json`. Each file is capped at 16 KB and the whole injection at 48 KB; what does not fit is listed for the agent to read on demand. You see nothing, except that the agent already knows your project. See [Project context](../guides/project-context.md).
30
29
 
31
- **git-gate.** Runs before every shell command and looks for `git` in it, including chained one-liners and `git -C <path>` forms.
30
+ **git-gate.** Runs before every shell command and looks for `git` in it, including chained one-liners and `git -C <path>` forms, and for a recursive scan of a whole filesystem.
32
31
  - **Destructive git** — `reset --hard`, `clean -f`, `branch -D`, `checkout -- `, a `push --force` — always gets a confirmation prompt, citing *Deletion Is Deliberate*. That is a Rule, so it holds in every build.
33
32
  - **Every `git commit` and `git push`** gets a prompt citing *Consent Before Push*. This part belongs to the **process** doctrine pack: build without it, or exclude that rule, and the hook stays wired with `--no-consent` — commits and pushes go through without asking, destructive git is still stopped ([Rules](rules.md)).
33
+ - **A recursive scan of a whole filesystem** — `find /`, `du -sh /`, `grep -r foo /`, `rg x C:\`, `Get-ChildItem C:\ -Recurse` — gets a prompt citing *Commands Must Return* (on Bob, it is blocked). In Git Bash `/` mounts every drive and mounted share, so such a scan runs for hours, and `| head` does not stop it. The prompt tells the agent to search a specific directory instead. A deeper path (`find /c/Users/me/project`), `ls /` without `-R` (`ls -r` is reverse order, not recursion), a quoted argument (a search pattern such as `grep -rn "/" src`), a `#` comment, and the remote half of an `ssh` command pass. This check is wired in every build, whatever packs or rules you excluded: it rides inside `git-gate` with no flag of its own, because a new flag or verb would reach machines still running an older hook shim, which would refuse it — and Claude Code blocks a tool call whose gate fails.
34
34
  - Any other command passes untouched.
35
35
 
36
36
  **rule-gate.** Runs before every file write.
@@ -45,7 +45,7 @@ Geneseed writes its hooks into the host's settings file. Every hook calls one pr
45
45
  export GENESEED_LLM="claude -p"
46
46
  ```
47
47
 
48
- Unset, `learn` does nothing useful — it prints the prompt it would have sent. Geneseed never stores or embeds an API key. See [Memory](memory.md).
48
+ Unset, `learn` does nothing useful: on a real Stop/SubagentStop/PreCompact call it returns immediately rather than reading the transcript for nothing, and on a manual run it prints the prompt it would have sent. Geneseed never stores or embeds an API key. See [Memory](memory.md).
49
49
 
50
50
  ## Verify
51
51
 
@@ -18,13 +18,14 @@ Every [host](hosts.md) gets the same content. This table is what each one lets G
18
18
  | Secrets kept out of files | ⚙️ block⁴ | 🪝 ask | 🪝 block¹ | 🪝 ask² |
19
19
  | Consent before every commit and push | 🔒 permission ask | 🪝 ask | 🪝 warning¹ | 🪝 ask² |
20
20
  | Rule-or-memory write needs your call | ⚙️ speed bump³ | 🪝 ask | 🪝 warning¹ | 🪝 ask² |
21
+ | User-only skills hidden from the model | 🔒 `permission.skill` deny | ✅ `disable-model-invocation` | ✅ `/`-command, no skill | ✅ `disable-model-invocation` |
21
22
  | Sovereign-repo exclusions | ⚙️ plugins stand down | ✅ `claudeMdExcludes` | ✅ rules shadow stub | ✅ `claudeMdExcludes` |
22
23
  | MCP server wiring | ✅ `mcp` | ✅ `mcpServers` | ✅ `mcp.json` | ✅ `mcpServers` |
23
24
  | Colour themes · LSP · workflow runner · primary agent · `/`-commands | ✅ | ➖ | ➖ | ➖ |
24
25
 
25
- ✅ native · ⚙️ OpenCode plugin · 🔒 `permission.bash` entry in `opencode.json` · 🪝 `settings.json` hook · ➖ no host mechanism (the rule still applies as prose).
26
+ ✅ native · ⚙️ OpenCode plugin · 🔒 `permission` entry in `opencode.json` · 🪝 `settings.json` hook · ➖ no host mechanism (the rule still applies as prose).
26
27
 
27
- ¹ **Bob** has its own hook contract: Claude's event names, but it ignores a hook's output on `PreToolUse`, so the only way to refuse is exit code 2. Geneseed therefore blocks only the two always-on rules (secrets, destructive git) and turns the two consent checks into a logged warning. It has no `SubagentStop` or `PreCompact` event, so learn runs on `Stop` only. **Unverified live** — there is no Bob install on the authoring machine; the rules preamble still holds if a hook does not fire.
28
+ ¹ **Bob** has its own hook contract: Claude's event names, but it ignores a hook's output on `PreToolUse`, so the only way to refuse is exit code 2. Geneseed therefore blocks only the rules that admit no judgement call — secrets, destructive git, a recursive scan of a whole filesystem, and a write under a project's own protected-checks list — and turns the two consent checks into a logged warning. It has no `SubagentStop` or `PreCompact` event, so learn runs on `Stop` only. **Unverified live** — there is no Bob install on the authoring machine; the rules preamble still holds if a hook does not fire.
28
29
  ² **OpenClaude** is a Claude Code fork and gets Claude's hooks and verdicts verbatim, in `~/.openclaude/settings.json` (global) or `.openclaude/settings.local.json` (per repo). **Unverified live** for the same reason.
29
30
  ³ OpenCode's plugin hook can only allow or throw — there is no "ask the user" — so this check stops the first such write once rather than prompting.
30
31
  ⁴ The checks differ in shape: OpenCode's guard plugin blocks writes *to* private-key and credential files by path; the Claude-shaped hook looks at the *content* being written for credential-shaped strings (cloud keys, tokens, private-key blocks) and asks. A `.env` file is exempt on the hook side — that is where a secret is allowed to live.
@@ -33,6 +34,6 @@ Every [host](hosts.md) gets the same content. This table is what each one lets G
33
34
 
34
35
  The **consent before commit and push** row depends on a choice you make at build time. It enforces the rule *Consent Before Push*, which belongs to the **process** doctrine pack ([Rules](rules.md)). Build without that pack, or exclude that one rule, and the commit/push question goes away — but the gate itself stays: on Claude Code and OpenClaude the `git-gate` hook is still wired (with `--no-consent`), so destructive git is still caught. Everything else in the table is always on.
35
36
 
36
- One OpenCode asymmetry: Geneseed shares `opencode.json` with you and cannot tell its own `git commit*` permission entry from one you typed, so a rebuild without the process pack leaves an existing entry in place and says so instead of deleting it.
37
+ One OpenCode asymmetry: Geneseed shares `opencode.json` with you and cannot tell its own `git commit*` permission entry from one you typed, so a rebuild without the process pack leaves an existing entry in place and says so instead of deleting it. A `permission.skill` deny is different: Geneseed records each one it writes, and a rebuild or uninstall takes back only those, never a deny you set yourself.
37
38
 
38
39
  In plain terms: [Enforced vs. asked](../understand/enforced-vs-asked.md).
@@ -40,7 +40,12 @@ explicit `HEAD:<ref>` — any other shape still asks. The exemption only ever co
40
40
  branch, and never a shared one (`main`, `master`, `develop`, `development`, `release/*`,
41
41
  `hotfix/*`). The **final** commit of a run — the one that removes `LOOP.md` on `$close` — is
42
42
  outside the exemption too, because the loop is no longer running when it lands; that ask is your
43
- consent to close it. **Merging a loop branch into anything else always asks, with no exception.**
43
+ consent to close it. On OpenCode there is no DYNAMIC exemption at all — the host never calls the
44
+ plugin hook it would need — so every commit still asks. Its PUSH does not: pushing a `loop/*`
45
+ branch is not pushing `main`/`master`, so OpenCode's `permission.bash` carries a static `allow`
46
+ for the loop engine's own push shapes (user decision, 2026-10-10) — a force push, a `+refspec`,
47
+ or a `--delete`/bare-`:` push to `loop/*` still asks there too.
48
+ **Merging a loop branch into anything else always asks, with no exception.**
44
49
  The loop reports the branch to merge and the iterations that landed in "to review" — it never
45
50
  merges for you.
46
51
 
@@ -51,4 +51,4 @@ A [theme](themes.md) renames the nouns — an `imperial` install calls the invar
51
51
 
52
52
  ### Related
53
53
 
54
- [Footprint](footprint.md) — how much of this text loads every turn · [Exclusions](exclusions.md) — switching rules off per rule versus per repo · [Glossary](../reference/glossary.md)
54
+ [Footprint](footprint.md) — how much of this text loads every turn · [Exclusions](exclusions.md) — switching rules off per rule versus per repo · [Rule enforcement, by host](enforcement-table.md) — which rules a hook actually checks · [Glossary](../reference/glossary.md)
@@ -1288,3 +1288,62 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
1288
1288
  gitignored `__pycache__` executed instead of the source after a `git stash` round-trip
1289
1289
  restored an mtime the cache satisfied — **a test can fail, or pass, against bytecode that is
1290
1290
  not the source.**
1291
+
1292
+ ---
1293
+
1294
+ ## Considered and refused
1295
+
1296
+ Ideas that were weighed and turned down. Each looks reasonable on its own, which is why it keeps
1297
+ coming back; the reason it was refused is recorded beside it. **An agent must not re-propose an
1298
+ idea from this list without new evidence** that the stated reason no longer holds. The choices
1299
+ Geneseed did make are listed in [Why Geneseed works this way](understand/design-choices.md).
1300
+
1301
+ - **A backup or restore layer for uninstall** (2026-10-05). Managed blocks, key-by-key merges and
1302
+ claim-on-create already mean uninstall overwrites nothing of yours, so there is nothing to
1303
+ restore. See [ADR 0004](adr/0004-uninstall-without-backups.md).
1304
+ - **A full-screen terminal interface (TUI)**, refused twice: dropped in the 2.0 move off Python
1305
+ (2026-08-11), and rejected again (2026-10-02). Corporate Windows consoles mangle ANSI escapes and
1306
+ code pages; the line wizard and the web console cover the same ground.
1307
+ - **Retiring the D/O/F/R/Q/A codes** (2026-08-27), **or replacing them with Conventional Comments**
1308
+ (2026-09-09). The codes are in daily use, and the rule that carries them, Codes That Persist, is
1309
+ already optional with the comms pack. The two schemes answer different questions: a code
1310
+ addresses an item across a conversation, and a Conventional Comments label grades a review
1311
+ finding's severity. Both stay.
1312
+ - **Merging laws that look alike** (2026-08-27): three pairs of laws, plus Data, Not Orders with
1313
+ External Gate, which was then a law and is now a rigor doctrine. Each pair is a deliberate
1314
+ seam, and each side guards a different failure. Surface Failures stops and reports a failure; Cure the Cause fixes
1315
+ its root. One Intent, One Act bounds what a single change is for; Least Privilege bounds the
1316
+ power it uses. Deletion Is Deliberate guards acts that cannot be undone; Least Privilege guards
1317
+ access. Data, Not Orders keeps read content from steering the agent; External Gate keeps the
1318
+ agent from approving its own actions.
1319
+ - **Splitting the web console's fonts per flavour** (2026-08-29). A browser only fetches the font
1320
+ the active flavour uses, so a split saves nothing and adds a flash of unstyled text.
1321
+ - **`Intl.NumberFormat`, `util.parseArgs` and an external heading-id library** (2026-08-27).
1322
+ Each diverges on real inputs: `Intl` at unit boundaries in the compact number format, the
1323
+ heading library on a large share of existing anchors, and `parseArgs` cannot express the
1324
+ argument queue the CLI needs.
1325
+ - **A `--validate-only` flag on `geneseed build`** (2026-09-30). The generator may not start a
1326
+ process, and validation runs the doctor; `geneseed validate` is the dry run. See
1327
+ [Verify it works](guides/verify.md).
1328
+ - **An accessibility agent, a memory-curator agent and a triager agent** (2026-09-06).
1329
+ Accessibility is part of the reviewer agent's job, memory curation belongs in a skill, and
1330
+ foreman mode is the triager.
1331
+ - **An external skill loader** (catalogues, a gateway skill, sync and lock files) **and skills
1332
+ bound to a ticket tracker** (2026-10-08). The loader would save about 0.5k tokens per
1333
+ turn at the cost of a large release; it is worth reviving only for a large external catalogue.
1334
+ The ticket-tracker skills had no tracker to serve.
1335
+ - **TypeScript `strict` mode** (not pursued, 2026-10-09). It would take thousands of annotations
1336
+ for a scorecard gain under two points. Gaming the scorecard's API-docs check with a bare
1337
+ `jsdoc.json` was refused outright.
1338
+ - **A tool matcher on Bob's gate hook** (2026-09, reconfirmed in the 2026-10 host review). Bob's
1339
+ tool list drifts from its own changelog, so an allow-list would let a new write tool through,
1340
+ and a deny-list needs a regex dialect Bob's docs never confirm; the gain would be speed only.
1341
+ See [the Bob adapter](../adapters/bob/README.md).
1342
+ - **Splitting the context hook into two commands** (2026-10-10). Measured on real repositories,
1343
+ the split would have recovered a dropped document in only a few of them; the hook keeps one
1344
+ command under its output budget instead. See [Token footprint](token-footprint.md).
1345
+ - **A runtime posture or mode toggle that replaces the build-time anchor** (2026-07-13). A register
1346
+ fixed at build time does not drift during a session, which is the point of anchoring it, so the
1347
+ anchor stays. A runtime toggle that works beside the anchor, without replacing it, remains
1348
+ possible. See
1349
+ [Foreman mode](concepts/foreman-mode.md).
package/docs/extending.md CHANGED
@@ -317,8 +317,14 @@ Rare, and mechanical. On top of §2a for each rule the pack carries:
317
317
  marker (§9), which names only the packs that existed when it was built, so a new pack reaches
318
318
  an existing install only if that install is re-emitted *and* the reader adds it. A pack that
319
319
  merely adds rules can stay off for old installs. A pack that *moves* a rule out of another
320
- pack cannot, or the moved rule disappears on the next rebuild: `comms` (2026-09-27) is the
321
- worked example — see `legacyProcessCarrier` in `js/hosts/installs.mjs`.
320
+ pack cannot, or the moved rule disappears on the next rebuild — `comms` (2026-09-27) is the
321
+ worked example. Through 3.x this was auto-migrated on sight; 4.0.0 retired that (CHANGELOG
322
+ "Removed") because the detector it relied on breaks the day the donor pack regains a rule at
323
+ the same old count. `js/hosts/installs.mjs`'s `legacyProcessCarrier` now only warns a
324
+ pre-migration install to re-run `geneseed setup` rather than rewriting its data for it, and
325
+ `legacyProcessCarrierOfDir` makes `rebuild-all`/`upgrade` (§9 below) skip that install
326
+ entirely rather than re-emit it — re-emitting would write a fresh carrier that drops the
327
+ rewrite silently and for good, since no `process 8` text survives it to warn about next time.
322
328
 
323
329
  ---
324
330
 
@@ -785,7 +791,9 @@ rows, nor `web/package.json`.
785
791
  **Which installs get missed:** project installs that were never registered; installs disabled via
786
792
  the `.geneseed-disabled` stash; registry rows whose directory is gone; and everything after the
787
793
  point where a `rebuild-all` fails mid-loop — it is best-effort by contract and leaves a mixed
788
- machine. Autostart entries are surveyed and reported, never rewritten.
794
+ machine. Autostart entries are surveyed and reported, never rewritten. An install whose carrier
795
+ predates the comms pack (§2d item 7) is also skipped, deliberately, every run until its owner
796
+ runs `geneseed setup` for it — `rebuild-all` names it SKIPPED rather than re-emitting it.
789
797
 
790
798
  **The web daemon serves the code it launched with**, plus an in-memory cache of every dist file.
791
799
  After a `web/dist` rebuild the correct move is `geneseed web restart`, not a hard refresh —
@@ -14,7 +14,7 @@ The setup wizard runs this for you (see [Install](install.md)). Use the commands
14
14
  geneseed build --emit opencode-global
15
15
  ```
16
16
 
17
- This writes into OpenCode's config directory: `$OPENCODE_CONFIG_DIR`, else `$XDG_CONFIG_HOME/opencode`, else `~/.config/opencode` (on Windows too: `C:\Users\<you>\.config\opencode`). The install contains `AGENT.md`, `agents/`, `skills/`, the plugins, the `memory/` store, and a merged `opencode.json` whose `instructions` key points at `AGENT.md`. Every repo you open in OpenCode inherits it. You do not need a per-repo file, because the context plugin finds each repo's docs by itself (see [Project context](project-context.md)).
17
+ This writes into OpenCode's config directory: `$OPENCODE_CONFIG_DIR`, else `$XDG_CONFIG_HOME/opencode`, else `~/.config/opencode` (on Windows too: `C:\Users\<you>\.config\opencode`). The install contains `AGENT.md`, `agents/`, `skills/`, the plugins, the `memory/` store, and a merged `opencode.json` whose `instructions` key points at `AGENT.md`. It also writes a one-line Geneseed block into `AGENTS.md` there when that file is missing. OpenCode loads `~/.claude/CLAUDE.md` only when `AGENTS.md` is absent, and a Claude install keeps the harness in that file too, so without the block the harness would load twice. If you already have an `AGENTS.md`, it is left alone. Every repo you open in OpenCode inherits it. You do not need a per-repo file, because the context plugin finds each repo's docs by itself (see [Project context](project-context.md)).
18
18
 
19
19
  The install is non-destructive. A manifest (`.geneseed-manifest.json`) records the files Geneseed owns. A rebuild replaces and prunes only those files, and never touches your own agents, skills, plugins or memory.
20
20
 
@@ -45,7 +45,7 @@ The agent passes your answer on as `geneseed loop decide --verdict ok|no|amend -
45
45
  git log --format='%h %s%n%(trailers)' loop/<slug>
46
46
  ```
47
47
 
48
- While a loop runs on its `loop/*` branch, the git gate lets exactly one commit-and-push shape through without asking, the one the agent uses to close each iteration. Any other git command still asks, as usual. Bob has no ask prompt, so there the commit and push gate logs a warning instead of asking.
48
+ While a loop runs on its `loop/*` branch, the git gate lets exactly one commit-and-push shape through without asking, the one the agent uses to close each iteration. Any other git command still asks, as usual. Bob has no ask prompt, so there the commit and push gate logs a warning instead of asking. OpenCode has no DYNAMIC exemption: its plugin hook for it is never called, so a loop there still asks on every commit. Its push to a `loop/*` branch does not — pushing a branch that is not `main`/`master` is allowed statically — but a force push, a `+refspec`, or a `--delete`/bare-`:` push still asks, even to `loop/*`.
49
49
 
50
50
  ## Stop, resume, and what ends a loop
51
51
 
@@ -6,7 +6,7 @@ kind: "concept"
6
6
  section: "Set up"
7
7
  description: "File access limited to the folders you list, and the placeholder path to replace."
8
8
  ---
9
- One of the [MCP server](mcp.md) presets. Where each host keeps the config, and how to check the server connected, are on that page.
9
+ One of the [MCP servers](mcp.md) Geneseed documents. Where each host keeps the config, and how to check the server connected, are on that page.
10
10
 
11
11
  [`@modelcontextprotocol/server-filesystem`](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) gives the agent file access limited to the folders you list, through `npx`. **The allowed folders are command-line arguments.** The server refuses any path outside them, so grant the narrowest set that works, not `$HOME` or `/`.
12
12
 
@@ -34,7 +34,7 @@ One of the [MCP server](mcp.md) presets. Where each host keeps the config, and h
34
34
  ```
35
35
  <!--/harness-->
36
36
 
37
- > **Replace the placeholder.** The preset ships with the literal path `/path/to/allowed/dir`. If you enable the server without changing it, the server runs but can reach nothing. This is the classic "the filesystem MCP sees nothing" problem.
37
+ > **Replace the placeholder.** The blocks above end with a sample path. If you register the server without changing it, the server runs but can reach nothing. This is the classic "the filesystem MCP sees nothing" problem.
38
38
 
39
39
  ---
40
40
 
@@ -6,7 +6,7 @@ kind: "concept"
6
6
  section: "Set up"
7
7
  description: "Merge requests, issues and CI from the agent: token scopes, the config block, a second instance."
8
8
  ---
9
- One of the [MCP server](mcp.md) presets. Where each host keeps the config, and how to check the server connected, are on that page.
9
+ One of the [MCP servers](mcp.md) Geneseed documents. Where each host keeps the config, and how to check the server connected, are on that page.
10
10
 
11
11
  > **Never commit a real token.** The presets ship with an empty `GITLAB_PERSONAL_ACCESS_TOKEN` and a sample filesystem path. Fill them in your own config, never in a tracked file.
12
12
 
@@ -59,7 +59,7 @@ You can also register it with `claude mcp add`.
59
59
 
60
60
  Keep the `/api/v4` suffix on the URL.
61
61
 
62
- **A second instance** (gitlab.com plus a self-hosted server, for example) is a copy of the same block under a new name, such as `gitlab-acme`, with its own `GITLAB_API_URL` and token. The preset covers the first instance only. You add the copy by hand.
62
+ **A second instance** (gitlab.com plus a self-hosted server, for example) is a copy of the same block under a new name, such as `gitlab-acme`, with its own `GITLAB_API_URL` and token. You add the copy by hand, like the first.
63
63
 
64
64
  ---
65
65
 
@@ -6,13 +6,13 @@ kind: "concept"
6
6
  section: "Set up"
7
7
  description: "Convert PDF and Office files to markdown: uvx or pipx, OCR extras, corporate TLS."
8
8
  ---
9
- One of the [MCP server](mcp.md) presets. Where each host keeps the config, and how to check the server connected, are on that page.
9
+ One of the [MCP servers](mcp.md) Geneseed documents. Where each host keeps the config, and how to check the server connected, are on that page.
10
10
 
11
11
  [MarkItDown](https://github.com/microsoft/markitdown) (Microsoft) converts PDF, Word, Excel, PowerPoint and HTML to markdown. It exposes one tool, `convert_to_markdown(uri)`, which accepts `file:`, `http:`, `https:` and `data:` URIs.
12
12
 
13
13
  **1. Make the command resolve.**
14
14
 
15
- - **`uvx` (recommended, nothing to install).** If you have [uv](https://docs.astral.sh/uv/), `uvx markitdown-mcp` fetches and caches the server on first use. This is the command the preset uses.
15
+ - **`uvx` (recommended, nothing to install).** If you have [uv](https://docs.astral.sh/uv/), `uvx markitdown-mcp` fetches and caches the server on first use. This is the command the blocks in [MCP servers](mcp.md) use.
16
16
  - **`pipx`.** Without uv, run `pipx install markitdown-mcp`, then use `markitdown-mcp` alone as the command (OpenCode: `["markitdown-mcp"]`; Claude: `"command": "markitdown-mcp"`, no args).
17
17
 
18
18
  ```
@@ -29,7 +29,7 @@ export UV_SYSTEM_CERTS=true # older uv: UV_NATIVE_TLS=true
29
29
  # fallback: export SSL_CERT_FILE=/path/to/corporate-root-ca.pem
30
30
  ```
31
31
 
32
- **3. Register it**, with the console toggle or one of the blocks in [MCP servers](mcp.md). Then restart your agent.
32
+ **3. Register it** by adding one of the blocks in [MCP servers](mcp.md) to your config. Then restart your agent.
33
33
 
34
34
  ---
35
35
 
@@ -4,15 +4,15 @@ order: 16
4
4
  title: "MCP servers"
5
5
  kind: "concept"
6
6
  section: "Set up"
7
- description: "Where MCP config lives for each host, the three presets, and how to check a server connected."
7
+ description: "Where MCP config lives for each host, three servers worth adding, and how to check a server connected."
8
8
  ---
9
- MCP (Model Context Protocol) servers give your agent extra tools: converting a PDF, opening a merge request, reading files outside the repo. Geneseed ships three ready-made server **presets** (MarkItDown, GitLab and Filesystem) that you can switch on without writing JSON by hand.
9
+ MCP (Model Context Protocol) servers give your agent extra tools: converting a PDF, opening a merge request, reading files outside the repo. Geneseed documents three servers worth adding (MarkItDown, GitLab and Filesystem), with the config block for each host. You add them to your config yourself: which servers you need depends on you and on your host.
10
10
 
11
11
  > **You already know this:** an MCP entry is a line in a Procfile. It names a command the agent starts on demand. Writing the line does not install the program behind it.
12
12
 
13
- Each preset is a *local* server, a command your agent launches when it needs the server. Registering a preset only points the agent at that command. **You** install the tool behind it (or let `uvx` or `npx` fetch it) and you supply any credentials.
13
+ Each of the three is a *local* server, a command your agent launches when it needs the server. Registering one only points the agent at that command. **You** install the tool behind it (or let `uvx` or `npx` fetch it) and you supply any credentials.
14
14
 
15
- > **Never commit a real token.** The presets ship with an empty `GITLAB_PERSONAL_ACCESS_TOKEN` and a sample filesystem path. Fill them in your own config, never in a tracked file.
15
+ > **Never commit a real token.** The blocks here carry an empty `GITLAB_PERSONAL_ACCESS_TOKEN` and a sample filesystem path. Fill them in your own config, never in a tracked file.
16
16
 
17
17
  ## Where the config lives
18
18
 
@@ -35,11 +35,11 @@ The same MarkItDown server in both shapes:
35
35
  { "mcpServers": { "markitdown": { "command": "uvx", "args": ["markitdown-mcp"] } } }
36
36
  ```
37
37
 
38
- **Toggle instead of editing.** In the web console (`geneseed web`), **Settings → MCP servers** adds, enables, disables or removes any preset in the right file, already in the right shape. Not sure which file that is? The **Harnesses** page shows the config path for each install, and `geneseed mcp` prints the same paths and lists the servers each install has wired, without starting anything.
38
+ **See what is wired.** Geneseed never writes these files. The web console (`geneseed web`) shows, for each active install, its config path and the servers it holds, enabled or disabled, with a button that opens the folder so you can edit the file. `geneseed mcp` prints the same in a terminal, without starting anything.
39
39
 
40
- The reference OpenCode config ships MarkItDown enabled and the GitLab and Filesystem entries disabled, so a merge never switches on a server that has no credentials yet. Fill in the blanks, then switch the server on.
40
+ The reference OpenCode config ships MarkItDown enabled and the GitLab and Filesystem entries disabled, so a merge never switches on a server that has no credentials yet. Fill in the blanks, then set `enabled` to `true`.
41
41
 
42
- ## The presets
42
+ ## Three servers worth adding
43
43
 
44
44
  - [MarkItDown](mcp-markitdown.md): converts PDF, Word, Excel, PowerPoint and HTML to markdown.
45
45
  - [GitLab](mcp-gitlab.md): repository, merge request, issue and CI tools over the GitLab API.
@@ -99,7 +99,7 @@ A line is simply left out when its source is missing.
99
99
 
100
100
  ## How OpenCode delivers it
101
101
 
102
- By default, the plugin delivers the context invisibly. It prepends the context to each request through OpenCode's experimental `experimental.chat.messages.transform` hook, so nothing appears in the conversation. Because it is re-sent with every request, the context also survives compaction. If your OpenCode build lacks that hook, the plugin notices and falls back to posting a visible `PROJECT CONTEXT` message.
102
+ By default, the plugin delivers the context invisibly. It prepends the context to each request through OpenCode's experimental `experimental.chat.messages.transform` hook, so nothing appears in the conversation. Because it is re-sent with every request, the context also survives compaction. If your OpenCode build lacks that hook, the plugin notices and falls back to posting a visible `PROJECT CONTEXT` message. On OpenCode 2 the same invisible delivery goes through its stable per-request `context` hook, so no fallback is needed.
103
103
 
104
104
  - `GENESEED_CONTEXT_VISIBLE=1` always shows the visible block, so you can see what the agent received.
105
105
  - `GENESEED_CONTEXT_INJECT=off` turns injection off. The agent then relies on the instruction in `AGENT.md` to read the docs itself.
@@ -19,7 +19,7 @@ What gets loaded into the agent's context at session start — your project's do
19
19
  | `GENESEED_CONTEXT` | context plugin, context hook | Explicit `context.json` manifest path; overrides discovery. |
20
20
  | `GENESEED_ROOT` | context hook | Repo root to discover docs from (default: the current directory). |
21
21
  | `GENESEED_WIKI` | context + guard plugins, context hook, web console | Explicit `geneseed-wiki.jsonc` path (default `$GENESEED_HARNESS/geneseed-wiki.jsonc`, else beside the install). |
22
- | `GENESEED_STACK_GLOBAL` | context hook, Claude Code project build | `1` lets a global and a project install of the same host both inject. By default the project install makes the global one stand down. Rebuild the project install after changing it. |
22
+ | `GENESEED_STACK_GLOBAL` | context, git-gate and learn hooks, Claude Code project build | `1` lets a global and a project install of the same host both run. By default a live project install in the session's project folder (not a parent of it) that wires the same hook makes the global one stand down. Rebuild the project install after changing it. |
23
23
  | `GENESEED_CONTEXT_INJECT` | context plugin | `off` disables injection; the agent is only *asked* to read the docs. |
24
24
  | `GENESEED_CONTEXT_VISIBLE` | context plugin | `1` shows the visible `PROJECT CONTEXT` block instead of the invisible per-request delivery. |
25
25
  | `GENESEED_CONTEXT_TRANSFORM` | context plugin | Legacy. `0`/`off` = same as `GENESEED_CONTEXT_VISIBLE=1`; `1` matches the default. |
@@ -36,5 +36,6 @@ Where distilled memories land at session end, and which model distils them. See
36
36
  | `GENESEED_HARNESS` | learn, context, guard plugins; CLI | Base whose `memory/` is written (and where `geneseed-wiki.jsonc` is looked up). Optional — the plugin finds the in-config store by itself; set it to pin the location. |
37
37
  | `GENESEED_MEMORY` | learn plugin, CLI | Explicit memory dir; wins over `GENESEED_HARNESS`. |
38
38
  | `GENESEED_MODEL` | learn + context plugins | `provider/model` fallback when the session's model can't be read from the transcript. |
39
- | `GENESEED_LLM` | learn hook (Claude Code, Bob, OpenClaude) | Model CLI used to distil, e.g. `claude -p`. Unset, the hook prints the prompt instead of distilling. |
39
+ | `GENESEED_LLM` | learn hook (Claude Code, Bob, OpenClaude) | Model CLI used to distil, e.g. `claude -p`. Unset, a real Stop/SubagentStop/PreCompact call returns immediately — nothing reads its stdout there; a manual `learn` run (notes piped or given as a file) still prints the prompt instead of distilling. |
40
+ | `GENESEED_LEARN_CHILD` | learn hook | Set by `learn` itself on the model CLI it spawns; a nested session's own Stop hook sees it and returns immediately, which stops `GENESEED_LLM="claude -p"` (hooks not disabled) from recursing. Not meant to be set by hand. |
40
41
  | `GENESEED_LEARN_DEBOUNCE_MS` | learn plugin | Quiet period before distilling, in ms (default 60000). |
@@ -20,8 +20,9 @@ Where Geneseed writes an install, and what an upgrade re-renders.
20
20
  | --- | --- | --- |
21
21
  | `GENESEED_HOME` | build / hooks | Directory holding the hook shim `bin/geneseed-hook` (default `~/.geneseed`). Change it, then rebuild, so the emitted hooks point at the new location. |
22
22
  | `GENESEED_NO_SHIM_CHECK` | CLI | `1` silences the warning every `geneseed` command prints when the hook shim points at a path that no longer exists. For scripted callers; the `status` gates row still says `DEAD`. |
23
- | `OPENCODE_CONFIG_DIR` | OpenCode emits, `diff`, plugins | OpenCode's config dir — where the global install is written. |
23
+ | `OPENCODE_CONFIG_DIR` | OpenCode emits, `diff`, plugins | OpenCode's config dir — where the global install is written. OpenCode still loads `$XDG_CONFIG_HOME/opencode` beside it, so `status` and `doctor` warn when both hold a Geneseed install. |
24
24
  | `XDG_CONFIG_HOME` | OpenCode emits, plugins | Used when `OPENCODE_CONFIG_DIR` is unset: the install goes to `$XDG_CONFIG_HOME/opencode` (default `~/.config/opencode`). |
25
+ | `CLAUDE_CONFIG_DIR` | Claude Code emits, hooks, lifecycle verbs | Claude Code's config dir (default `~/.claude`) — where the global install is written. A Geneseed install left in `~/.claude` is still treated as a global, never as a project at your home folder. |
25
26
  | `BOB_CONFIG_DIR` | Bob emits | Bob's config dir (default `~/.bob`). |
26
27
  | `OPENCLAUDE_CONFIG_DIR` | OpenClaude emits | OpenClaude's own config-dir variable (default `~/.openclaude`); also moves `.openclaude.json`. |
27
28
  | `GENESEED_EMIT` | `geneseed upgrade` | Which emit target to re-render (e.g. `opencode-global`). Unset, it is read from the install's `.geneseed-emit` marker, else the plain bundle. |
@@ -14,6 +14,15 @@ Every plugin installs in the same step as the rest of the harness: `geneseed set
14
14
 
15
15
  Every plugin swallows its own errors — none can block or crash a session. Each plugin's page lists the variables that tune it.
16
16
 
17
+ ## OpenCode 2
18
+
19
+ The same files work on OpenCode 1 (1.3.4 or newer) and OpenCode 2. OpenCode 2 rewrote the plugin API, and each plugin carries both halves. You don't need to configure anything. Two differences show on OpenCode 2:
20
+
21
+ - **A guard block shows up as a failed tool call.** The call never runs. The model gets an error that starts with `[geneseed-guard] blocked:` and gives the reason.
22
+ - **The Activity view shows no todo list or files-touched count.** OpenCode 2 publishes no event for either.
23
+
24
+ If a plugin seems silent on OpenCode 2, check that `~/.config/opencode/plugins/` holds the current files. Copies from before this change fail to load with *"Plugin must export a default definition with an id and an effect or setup function"*. Running `geneseed rebuild-all` replaces them.
25
+
17
26
  ## The plugins
18
27
 
19
28
  - [geneseed-context](plugin-context.md): Puts your project docs, rules, profile, memory index and wiki in front of the agent.
@@ -10,15 +10,15 @@ One of the [OpenCode plugins](opencode-plugins.md). [Environment variables](envi
10
10
 
11
11
  **What it does.** Enforces the safety rules at the tool boundary — before a tool call runs, not after. High-confidence patterns only, so legitimate work is not caught.
12
12
 
13
- - **Blocks** writes to private-key and credential files (*Sealed Secrets*), catastrophic shell commands such as deleting the filesystem root (*Deletion Is Deliberate*), and any change — write, move, rename, delete — under a wiki folder declared `protected` in `geneseed-wiki.jsonc`, and writes to a check listed in the project's `.geneseed/protected-checks.txt` (*External Gate* — see [Hooks](../concepts/hooks.md)).
13
+ - **Blocks** writes to private-key and credential files (*Sealed Secrets*), catastrophic shell commands such as deleting the filesystem root (*Deletion Is Deliberate*), a recursive scan rooted at a whole filesystem such as `find /` (*Commands Must Return* — search a specific directory instead), and any change — write, move, rename, delete — under a wiki folder declared `protected` in `geneseed-wiki.jsonc`, and writes to a check listed in the project's `.geneseed/protected-checks.txt` (*External Gate* — see [Hooks](../concepts/hooks.md)).
14
14
  - **Warns** (logged, allowed) on `.env` writes and force-push.
15
15
  - **Speed-bumps** the *first* write to `user-rules.md` or a memory file: it is refused once, naming *Persist Insight* — whether something is a standing rule or a fact to remember is your call, settled through the `rule` skill. The re-issued write goes through.
16
16
 
17
17
  It ships whole on every install, whatever [doctrine packs](../concepts/rules.md) you chose. On Claude Code and Bob the same checks are hooks, which can *ask* you instead of refusing; OpenCode's tool hook has no ask tier. Protected wiki folders are enforced only here — on the other hosts they are an instruction.
18
18
 
19
- A `permission.ask` hook lets through only a fixed set of commit/push forms on a `loop/*` branch with a running loop — anything else still asks, same as the Claude/Bob git-gate's loop/* exemption.
19
+ There is no DYNAMIC loop/* exemption on OpenCode — it declares a `permission.ask` plugin hook but never calls it, so this guard cannot let a loop's own commit through the way the Claude/Bob git-gate's `loopExempt` does. A loop's commit on OpenCode still gets the static `permission.bash` ask, every time. A push to a `loop/*` branch does not: pushing a loop branch is not pushing `main`/`master`, so [`js/hosts/settings.mjs`](../../js/README.md) wires a static `allow` for the exact refspec shapes the loop engine pushes (`git push [-u] <remote> HEAD:loop/<slug>`, and the `refs/heads/` spelling) — never `main`/`master`, and a force push, a `+refspec`, or a `--delete`/bare-`:` push to a `loop/*` branch still asks, because those keys are ordered to win.
20
20
 
21
- **When it runs.** Before every tool call (`tool.execute.before`), and before a static `permission.bash` ask is shown to you (`permission.ask`).
21
+ **When it runs.** Before every tool call (`tool.execute.before`).
22
22
 
23
23
  **What you see.** A refused tool call: `[geneseed-guard] blocked: <reason> — set GENESEED_GUARD=off to allow`. Warnings go to stderr.
24
24
 
@@ -32,6 +32,40 @@ If you moved the shim itself with `GENESEED_HOME`, set it the same way before re
32
32
 
33
33
  **Confirm.** `geneseed doctor` prints no `[shim]` line, and a new session opens with the readiness sigil and your project docs in context.
34
34
 
35
+ <!--harness:claude-->
36
+ *(Claude Code only)*
37
+
38
+ ## A Bash/Write/Edit call is refused with `onFailure is "block"`
39
+
40
+ Claude Code refuses a `Bash`, `PowerShell`, `Write`, `Edit` or `NotebookEdit` call outright, and the transcript carries wording like:
41
+
42
+ > `PreToolUse:Bash hook error: [...]: failed; blocking because onFailure is "block"`
43
+
44
+ (or `... timed out; blocking because onFailure is "block"`) — not the usual "nothing is enforced" silence, but every matching tool call refused, one after another.
45
+
46
+ **Cause.** Claude Code's two gates, `git-gate` and `rule-gate`, carry `onFailure: "block"` (Claude Code v2.1.295+, required): when the gate itself cannot run at all — a dead hook shim, a moved checkout, a crash — the tool call is now refused instead of silently let through. This is deliberate (a crashing gate failing open is worse), but it turns the usual "Hooks stopped firing" symptom above into a hard stop for exactly these two gates. The underlying cause is almost always the same stale or missing shim: `geneseed doctor`/`geneseed status`, run from **outside** the stuck session, now also catch the shim having been deleted outright (not just moved) while a live install still names it — `[shim] <path> does not exist, but <install> still runs its hooks through it` / the `gates` row reading `DEAD`.
47
+
48
+ **Fix.** Rebuild from wherever Geneseed lives now:
49
+
50
+ ```bash
51
+ geneseed rebuild-all
52
+ ```
53
+
54
+ **Recovering without leaving the blocked session.** Claude Code's bash mode — a message that starts with `!`, e.g. `! geneseed rebuild-all` — is reported (outside Anthropic's own hooks reference, which is silent on the point either way) to bypass `PreToolUse` hooks entirely, so it is worth one try before leaving the session:
55
+
56
+ ```
57
+ ! geneseed rebuild-all
58
+ ```
59
+
60
+ or `! geneseed doctor` to see the cause first. If that message is ALSO refused, that is proof the bypass does not apply to your Claude Code build — fall back to a plain terminal outside Claude Code entirely, where no hook can fire at all:
61
+
62
+ ```bash
63
+ geneseed rebuild-all # or: geneseed doctor
64
+ ```
65
+
66
+ **Confirm.** Start a **new** Claude Code session after the rebuild; a `git commit`, a `Write`/`Edit` call and `geneseed doctor` all go through again with no `onFailure` refusal.
67
+ <!--/harness-->
68
+
35
69
  ## The agent never shows the readiness sigil
36
70
 
37
71
  The first reply of a session should open with the readiness sigil (`✅` in the neutral theme) — the agent's sign that it loaded the harness.
@@ -0,0 +1,29 @@
1
+ ---
2
+ group: understand
3
+ order: 14
4
+ title: "Choices: engineering, skills and agents"
5
+ kind: "concept"
6
+ section: "Why it works this way"
7
+ description: "How Geneseed's code and tests are kept, what it offers the agent, and why."
8
+ ---
9
+ How Geneseed's own code and tests are kept, and what the harness offers the agent. Part of [Why Geneseed works this way](design-choices.md); the other parts cover [the shape of the harness](design-choices-shape.md), [gates](design-choices-gates.md), [hosts](design-choices-hosts.md), and [install and update](design-choices-install.md).
10
+
11
+ ## Engineering
12
+
13
+ - **No runtime dependencies; what is needed is vendored under `js/vendor/`.** `npm ci` and `git pull` must work on locked-down machines, and a fresh clone cannot resolve a registry package. See [ADR 0001](../adr/0001-zero-runtime-dependencies.md).
14
+ - **The generator and the gate verbs start no process; the CLI only through a declared allow-list.** Geneseed must never depend on whichever interpreter happens to be on `PATH`. See [ADR 0002](../adr/0002-no-spawn-on-the-generator-and-hook-path.md).
15
+ - **Tests state their expected values in the file, with the sentence that explains the rule.** A recorded snapshot says what came out, and nothing about why it is right. See [ADR 0003](../adr/0003-expected-values-written-out.md).
16
+ - **A test is proven by putting the bug back.** A check that was never seen to fail has never shown it holds anything. See [Extending](../extending.md).
17
+ - **CI gates on the reported test count as well as the exit code.** A test glob that matches nothing prints zero tests and exits 0. See [Extending](../extending.md).
18
+ - **Counts in the docs are computed.** A copied number drifts from the thing it counts, so pages use count tokens and the doctor flags a typed number. See [Extending](../extending.md).
19
+ - **Generated project docs are Markdown by default, AsciiDoc on request.** Projects and agents read Markdown. See [ADR 0006](../adr/0006-markdown-by-default-for-generated-docs.md).
20
+ - **The built web console, `web/dist/`, is tracked and rebuilt with every change to `web/src/`.** An install must never have to build the interface. See [Web console internals](../web-ui.md).
21
+
22
+ ## Skills and agents
23
+
24
+ - **Sharpen before you add.** Before adding a skill, merge, refine or delete one. Skills age fast, and the always-loaded list has to stay short to route well. See [Extending](../extending.md).
25
+ - **User-only skills and the aliases of old names cost no tokens and are hidden from the model.** The agent sees each skill once. See [Skills](../concepts/skills.md).
26
+ - **`--exclude-skills` filters in one place, `renderAll`.** A new host inherits the exclusion with no work of its own. See [Exclusions](../concepts/exclusions.md).
27
+ - **No skill is named like a Claude Code built-in command.** The built-in would win; the review skill is `geneseed-code-review` for this reason. See [Project context](../guides/project-context.md).
28
+ - **Descriptions carry a "Use when" trigger in the third person, never cut off and never with an XML-like tag.** The description is the only text a host routes on, so a defect there is a skill that silently never fires; the doctor checks the form (`descriptionProblems`). See [Skills](../concepts/skills.md).
29
+ - **Skills that sound alike each have one job.** `roast-me` critiques your own ask or plan, `geneseed-code-review` reviews code, `fresh-eyes` is a blind second reading, and `pipeline` relays work between agents. Clear scopes make routing precise. See [Skills](../concepts/skills.md).
@@ -0,0 +1,23 @@
1
+ ---
2
+ group: understand
3
+ order: 11
4
+ title: "Choices: gates"
5
+ kind: "concept"
6
+ section: "Why it works this way"
7
+ description: "How the hook gates decide, fail and stay out of your way, and why."
8
+ ---
9
+ The gates are the one place the harness does more than ask. Part of [Why Geneseed works this way](design-choices.md); the other parts cover [the shape of the harness](design-choices-shape.md), [hosts](design-choices-hosts.md), [install and update](design-choices-install.md), and [engineering, skills and agents](design-choices-engineering.md).
10
+
11
+ - **A gate that cannot evaluate a call asks.** `guardGate` turns an unreadable payload or a bug into a prompt carrying the error, and every prompt adds a line to `notebook/gates.jsonl`. A crashed gate must never become a permissive one. See [Hook gates](../concepts/hook-gates.md) and [ADR 0007](../adr/0007-gates-fail-closed-per-host-ceiling.md).
12
+ - **On Claude Code, a gate that cannot start blocks the call.** The gate hooks carry `onFailure: "block"`, the same fail-closed rule applied by the host itself. See [ADR 0007](../adr/0007-gates-fail-closed-per-host-ceiling.md) and [Troubleshooting hooks](../reference/troubleshoot-hooks.md).
13
+ - **An argument error on a gate verb exits 0.** The hook shim is shared machine-wide, so an older `geneseed-hook` can receive a flag only a newer install knows; with `onFailure: "block"`, a refusal there would block every tool call in every session. See [ADR 0007](../adr/0007-gates-fail-closed-per-host-ceiling.md).
14
+ - **The verdict travels as JSON on stdout, the hook exits 0, and nothing else is printed.** One stray byte on stdout would turn a blocking gate silently permissive. See [ARCHITECTURE.md](../../ARCHITECTURE.md) and [SECURITY.md](../../SECURITY.md).
15
+ - **Gates stay narrow and cheap to load.** Everything the hook entry point imports is paid on every tool call of every session. See [Hook gates](../concepts/hook-gates.md).
16
+ - **One shim for the whole machine, rewritten by every build, last writer wins.** Moving a checkout and rebuilding then repairs every install at once. A checkout under the OS temp folder or in a git worktree never rewrites it (`ephemeralCheckout`), since the shim would outlive it. See [Hook gates](../concepts/hook-gates.md).
17
+ - **A global gate stands down only for a live project install that runs the same hook.** `globalHookStandingDown` checks the session's project directory for an install whose manifest records a group running the same verb, still present in its settings file. A gate goes quiet only when an equivalent gate replaces it; a doubled prompt is safe and a silenced gate is not. See [Hook gates](../concepts/hook-gates.md).
18
+ - **The destructive-git list is closed.** It covers the irreversible acts Deletion Is Deliberate names, so the gate adds no policy the law does not state. See [Hooks](../concepts/hooks.md).
19
+ - **A recursive scan rooted at a filesystem root asks on Claude Code and OpenClaude, and blocks on Bob and OpenCode.** Hosts with no ask tier must block, or the scan starts anyway. The remote half of an `ssh` command is exempt, since it runs on the remote machine. See [ADR 0010](../adr/0010-root-scan-guard.md).
20
+ - **On OpenCode, a push to a `loop/*` branch is allowed.** A loop branch is never `main` or `master`; a force push, an extra refspec or a delete still asks, and so does every commit. See [LOOP.md, trailers and the loop branch](../concepts/loop-branch.md).
21
+ - **The OpenCode guard plugin allows the call when its own code fails.** OpenCode has no ask tier, so failing closed there would lock the session with nothing to answer it. See [The guard plugin](../reference/plugin-guard.md) and [ADR 0007](../adr/0007-gates-fail-closed-per-host-ceiling.md).
22
+ - **Geneseed's hook groups are appended after yours.** A merge keeps your own `PreToolUse` entries in place and adds its groups at the end, so it never reorders hooks you wrote. See [Hook gates](../concepts/hook-gates.md).
23
+ - **`learn` runs on `Stop`, `SubagentStop` and `PreCompact`, and moving it to a session-end event was turned down.** It distils the tail of the transcript, which a session-end event does not reliably leave it. See [Hooks](../concepts/hooks.md) and [Memory](../concepts/memory.md).
@@ -0,0 +1,21 @@
1
+ ---
2
+ group: understand
3
+ order: 12
4
+ title: "Choices: hosts"
5
+ kind: "concept"
6
+ section: "Why it works this way"
7
+ description: "How Geneseed treats Claude Code, OpenClaude, OpenCode and IBM Bob, and why."
8
+ ---
9
+ How Geneseed treats each host it installs into. Part of [Why Geneseed works this way](design-choices.md); the other parts cover [the shape of the harness](design-choices-shape.md), [gates](design-choices-gates.md), [install and update](design-choices-install.md), and [engineering, skills and agents](design-choices-engineering.md).
10
+
11
+ - **Host parity: a feature reaches every host in the same change.** A host that gets a feature before the others turns into a special case on every later feature, which is how Copilot ended. See [ADR 0008](../adr/0008-host-parity-ceiling-of-the-weakest-host.md).
12
+ - **The weakest host sets the ceiling.** If Bob, the most limited host, cannot carry a feature, the feature waits as a design note. A `PostToolUse` check hook is parked for this reason: Bob's protocol gives that event no path back to the model. See [ADR 0008](../adr/0008-host-parity-ceiling-of-the-weakest-host.md).
13
+ - **OpenClaude is treated as the Claude Code release it forked from.** `claudeHookGroups` writes it no field documented only for a newer Claude Code, since the fork cannot be shown to understand one. See [ADR 0008](../adr/0008-host-parity-ceiling-of-the-weakest-host.md).
14
+ - **Bob blocks only where no judgement call is needed.** Bob has no ask tier, so the rules in `BLOCK_RULES` exit 2 and the rest become a warning. Blocking everything would leave Bob unable to commit. See [Hook gates](../concepts/hook-gates.md) and [ADR 0007](../adr/0007-gates-fail-closed-per-host-ceiling.md).
15
+ - **Bob's gate hook has no tool matcher.** Bob's tool list drifts from its own changelog, so an allow-list would let the next new write tool through unchecked. The fused `tool-gate` reads the payload's shape instead. See [the Bob adapter](../../adapters/bob/README.md).
16
+ - **On Windows, the PowerShell form of the Claude Code hooks is written only when Git Bash is absent.** `claudeHookShell` detects Git Bash by looking at files on `PATH`, without starting a process, so nothing changes on a machine that has it. Without that form, every hook would fail to parse. See [Hooks](../concepts/hooks.md).
17
+ - **OpenCode gets a one-line managed `AGENTS.md` when that file is missing.** OpenCode falls back to `~/.claude/CLAUDE.md` when `AGENTS.md` is absent, which would load the harness twice beside a Claude install. An existing `AGENTS.md` is left alone. See [Install for OpenCode](../guides/install-opencode.md).
18
+ - **Per-repo hooks go into the untracked `settings.local.json`.** The hook commands hold paths on your machine, and the team-shared `settings.json` would hand every teammate broken hooks. See [Hook gates](../concepts/hook-gates.md).
19
+ - **The manifest records which settings file holds the hooks.** Uninstall and the gate stand-down read the manifest's `settings_file` back (`settingsFile` in `js/hosts/hosts.mjs`) rather than assume a path, since the file differs by host and scope (Bob's global one is nested). See [Hook gates](../concepts/hook-gates.md).
20
+ - **Copilot was removed in one pass.** It was never verified live, its hook output never reached the model, and it shared no surface with the other hosts. See [ADR 0008](../adr/0008-host-parity-ceiling-of-the-weakest-host.md) and [Candidate hosts](../candidate-hosts.md).
21
+ - **What belongs to the host stays with the host.** Compaction, sandboxing and the plugin runtime are each host's own territory; Geneseed configures them where a host allows and builds no replacement. See [Hosts](../concepts/hosts.md).
@@ -0,0 +1,19 @@
1
+ ---
2
+ group: understand
3
+ order: 13
4
+ title: "Choices: install, uninstall, update"
5
+ kind: "concept"
6
+ section: "Why it works this way"
7
+ description: "What Geneseed writes on your machine, how it takes it back, and why."
8
+ ---
9
+ What Geneseed writes on your machine, and how it takes it back. Part of [Why Geneseed works this way](design-choices.md); the other parts cover [the shape of the harness](design-choices-shape.md), [gates](design-choices-gates.md), [hosts](design-choices-hosts.md), and [engineering, skills and agents](design-choices-engineering.md).
10
+
11
+ - **Uninstall needs no backup, by construction.** Your root file gets a delimited managed block, settings files are merged key by key, skills and agents are claimed only when Geneseed created them, and uninstall deletes only what the manifest owns. Nothing of yours is overwritten, so there is nothing to restore. See [ADR 0004](../adr/0004-uninstall-without-backups.md) and [Taking it back out](take-it-out.md).
12
+ - **A settings file with comments is refused, never rewritten.** Rewriting it would drop your comments; Geneseed prints what to add by hand instead. See [ADR 0004](../adr/0004-uninstall-without-backups.md).
13
+ - **Every emit runs its stages in one order: render, wire, prune, manifest, verify.** A render writes a whole file, so it must never follow a wire step that has just reconciled your settings into it. See [DESIGN.md](../../DESIGN.md).
14
+ - **Self-update is a fast-forward `git pull` plus a rebuild, and a dirty tree stops it.** Locked-down corporate Windows machines block scripts and archives but allow git, so git is the one route that works everywhere a clone does. See [ADR 0009](../adr/0009-self-update-is-a-git-pull.md) and [Upgrade](../guides/upgrade.md).
15
+ - **`upgrade` runs `rebuild-all`.** A bundle is a render, so a change reaches an install only when it is emitted again. See [Upgrade](../guides/upgrade.md) and [Migrate from a clone to npm](../guides/migrate.md).
16
+ - **The installers check for Node and git and never install them.** Installing a prerequisite would make Geneseed a package manager on machines where software arrives through IT; the installer names what is missing and where to get it. See [From a clone](../guides/install-clone.md).
17
+ - **A `--config-dir` preview is never registered, and the only dry run is `geneseed validate`.** A registered preview folder would be rebuilt by every `rebuild-all`, and the generator may not start the process a dry run needs. See [Verify it works](../guides/verify.md) and [Environment variables](../reference/environment.md).
18
+ - **Uninstalling one host from a shared repo hands the root marker to the host that remains.** The marker names the install, so leaving the removed host's name there would drop the other host from the registry. See [Uninstall](../guides/uninstall.md).
19
+ - **The old wiki file name, `wiki.jsonc`, is still read when `geneseed-wiki.jsonc` is absent, with no end date.** Removing the fallback would silently lose a wiki configured under the old name. See [Context and memory variables](../reference/env-context.md) and [Connect your wiki](../guides/wiki.md).
@@ -0,0 +1,21 @@
1
+ ---
2
+ group: understand
3
+ order: 10
4
+ title: "Choices: the shape of the harness"
5
+ kind: "concept"
6
+ section: "Why it works this way"
7
+ description: "How the generated text is assembled, ranked and sized, and why."
8
+ ---
9
+ How the generated harness is assembled, ranked and sized. Part of [Why Geneseed works this way](design-choices.md); the other parts cover [gates](design-choices-gates.md), [hosts](design-choices-hosts.md), [install and update](design-choices-install.md), and [engineering, skills and agents](design-choices-engineering.md).
10
+
11
+ - **Three tiers: Ethos, laws, toggleable doctrines.** The harness once shipped one flat list of laws. Splitting it lets a build make the practices optional while the core stays fixed. See [Rules](../concepts/rules.md) and [Extending](../extending.md).
12
+ - **A fixed conflict order: Ethos and laws, then your `user-rules.md`, then doctrines, then `PROFILE.md`.** Your own rules outrank any practice pack chosen at build time, and may tighten a law but never repeal one. See [Rules](../concepts/rules.md) and [DESIGN.md](../../DESIGN.md).
13
+ - **The lean footprint is the default, and its short text is written by hand.** Each law and doctrine rule carries an authored short half. Cutting the long text at its first full stop left orphan sentences an agent could not act on. See [Footprint](../concepts/footprint.md) and [Token footprint](../token-footprint.md).
14
+ - **An amendment goes into both halves of a rule.** Most installs only ever see the short half, so an amendment to the long half alone does not exist for them. See [Extending](../extending.md).
15
+ - **Rules have permanent ids and are cited by name.** A rule's number is only its position, so removing a rule rewires nothing, and a retired id is never reused. See [ADR 0005](../adr/0005-stable-rule-ids.md).
16
+ - **An always-on tier never cites a doctrine.** A build without that pack would ship a law pointing at text it does not contain; `constitutionProblems` refuses the citation. See [DESIGN.md](../../DESIGN.md).
17
+ - **Posture and mode are two separate axes, both fixed at build time.** Posture is the relationship register (peer, mentor and the rest); mode is the operating register (direct or foreman). A register declared in a prompt but not anchored in the build drifts during a session. See [Collaboration](../concepts/collaboration.md) and [Foreman mode](../concepts/foreman-mode.md).
18
+ - **Size is measured on the rendered file, against a ceiling.** The ceiling lives in `tests/unit/emit_smoke.test.mjs`, and an overrun is fixed by shortening the text. Overruns come from what is rendered, which a word count of the source does not see. See [Token footprint](../token-footprint.md).
19
+ - **Marker lines (`Excluded rules:`, `Excluded skills:`) are written only when non-empty.** A default build then stays byte-identical to one from before the option existed. See [DESIGN.md](../../DESIGN.md) and [Exclusions](../concepts/exclusions.md).
20
+ - **Session files are injected at start unless still identical to their seed, and the root file is never injected a second time.** The host already loads the root file itself; injecting it again paid for the whole harness twice per session. See [Token footprint](../token-footprint.md).
21
+ - **The context hook keeps its whole output under `OUTPUT_BUDGET`, counting each line's Windows line ending.** Claude Code moves a longer hook output to a file and shows the model a short preview, so everything past the cut is lost without a warning. Splitting the hook into two commands was measured and turned down (see [Considered and refused](../design-history.md#considered-and-refused)). See [Token footprint](../token-footprint.md).