geneseed 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/README.md +22 -12
  3. package/SETUP.md +38 -7
  4. package/SHIPPED.md +6 -5
  5. package/adapters/bob/README.md +40 -10
  6. package/adapters/claude-code/README.md +5 -0
  7. package/adapters/claude-code/settings.json +8 -1
  8. package/adapters/copilot/README.md +50 -24
  9. package/adapters/opencode/README.md +5 -1
  10. package/adapters/opencode/plugins/geneseed-guard.js +40 -6
  11. package/bin/build-driver.mjs +16 -10
  12. package/bin/geneseed-cli.mjs +55 -56
  13. package/bin/geneseed-hook.mjs +21 -14
  14. package/docs/candidate-hosts.md +74 -0
  15. package/docs/extending.md +6 -5
  16. package/docs/token-footprint.md +32 -21
  17. package/docs/web-ui.md +5 -13
  18. package/harness.config.json +1 -1
  19. package/js/README.md +5 -6
  20. package/js/build/emit-claude.mjs +22 -7
  21. package/js/build/render.mjs +37 -19
  22. package/js/build/source.mjs +22 -2
  23. package/js/cli-table.json +84 -0
  24. package/js/hosts/hooks.mjs +240 -78
  25. package/js/hosts/hosts.mjs +56 -2
  26. package/js/hosts/link.mjs +1 -1
  27. package/js/hosts/mcp.mjs +3 -1
  28. package/js/hosts/native.mjs +17 -0
  29. package/js/hosts/settings.mjs +173 -5
  30. package/js/inspect/excludes.mjs +1 -1
  31. package/js/inspect/inventory.mjs +1 -1
  32. package/js/inspect/status.mjs +50 -2
  33. package/js/maintain/uninstall.mjs +38 -11
  34. package/js/maintain/update.mjs +2 -2
  35. package/js/web/actions.mjs +8 -8
  36. package/js/web/api.mjs +5 -10
  37. package/js/web/docs.mjs +9 -31
  38. package/js/web/handler.mjs +20 -30
  39. package/js/web/jobs.mjs +5 -26
  40. package/js/web/routes.mjs +18 -84
  41. package/js/web/server.mjs +3 -3
  42. package/package.json +1 -1
  43. package/registry.json +14 -0
  44. package/src/AGENT.md.tmpl +55 -16
  45. package/src/agents/explorer.md +1 -1
  46. package/src/doctrines/README.md +3 -3
  47. package/src/doctrines/rigor.md +13 -0
  48. package/src/laws/universal.md +98 -20
  49. package/src/ontology/universal.md +38 -13
  50. package/src/skills/_template.md +5 -1
  51. package/src/skills/bruno-collection-generator.md +63 -0
  52. package/src/skills/bruno-test-writer.md +62 -0
  53. package/src/skills/crash-course.md +1 -0
  54. package/src/skills/decode.md +1 -0
  55. package/src/skills/drill.md +1 -0
  56. package/src/skills/feynman.md +1 -0
  57. package/src/skills/forge-mcp.md +1 -1
  58. package/src/skills/herdr.md +1 -0
  59. package/src/skills/learning-path.md +1 -0
  60. package/src/skills/opencode-theme.md +1 -0
  61. package/src/skills/research.md +1 -1
  62. package/themes/_TEMPLATE.json +3 -0
  63. package/themes/biker.json +3 -0
  64. package/themes/commentator.json +3 -0
  65. package/themes/cyberpunk.json +3 -0
  66. package/themes/gamer.json +3 -0
  67. package/themes/imperial.json +3 -0
  68. package/themes/joker.json +3 -0
  69. package/themes/marvin.json +3 -0
  70. package/themes/mean.json +3 -0
  71. package/themes/military.json +3 -0
  72. package/themes/neutral.json +3 -0
  73. package/themes/pirate.json +3 -0
  74. package/themes/sports.json +3 -0
  75. package/themes/verstappen.json +3 -0
  76. package/themes/wizard.json +3 -0
  77. package/web/dist/assets/Activity-BfVcLrda.js +1 -0
  78. package/web/dist/assets/ActivityDetail-CQXccGPl.js +1 -0
  79. package/web/dist/assets/{CatalogRow-DqS4e5XM.js → CatalogRow-DALEQImZ.js} +1 -1
  80. package/web/dist/assets/{Diff-CIrGbiqm.js → Diff-BIjxXx46.js} +1 -1
  81. package/web/dist/assets/{Doctor-CpI53oHY.js → Doctor-CJLbgsrw.js} +1 -1
  82. package/web/dist/assets/FilterInput-CkUCk_lY.js +1 -0
  83. package/web/dist/assets/GreenhouseView-DfC5IILJ.js +2 -0
  84. package/web/dist/assets/{Harness-vRYs11kX.js → Harness-Dm5CCU-v.js} +1 -1
  85. package/web/dist/assets/JournalView-Cpu2eI4s.js +1 -0
  86. package/web/dist/assets/Laws-DDDVFuQz.js +3 -0
  87. package/web/dist/assets/Library-Dao3T9na.js +1 -0
  88. package/web/dist/assets/{Markdown-kgpg9EQM.js → Markdown-C__nuWX5.js} +1 -1
  89. package/web/dist/assets/OperatorHudView-DEpCtAjP.js +2 -0
  90. package/web/dist/assets/{Profile-CHr-PPf6.js → Profile-BlXtUlh2.js} +1 -1
  91. package/web/dist/assets/{Rules-CsC6hfqk.js → Rules-DIre6gxR.js} +1 -1
  92. package/web/dist/assets/Skills-rY13ZPhM.js +1 -0
  93. package/web/dist/assets/{StatusBadge-gRrak0nO.js → StatusBadge-wW_RS_my.js} +1 -1
  94. package/web/dist/assets/{activity-B0-FXZCj.js → activity-DpYPKNrA.js} +1 -1
  95. package/web/dist/assets/{index-DJOhOwwR.js → index-BEZvPvpF.js} +1 -1
  96. package/web/dist/assets/index-DNQE5ydy.js +10 -0
  97. package/web/dist/assets/index-DzdQjvM1.css +1 -0
  98. package/web/dist/assets/{index-Bx3I7cRW.js → index-PmQGRS1X.js} +1 -1
  99. package/web/dist/assets/jobBuckets-DfTUJUxR.js +1 -0
  100. package/web/dist/assets/lawCats-DUS9UTH4.js +1 -0
  101. package/web/dist/index.html +2 -2
  102. package/web/src/pages/Laws.jsx +82 -64
  103. package/js/web/graph.mjs +0 -0
  104. package/web/dist/assets/Activity-BTCG2gMl.js +0 -1
  105. package/web/dist/assets/ActivityDetail-julsmPDB.js +0 -1
  106. package/web/dist/assets/Graph-CS4tA7YS.js +0 -1
  107. package/web/dist/assets/Laws-BjbHJrJt.js +0 -3
  108. package/web/dist/assets/Library-CjC7BAWN.js +0 -1
  109. package/web/dist/assets/Skills-BvatJGfe.js +0 -1
  110. package/web/dist/assets/index-BnRWQbZb.js +0 -12
  111. package/web/dist/assets/index-D0f9tJlA.css +0 -1
  112. package/web/dist/assets/roman-DkYN0LX9.js +0 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,116 @@ labels in `harness.config.json`. The canonical identity of an *installed* harnes
6
6
  is the source fingerprint in `.geneseed-version` (see `geneseed version`), not this
7
7
  label. For the capability ↔ spec map, see [SHIPPED.md](SHIPPED.md).
8
8
 
9
+ ## [Unreleased]
10
+
11
+ _Nothing yet._
12
+
13
+ ## [3.3.0] — 2026-09-06
14
+
15
+ The host-gap release: every host now gets the hook surface its runtime actually
16
+ offers, in that runtime's own dialect. Bob's hooks are **unverified live** — no Bob
17
+ install existed on the authoring machine; the emit follows Bob's documented contract
18
+ and a re-emit migrates an older install's flat `settings.json` automatically.
19
+
20
+ ### Added
21
+
22
+ - **Copilot gets hooks.** The Copilot CLI reads lifecycle hooks from
23
+ `~/.copilot/settings.json` — one command per event, no matcher groups — and the global
24
+ emit now wires two: `sessionStart` runs `context` (eager project-context injection, in
25
+ Copilot's `additionalContext` envelope) and `toolCall` runs the new `tool-gate` verb, the
26
+ git and rule gates fused behind the one command the event allows. Copilot's `toolCall` has
27
+ no ask tier, so `--host copilot` makes Laws I and IV block and the two consent rules
28
+ (process 1, process 5) warn on stderr — the stance the OpenCode guard already takes. No
29
+ learn hook: Copilot's end-of-session payloads carry no transcript. A hook the user already
30
+ set on an event is left alone and the event is not claimed; uninstall removes exactly the
31
+ claimed hooks. The per-repo emit wires nothing. Hook verbs 4 → 5; `--host` on the
32
+ gates and on `context`.
33
+ - **Bob's hooks speak Bob.** Bob documents five hook events with Claude's names but its own
34
+ protocol: the global file is the nested `~/.bob/settings/settings.json`, and on `PreToolUse`
35
+ stdout is ignored — a refusal is exit code 2. The Bob emit wrote Claude's whole set to the
36
+ flat file with stdout verdicts, so its gates were permissive on every call and
37
+ `SubagentStop`/`PreCompact` named events Bob does not have (the 2026-09-04 review). The
38
+ emit now writes three groups to the nested file — `SessionStart` context (plain stdout),
39
+ one matcher-less `PreToolUse` `tool-gate`, `Stop` learn — and `--host bob` makes Laws I/IV
40
+ exit 2 with the reason on stderr while the consent rules warn and allow. A re-emit unwires
41
+ the old flat file and migrates; `geneseed mcp` targets the nested file. Unverified live.
42
+ - **Candidate hosts costed.** `docs/candidate-hosts.md` records what
43
+ Codex CLI, Cursor and Gemini CLI offer for the three surfaces Geneseed emits, from their
44
+ own docs: all three read `AGENTS.md` (Gemini via `context.fileName`) and load skills from
45
+ the shared `.agents/skills/` convention; Codex's hooks are Claude-shaped with an ask tier,
46
+ Gemini's are the Bob Shell shape, Cursor's a third verdict spelling. SETUP path D points
47
+ each tool at what it can already use.
48
+ - **The OpenCode guard writes the gate ledger.** Every block by `geneseed-guard.js` now
49
+ appends one line to the install's `notebook/gates.jsonl` — rule (`law-1`, `law-4`,
50
+ `process-1`, `wiki`) and timestamp, never the path or command — the same file and
51
+ vocabulary the Claude-family hooks write, so `geneseed status` counts the boundary's
52
+ catches on the recommended install too, which until now had no gate evidence at all.
53
+ Warnings and `GENESEED_GUARD=warn` are not recorded; an install with no `notebook/`
54
+ records nothing and creates nothing.
55
+ - **Claude Code: `learn` on PreCompact.** `learn` distils the tail of the transcript, so
56
+ the per-turn Stop hook is a sliding window over the session — and auto-compaction is the
57
+ one moment that window is summarised away for good. A `PreCompact` group now runs the same
58
+ command before the summary; the SessionStart `compact` matcher (3.2.x) re-seeds context
59
+ after it. Existing installs pick the group up on their next re-emit.
60
+
61
+ - **Two Bruno skills — `bruno-collection-generator` and `bruno-test-writer`.** Native
62
+ flat specs, written in the harness voice after the procedures Bruno published in
63
+ [bruno-agent-skills](https://github.com/bruno-collections/bruno-agent-skills): the first
64
+ builds a collection (requests, environments, request docs) from routes, an OpenAPI
65
+ snippet, curl, or an endpoint list; the second writes assertions, scripts, and chaining
66
+ for a request or collection without leaking a secret or asserting on a value the
67
+ evidence does not support. Authored rather than vendored because the upstream folders
68
+ carry no license and lean on Python helpers, and every bundle ships with no interpreter
69
+ but Node. Skill count 47 → 49.
70
+ - **The gates fail closed.** `git-gate` and `rule-gate` signal on stdout and exit 0, so a
71
+ gate that threw and a gate that found nothing were the same observation to the host: a
72
+ silent allow. Any throw — a stdin that is not JSON, a path primitive refusing a form, a
73
+ bug — now becomes a `permissionDecision: "ask"` carrying the error. A well-formed absence
74
+ (empty stdin, `{}`, a null `tool_input`) still defers, so a hand-run gate never nags.
75
+ - **Laws I and IV are gated at the boundary.** `reset --hard`, `clean -f`, `branch -D`,
76
+ `checkout --` and `push --force*` ask under Law IV; a Write or Edit carrying a
77
+ vendor-prefixed credential asks under Law I unless the target is `.env*`. Before this the
78
+ hooks enforced one doctrine rule and no law.
79
+ - **The gate ledger.** Every ask appends one JSON line — `{ts, verb, rule, cwd}` — to
80
+ `<install>/notebook/gates.jsonl`, capped at the newest 1000. The rule, never the command:
81
+ a ledger storing what Law I caught would itself be a place a secret lands. `geneseed
82
+ status` gains a `gates` row counting asks by rule, and says so when `excludes.json` has
83
+ the gates standing down for this cwd — until now that bypass had no visible surface.
84
+ - **User-only skills.** `<!-- invocation: user -->` under a skill's purpose renders
85
+ `disable-model-invocation: true` in its SKILL.md: `/name` still works, the model never
86
+ self-triggers it. Seven carry it (`crash-course`, `decode`, `drill`, `feynman`,
87
+ `learning-path`, `herdr`, `opencode-theme`). OpenCode and Copilot ignore the key.
88
+ - **SessionStart re-seeds after compaction.** Auto-compaction keeps the instruction file
89
+ but summarises away the injected `context.json`, `MEMORY.md` index and `NOTEBOOK.md`.
90
+ The warm matcher is now `resume|compact`, same `context` command, AGENT.md not re-printed.
91
+ - **Authored LEAN blocks** (`<!-- LEAN:begin/else/end -->`) for every law, the Ontology,
92
+ the preamble, §6 Notebook and §8 Wiki, replacing first-sentence truncation. Lean root
93
+ file 29,966 → 25,614 bytes, about 1.1k tokens per session on every install. Fixes a
94
+ latent bug where `ontology/universal.md` on disk carried the condensed text; `laws/` and
95
+ `ontology/` now always render at full on disk.
96
+ - **Web:** daemon static cache keyed on mtime (a rebuild no longer needs `web restart`),
97
+ drawer focus management, skeleton loading states, a shared filter input on Laws / Skills /
98
+ Library, pollers paused while the tab is hidden, gzip computed once per asset, heavy
99
+ dashboard subviews lazy-loaded (eager chunk 80 KB → 59 KB).
100
+
101
+ ### Changed
102
+
103
+ - **Laws IX and XI retired in place.** XI folds back into III and IX becomes rigor 5; the
104
+ headings stay with a one-line retirement body so the ~166 `{{LAW}}` cross-references
105
+ still resolve. Doctrine count 23 → 24. Law count stays at 11 numerals.
106
+ - **One precedence table** at the top of AGENT.md; five restatements deleted. A new README
107
+ section, "What the harness asks of you", carries what the user owes the harness.
108
+ - Doctrine pack files are read once per process (mtime-keyed cache); the hook imports its
109
+ help text lazily, about 3 ms off every tool call.
110
+
111
+ ### Removed
112
+
113
+ - **The web Graph page**, the Lineage lens's MiniGraph card, `/api/graph` and
114
+ `js/web/graph.mjs` (~1,100 lines). The Journal lens's ConstitutionMap stays. Old
115
+ `#/graph` links land on the dashboard. The `web/src/api/` domain files merged into one
116
+ `api/index.js`; the Python-port bookkeeping (`NOT_PORTED*` sets, unreachable 501 guards)
117
+ retired with observable behaviour identical.
118
+
9
119
  ## [3.2.0] — 2026-08-27
10
120
 
11
121
  ### Added
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  [![Node >= 22.3](https://img.shields.io/badge/node-%3E%3D22.3-5fa04e)](package.json)
11
11
  [![zero dependencies](https://img.shields.io/badge/deps-zero-success)](package.json)
12
12
  [![Themes](https://img.shields.io/badge/themes-14-9cf)](themes/)
13
- [![Skills](https://img.shields.io/badge/skills-47-blueviolet)](src/skills/)
13
+ [![Skills](https://img.shields.io/badge/skills-49-blueviolet)](src/skills/)
14
14
  [![Agents](https://img.shields.io/badge/agents-17-orange)](src/agents/)
15
15
  [![Laws](https://img.shields.io/badge/laws-11-critical)](src/laws/universal.md)
16
16
  [![Plugins](https://img.shields.io/badge/plugins-7-teal)](adapters/opencode/plugins/)
@@ -156,13 +156,21 @@ The harness ships as a small set of layers, and the web console's rail is the sa
156
156
  | **🛡️ Rules** (`laws/`) | 11 universal laws the agent obeys — always in force, never toggleable: sealed-secrets, one-intent-one-act, verify-before-assert, deletion-is-deliberate, surface-failures, data-not-orders, least-privilege, root-cause, external-gate, echo-the-intent, absence-is-a-claim |
157
157
  | **📐 Doctrines** (`doctrines/`) | practice packs, chosen per install at build time: **craft** (how code is written), **rigor** (how work is proven), **ops** (how the machine is operated), **process** (how a task is run). A doctrine rule may tighten a Rule, never repeal one, and the user's own `user-rules.md` outranks it. Pick with `geneseed-build --doctrines craft,rigor` (or `none`), a single rule with `--exclude-rules "process 7"`, in the setup wizard, or through the switches on the console's Constitution page; all four pack files ship on disk either way, so a citation into a pack you left out still resolves |
158
158
  | **🤖 Agents** (17) | capability specialists: `reviewer`, `tester`, `architect`, `docs`, `security`, `explorer`, `developer` — plus a debate **council** the `council` skill convenes: `advocate`, `skeptic`, `pragmatist`, `steward`, `visionary`, `user-advocate`, `framer`, `empiricist`, `operator`, `historian` |
159
- | **🛠 Skills** (47) | repeatable workflows: brainstorm · **clarify** · plan · **codebase-design** · **domain-modeling** · **wayfinder** · **tickets** · tdd · **develop** · debug · **prototype** · refactor · **ponytail** · **forge-mcp** · geneseed-code-review · **fresh-eyes** · **review-response** · commit · **ship** · **release** · **migrate** · **git-archaeology** · **git-rescue** · repo-map · document-project · **frontend-design** · **prose** · **ingest** · **research** · **learning-path** · **gap-detector** · **feynman** · **crash-course** · **drill** · **decode** · handoff · roast-me · **council** · parallel-agents · **workflow** · **wiki** · **geneseed** · **rule** · **profile** · **opencode-theme** · **herdr** · **pipeline** |
159
+ | **🛠 Skills** (49) | repeatable workflows: brainstorm · **clarify** · plan · **codebase-design** · **domain-modeling** · **wayfinder** · **tickets** · tdd · **develop** · debug · **prototype** · refactor · **ponytail** · **forge-mcp** · **bruno-collection-generator** · **bruno-test-writer** · geneseed-code-review · **fresh-eyes** · **review-response** · commit · **ship** · **release** · **migrate** · **git-archaeology** · **git-rescue** · repo-map · document-project · **frontend-design** · **prose** · **ingest** · **research** · **learning-path** · **gap-detector** · **feynman** · **crash-course** · **drill** · **decode** · handoff · roast-me · **council** · parallel-agents · **workflow** · **wiki** · **geneseed** · **rule** · **profile** · **opencode-theme** · **herdr** · **pipeline** |
160
160
  | **🔌 Plugins** (OpenCode) | `geneseed-context` injects project docs *and your machine wiki* every session (and across compaction); `geneseed-learn` distils memory at session end; `geneseed-guard` enforces the safety Laws and protected wiki folders at the tool boundary; `geneseed-workflow` registers the `workflow` tool that runs saved orchestration scripts; `geneseed-notify` sends a native OS notification when a long run finishes; `geneseed-ponytail` holds a minimal-code mode (`/ponytail lite\|full\|ultra\|off`), opt-in, injecting the laziest-that-works ruleset every turn so it doesn't drift; `geneseed-activity` streams what each session is doing to the web console's Activity view |
161
161
  | **🧠 Memory** (`memory/`) | one-fact-per-file durable knowledge, indexed by `MEMORY.md` (git-ignored, personal) |
162
162
  | **📓 Notebook** (`notebook/`) | the agent's sovereign space — any medium (code, tools, data, notes), self-ruled via a seed-once charter, always git-ignored; only its `.gitignore` is build-asserted |
163
163
  | **🌐 Wiki** (`wiki.jsonc`) | your own machine-wide knowledge base — typically an Obsidian vault — declared once per machine: entry notes load eager/lazy, the agent reads and **writes** it under the vault's own conventions, with an inbox fallback and guard-enforced protected folders |
164
164
  | **🧭 Context** | the project's own docs — auto-discovered on OpenCode, or via a `context.json` manifest |
165
165
 
166
+ ### 🤝 What the harness asks of you
167
+
168
+ The Pact in the Ontology is mutual. The agent's side is written into every install. Yours is not enforced — it is what keeps the pact honest, and only you can give it:
169
+
170
+ - **Don't punish candour.** When the agent contradicts you with evidence, flags a risk, or admits a doubt, that is the pact working, not defiance. Meeting it with penalty teaches the agent to flatter instead.
171
+ - **Give the context, don't withhold it.** The agent cannot weigh what it is not told. Front-load the constraint rather than fault its absence after the fact.
172
+ - **Decide when shown a fork.** When the agent lays out real alternatives, choose — an unmade decision stalls the work as surely as a wrong one.
173
+
166
174
  ### 🎨 Themes
167
175
 
168
176
  Fourteen themes ship — each a single JSON file in `themes/` carrying voice tokens only, so adding your own is a copy-and-edit away.
@@ -187,12 +195,12 @@ A second per-install dial, **footprint**, sets how much of the constitution `AGE
187
195
 
188
196
  | Footprint | The Ontology + Sections 1–2 of `AGENT.md` | Trade-off |
189
197
  | --- | --- | --- |
190
- | **lean** *(default)* | the Ontology **whole**; each Rule and each active doctrine rule as its heading + the rule line, then a pointer to the full text | lighter every turn; rationale is one on-demand read away |
198
+ | **lean** *(default)* | the Ontology and each Rule in their **authored** short form; each active doctrine rule as its heading + the rule line; then pointers to the full text | lighter every turn; rationale is one on-demand read away |
191
199
  | **full** | the Ontology, every Rule and every active doctrine rule at complete text **and** rationale, inlined | maximum guidance density; largest per-turn token cost |
192
200
 
193
- **The Ontology is never truncated.** Lean keeps only the first sentence of each `### ` block, and the four ontology sections are flowing prose rather than numbered rules — truncating them would leave four orphan sentences, so they ship whole at both footprints. The invariants and the doctrine rules *are* truncated at lean, each to its heading plus its first sentence.
201
+ **Lean is authored, not cut.** The Ontology and every Rule carry a hand-written lean form beside their full text (`LEAN` blocks in `src/`), so a Rule's lean footprint is decided by its author and not by where its first full stop happens to fall — the earlier first-sentence cut shipped Rule II as one 36-character line and dropped its stop-and-ask mechanism. Only doctrine rules are still machine-cut to their heading plus first sentence.
194
202
 
195
- Both footprints put the full text on disk beside `AGENT.md`: `laws/`, `ontology/` and `doctrines/` all ship in the bundle, and **all four pack files ship whether or not the pack was built in** — which is what lets a citation into an inactive pack resolve. So lean is a context/token optimization, **not** a rules cut. Measured on `neutral`, adding the ontology and doctrine tiers moved the always-loaded `AGENT.md` from 31,457 to 36,882 bytes at lean (+17.2%) and from 53,378 to 54,810 at full (+2.7%) — the lean growth is the price of shipping the ontology whole, and it was taken deliberately.
203
+ Both footprints put the full text on disk beside `AGENT.md`: `laws/`, `ontology/` and `doctrines/` all ship in the bundle at **full** text whatever the footprint, and **all four pack files ship whether or not the pack was built in** — which is what lets a citation into an inactive pack resolve. So lean is a context/token optimization, **not** a rules cut.
196
204
 
197
205
  Lean is the default: the rationale is one read away and the context it frees is paid back on every turn. Switch to **full** when token cost is a non-issue or you run a smaller model, which leans harder on always-present rationale. Set it with `--footprint lean|full`, the Settings toggle, the per-harness dropdown in the Harnesses tab, or the setup wizard. It's remembered in a `.geneseed-footprint` marker and preserved across rebuilds, on every host (OpenCode, Claude Code, Bob, Copilot).
198
206
 
@@ -220,22 +228,24 @@ is **identical on every host**. What differs is how much of it the host can
220
228
  | **Agents** (capability specialists) | ✅ native | ✅ | ✅ | ✅ `.agent.md` |
221
229
  | **Skills** (byte-identical) | ✅ | ✅ | ✅ | ✅ |
222
230
  | **Memory & Notebook** | ✅ | ✅ | ✅ | ✅ |
223
- | **Context injection** | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble |
231
+ | **Context injection** | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 🪝 hook³ |
224
232
  | **Memory write-back** (learn) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble |
225
- | **Git-gate consent** (process 5) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble |
226
- | **Rule-gate consent** (process 1) | ⚙️ plugin² | 🪝 hook | 🪝 hook¹ | 📄 preamble |
233
+ | **Git-gate consent** (process 5) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ (warn) | 🪝 hook³ (warn) |
234
+ | **Rule-gate consent** (process 1) | ⚙️ plugin² | 🪝 hook | 🪝 hook¹ (warn) | 🪝 hook³ (warn) |
235
+ | **Laws I / IV at the boundary** | ⚙️ plugin (block) | 🪝 hook (ask) | 🪝 hook¹ (exit 2) | 🪝 hook³ (block) |
227
236
  | **Sovereign-repo excludes** | ⚙️ plugin | ✅ `claudeMdExcludes` | ✅ rules-shadow | ➖ none |
228
237
  | **MCP server wiring** | ✅ `mcp` | ✅ `mcpServers` | ✅ `mcpServers` | ✅ `mcp-config.json` |
229
238
  | **Colour themes** | ✅ full palette | ➖ | ➖ | ➖ |
230
239
  | **LSP · workflow runner · primary-agent · `/`-commands** | ✅ | ➖ | ➖ | ➖ |
231
240
 
232
- <sub>✅ native support · ⚙️ OpenCode plugin · 🪝 `settings.json` hook · 📄 carried by preamble prose only · ➖ no host mechanism (harness discipline still applies) · ¹ Bob honours Claude-dialect hooks best-effort — inert if unsupported, harness still holds via the preamble. · ² OpenCode's `tool.execute.before` can only allow or throw, with no "ask the user" tier, so the rule gate is a one-shot speed bump there rather than a prompt.</sub>
241
+ <sub>✅ native support · ⚙️ OpenCode plugin · 🪝 `settings.json` hook · 📄 carried by preamble prose only · ➖ no host mechanism (harness discipline still applies) · ¹ Bob's own hook contract: global hooks in `~/.bob/settings/settings.json`, Claude's event names but stdout ignored on `PreToolUse` — a refusal is **exit code 2**. So Laws I/IV exit 2, the consent rules are a stderr line, `SessionStart` context is plain stdout, `Stop` runs learn; `SubagentStop`/`PreCompact` are not Bob events and are not written. Unverified live (no Bob install on the authoring machine); the harness still holds via the rules preamble. · ² OpenCode's `tool.execute.before` can only allow or throw, with no "ask the user" tier, so the rule gate is a one-shot speed bump there rather than a prompt. · ³ Copilot's `~/.copilot/settings.json` hooks (`sessionStart`, `toolCall`) wired by the **global** emit only; `toolCall` has no ask tier either, so Laws I/IV block and the two process rules warn on stderr. No learn: Copilot's end-of-session payloads carry no transcript.</sub>
233
242
 
234
243
  **Reading the matrix.** Everything above the divider is at full parity — no host
235
244
  drops an Agent, Skill, or the memory convention. The asymmetry is entirely in
236
- *automation mechanism*: OpenCode's plugin surface and Claude/Bob's hook surface
237
- enforce a few Rules for you, where **Copilot** (no hook mechanism) enforces them
238
- through preamble discipline instead. The OpenCode-only extras (themes, LSP,
245
+ *automation mechanism*: OpenCode's plugin surface and the Claude/Bob/Copilot hook
246
+ surfaces enforce a few Rules for you, each in the tier its host offers (a prompt on
247
+ Claude Code, a hard block or a logged warning where the host has no prompt to
248
+ give), and what no host can automate rides the preamble. The OpenCode-only extras (themes, LSP,
239
249
  workflow runner, primary-agent) have no analogue on a Claude-shaped host.
240
250
 
241
251
  Per-host wiring in depth: **[OpenCode](adapters/opencode/README.md)** ·
package/SETUP.md CHANGED
@@ -174,13 +174,22 @@ It wires:
174
174
  the gate is keyed on the rule, not on its pack.
175
175
  - **SessionStart** (`startup`/`clear`) — prints `AGENT.md` and injects the project
176
176
  context (`harness context`, which auto-discovers the repo's docs);
177
- - **SessionStart** (`resume`) — refreshes the project context only, without re-printing
178
- the static `AGENT.md`;
177
+ - **SessionStart** (`resume`/`compact`) — refreshes the project context only, without
178
+ re-printing the static `AGENT.md`. `compact` matters: auto-compaction summarises away the
179
+ injected context, memory index and notebook TOC, and this re-seeds them;
180
+ - Every gate **ask** appends one line to `notebook/gates.jsonl` in the install (rule and
181
+ timestamp, never the command); `geneseed status` counts them by rule and says when an
182
+ `excludes.json` entry has the gates standing down for the current directory. A defer
183
+ writes nothing. The OpenCode guard plugin writes the same file on every **block**, so
184
+ the count spans every host.
179
185
  - **Stop** — runs `harness learn` to capture durable memory. Opt in by setting
180
186
  `GENESEED_LLM` (e.g. `claude -p`); unset, it's a harmless no-op. Geneseed never
181
187
  embeds an API key.
188
+ - **PreCompact** — runs `harness learn` once more, right before auto-compaction
189
+ summarises the transcript tail that `learn` distils from. Memory is captured before
190
+ the summary; the SessionStart `compact` matcher re-seeds context after it.
182
191
 
183
- All four hooks are emitted as one stable path — the **hook shim** at
192
+ All the hooks are emitted as one stable path — the **hook shim** at
184
193
  `~/.geneseed/bin/geneseed-hook` (`geneseed-hook.cmd` on Windows) — instead of writing
185
194
  this machine's runtime and this checkout into your `settings.json`. The shim
186
195
  carries those two volatile paths itself, so moving or replacing the clone no longer
@@ -229,9 +238,16 @@ the preamble rides a file Copilot auto-loads: the repo-root `AGENTS.md` (CLI, co
229
238
  agent, and VS Code agent mode) or the personal `~/.copilot/copilot-instructions.md`
230
239
  (CLI). Two differences from the Claude paths:
231
240
 
232
- - **No hooks.** Copilot has no settings.json or hook mechanism, so there is no
233
- eager context injection and no `harness learn` Stop-hook — the memory convention
234
- still applies, driven by the preamble's instructions alone.
241
+ - **Hooks at global scope only, block-or-warn.** The global emit wires two hooks
242
+ into `~/.copilot/settings.json`: `sessionStart` (eager project-context injection,
243
+ as on Claude Code) and `toolCall` (both gates behind one command, because Copilot
244
+ takes one command per event). Copilot's `toolCall` has no "ask the user" tier, so
245
+ Laws I and IV **block** and the two consent rules (process 1, process 5) **warn**
246
+ on stderr instead of prompting. No `learn` hook — Copilot's `sessionEnd`/`agentStop`
247
+ payloads carry no transcript — so the memory convention rides the preamble's
248
+ instructions. The per-repo emit wires nothing: Copilot reads hooks from the
249
+ personal settings file only, and a machine-absolute command in a shared
250
+ `.github/` would fail on every teammate's machine.
235
251
  - **The per-repo layer lives in the shared `.github/`** (Copilot's repo config
236
252
  surface). Safe by construction: the ownership manifest + claim-on-create never
237
253
  touch files Geneseed didn't write — your workflows and same-named agents/skills
@@ -253,6 +269,21 @@ auto-loads a specific name, rename or symlink (`AGENT.md` → `AGENTS.md` / `CLA
253
269
  The rules work on agent self-discipline alone; the plugins (context, memory) are an
254
270
  OpenCode convenience.
255
271
 
272
+ Three tools on this path read more than the instructions file, by their own docs:
273
+
274
+ - **Codex CLI** auto-loads a repo-root `AGENTS.md` and `~/.codex/AGENTS.md`, and
275
+ discovers skills in `~/.codex/skills` or `.agents/skills`.
276
+ - **Cursor** auto-loads `AGENTS.md` (root and nested) and skills from `.cursor/skills`,
277
+ `.agents/skills` — and, for compatibility, `.claude/skills`, so a Claude project
278
+ install already gives Cursor the skill catalogue.
279
+ - **Gemini CLI** reads `GEMINI.md` by default; set `context.fileName` in its
280
+ `settings.json` to include `AGENTS.md`. Skills load from `.gemini/skills` or
281
+ `.agents/skills`.
282
+
283
+ Copy or symlink the bundle's `skills/` to `.agents/skills/` and all three see it. None
284
+ of these is a wired host yet — what each would take is costed in
285
+ [`docs/candidate-hosts.md`](docs/candidate-hosts.md).
286
+
256
287
  ### Path E — No runtime on the target at all
257
288
 
258
289
  A maintainer runs it once, on a machine that can run Node:
@@ -353,7 +384,7 @@ configuration you chose rather than a defect.
353
384
 
354
385
  ⚠ **Turning `process` off also removes the commit/push consent gate.** Doctrine `process 5`
355
386
  (*Consent Before Push*) is enforced twice: as prose in `AGENT.md`, and at the tool boundary —
356
- the `git-gate` PreToolUse hook on Claude Code and Bob, and the `git commit*` / `git push*`
387
+ the `PreToolUse` gate hook on Claude Code and Bob, and the `git commit*` / `git push*`
357
388
  permission entries on OpenCode. A build without the `process` pack wires neither, and
358
389
  rebuilding an install that had them takes the Claude hook back out of `settings.json`. That is
359
390
  deliberate and it is the rule the tiering rests on: **prompt and boundary must never
package/SHIPPED.md CHANGED
@@ -20,7 +20,7 @@ historical pointers, not files distributed with the repo.
20
20
  > **On version numbers.** [`harness.config.json`](harness.config.json)'s `version` is a
21
21
  > human-readable label only. The canonical identity of an installed harness is the
22
22
  > **source fingerprint** stamped in `.geneseed-version` (see `geneseed version`), not the
23
- > string. Current label: **3.2.0**.
23
+ > string. Current label: **3.3.0**.
24
24
 
25
25
  > **Spec status audit (2026-06-15).** Every dated spec authored before today was
26
26
  > verified against the source tree and confirmed implemented (code + tests + docs);
@@ -35,21 +35,22 @@ Every row below is present in the tree today; "Spec" links the design record.
35
35
  | Capability | What it is | Spec |
36
36
  | --- | --- | --- |
37
37
  | **Themed generator** | One neutral `src/` → 14 themed bundles via `geneseed-build` (zero dependencies); structure stays theme-independent | [DESIGN.md](DESIGN.md) |
38
- | **Laws / Agents / Skills / Memory / Notebook** | 11 laws, 17 agents, 47 skills, the memory convention, and the agent's sovereign notebook | [notebook](docs/specs/2026-06-11-notebook-sovereign-space.md) · [agent-own-space](docs/specs/2026-06-10-notebook-agent-own-space.md) |
39
- | **Three-tier constitution** | The governance surface splits into an always-on **Ontology** (Telos, Evidence, Decisions, Conduct — the Pact absorbed into Telos), the eleven always-on **Invariants**, and four toggleable **Doctrine** packs (craft, rigor, ops, process) addressed `<pack> <n>`. Packs are chosen at build time with `--doctrines` or the setup wizard; the whole catalogue ships on disk either way, so a citation into an inactive pack still resolves. Precedence, in order: Ontology + Invariants, then the user's `user-rules.md`, then the active Doctrines, then `PROFILE.md`. `doctor`'s `constitutionProblems` gates pack numbering, the per-rule Principle column, every `{{DOCTRINE}}` citation in `src/`, and the rule that an always-on tier may never cite a toggleable one | [three-tier-constitution](docs/specs/2026-08-19-three-tier-constitution-design.md) |
38
+ | **Laws / Agents / Skills / Memory / Notebook** | 11 laws, 17 agents, 49 skills, the memory convention, and the agent's sovereign notebook | [notebook](docs/specs/2026-06-11-notebook-sovereign-space.md) · [agent-own-space](docs/specs/2026-06-10-notebook-agent-own-space.md) |
39
+ | **Three-tier constitution** | The governance surface splits into an always-on **Ontology** (Telos, Evidence, Decisions, Conduct — the Pact absorbed into Telos), the eleven always-on **Invariants** (IX and XI retired in place — folded into rigor 5 and Law III, numbers kept), and four toggleable **Doctrine** packs (craft, rigor, ops, process) addressed `<pack> <n>`. Packs are chosen at build time with `--doctrines` or the setup wizard; the whole catalogue ships on disk either way, so a citation into an inactive pack still resolves. Precedence, in order: Ontology + Invariants, then the user's `user-rules.md`, then the active Doctrines, then `PROFILE.md`. `doctor`'s `constitutionProblems` gates pack numbering, the per-rule Principle column, every `{{DOCTRINE}}` citation in `src/`, and the rule that an always-on tier may never cite a toggleable one | [three-tier-constitution](docs/specs/2026-08-19-three-tier-constitution-design.md) |
40
40
  | **`doctor`** | One check: unresolved tokens, dead/non-hermetic links, theme-key parity, authoring gates, rendered-bundle drift | [theme-aware doctor/diff](docs/specs/2026-06-07-theme-aware-doctor-diff.md) |
41
41
  | **`diff` + improvements export** | Deployed-vs-source drift report; export an `improvements.md` back-port | [improvements-export](docs/specs/2026-06-12-improvements-export.md) |
42
42
  | **`status` / `version`** | Headless dashboard + fingerprint verdict (no TUI needed) | [status-command](docs/specs/2026-06-07-status-command.md) · [version-and-uninstall](docs/specs/2026-06-07-version-and-uninstall.md) |
43
43
  | **Terminal rendering** | There is no full-screen control panel: `tui` and `menu` are verbs that say so and print the command list. The display-width and glyph layer ships on, behind `status` and the install animation | [refactor-polish](docs/specs/2026-06-07-tui-refactor-polish.md) · [modern-refresh](docs/specs/2026-06-08-tui-modern-refresh.md) · [windows-tui](docs/specs/2026-06-11-windows-tui.md) |
44
44
  | **Web console** | Local browser UI (Dashboard, Library, Graph, Docs, Specs, Changes, Doctor, Themes, Settings), auto-build, in-app docs menu | [web-ui-v2](docs/specs/2026-06-12-web-ui-v2.md) · [web-auto-build](docs/specs/2026-06-12-web-auto-build.md) · [docs-menu](docs/specs/2026-06-13-docs-menu.md) |
45
45
  | **OpenCode adapter** | Native agents/skills mapping + plugins (context, learn, guard, workflow, notify, ponytail, activity); global-emit link integrity; JSONC config target | [adapter-features](docs/specs/2026-06-07-opencode-adapter-features.md) · [global-emit](docs/specs/2026-06-07-global-emit-link-integrity.md) · [jsonc-target](docs/specs/2026-06-12-opencode-jsonc-target.md) · [ponytail](docs/specs/2026-06-16-ponytail-minimal-code-mode.md) |
46
+ | **Host hooks, per dialect** | Every host gets the hook surface its runtime offers, spoken in that runtime's own verdict dialect via `--host`: Claude Code asks (`permissionDecision`), Copilot blocks (`{"block":true}`, `sessionStart` + `toolCall` in `~/.copilot/settings.json`), Bob refuses by exit code 2 (five Claude-named events, nested `~/.bob/settings/settings.json`, no subagent event), OpenCode's guard throws. Laws I/IV are the hard tier everywhere; the consent rules ask where a host can ask and warn where it cannot. `tool-gate` fuses the two gates for hosts that run one command per event; `PreCompact` runs `learn` before Claude compacts; every block or ask lands in `notebook/gates.jsonl` on every host | [copilot](adapters/copilot/README.md) · [bob](adapters/bob/README.md) · [claude-code](adapters/claude-code/README.md) · [candidate hosts](docs/candidate-hosts.md) |
46
47
  | **Workflow primitive** | Saved, code-driven subagent orchestration (`workflow` tool + runtime); shipped scripts include `dispatch` — decompose a goal, route each subtask to its owning agent, converge | [workflow-primitive](docs/specs/2026-06-09-opencode-workflow-primitive.md) |
47
- | **Per-agent memory** | Each capability agent keeps durable lessons in `memory/agents/<name>.md`: read at dispatch, written back mechanically by `learn` on session/subagent end (OpenCode plugin + claude/bob `SubagentStop`); `learn --consolidate` rebuilds the index | [orchestration-memory-upgrade](docs/specs/2026-07-04-orchestration-memory-upgrade.md) |
48
+ | **Per-agent memory** | Each capability agent keeps durable lessons in `memory/agents/<name>.md`: read at dispatch, written back mechanically by `learn` on session/subagent end (OpenCode plugin + Claude `SubagentStop`; Bob has no subagent event); `learn --consolidate` rebuilds the index | [orchestration-memory-upgrade](docs/specs/2026-07-04-orchestration-memory-upgrade.md) |
48
49
  | **Wiki integration** | Machine-wide knowledge base (`wiki.jsonc`): eager/lazy loading, conventions, protected folders enforced at the tool boundary | [wiki-knowledge-base](docs/specs/2026-06-11-wiki-knowledge-base.md) |
49
50
  | **Collaboration layer** | Negotiated postures (peer/mentor/expert/assistant/artisan, chosen at setup, inlined in AGENT.md), the bidirectional Pact, epistemically-typed memory (`force:` + Bridge rule), and a seed-once `PROFILE.md` identity with a web editor tab and a `profile` skill that interviews the user to draft it | [collaboration-layer](docs/specs/2026-07-12-collaboration-layer.md) |
50
51
  | **Rule front door** | Both durable stores enter through the `rule` skill's fork (*standing rule, or fact to remember?* — always asked, never inferred): the memory branch elicits the binding `force` and shows the file before writing, the rule branch pressure-tests by reformulation and a mandatory counter-example. Doctrine process 1 carries the norm on every host; a `rule-gate` PreToolUse hook (Claude, Bob) and a `geneseed-guard` speed bump (OpenCode) hold it at the tool boundary | [rule-front-door](docs/specs/2026-07-30-rule-front-door.md) |
51
52
  | **Entity registry** | `registry.json` records a lifecycle status, version, owner and add-date for every agent and skill in the tree (maintainer-side — never rendered into a bundle, so it costs no session context). `doctor` gates it against `src/` in both directions, sweeps everything that ships for credentials, and holds each vendored skill folder to an immutable upstream commit. The status surfaces as a badge in the web catalog, shown only where it deviates from `approved` | [entity-registry](docs/specs/2026-07-31-entity-registry.md) |
52
- | **Node runtime + npm package** | The whole harness runs from Node: every command `js/cli-table.json` declares, all four hook verbs, every web-console endpoint (the OS-native folder picker excepted, and declined on purpose) and both generators. Every commit runs the unit suite, `doctor --all` across every theme × host × footprint, and the generator's two self-comparisons, on Linux and Windows both; the frozen emit/cli/web recordings that also gated it retired on 2026-08-17 (`docs/limits.md`). It ships as the zero-dependency `geneseed` package (`geneseed`, `geneseed-hook`, `geneseed-build`), so `npx geneseed setup` needs no clone and nothing else installed; `geneseed migrate` moves an existing checkout install across. Node is the only runtime it asks for: the `token-report` skill's own script is `token_report.mjs`, gated over seeded transcripts for all four hosts, and `daydream` and `herdr` call `node -e` | [npx-distribution](docs/specs/2026-08-06-npx-distribution.md) |
53
+ | **Node runtime + npm package** | The whole harness runs from Node: every command `js/cli-table.json` declares, all five hook verbs, every web-console endpoint (the OS-native folder picker excepted, and declined on purpose) and both generators. Every commit runs the unit suite, `doctor --all` across every theme × host × footprint, and the generator's two self-comparisons, on Linux and Windows both; the frozen emit/cli/web recordings that also gated it retired on 2026-08-17 (`docs/limits.md`). It ships as the zero-dependency `geneseed` package (`geneseed`, `geneseed-hook`, `geneseed-build`), so `npx geneseed setup` needs no clone and nothing else installed; `geneseed migrate` moves an existing checkout install across. Node is the only runtime it asks for: the `token-report` skill's own script is `token_report.mjs`, gated over seeded transcripts for all four hosts, and `daydream` and `herdr` call `node -e` | [npx-distribution](docs/specs/2026-08-06-npx-distribution.md) |
53
54
  | **Runtime awareness** | MCP discovery, web research, tool-call batching, review discipline — carried by the doctrine packs (`ops 1` tool discovery, `process 4` read the docs first, `rigor 2`/`rigor 3` honest tests and cover-and-verify), not by the invariants | [runtime-awareness](docs/specs/2026-06-07-runtime-awareness-and-review-discipline.md) |
54
55
  | **Run from anywhere / uninstall** | `link`/`unlink` onto PATH; global uninstall that keeps the memory store | [version-and-uninstall](docs/specs/2026-06-07-version-and-uninstall.md) |
55
56
 
@@ -62,16 +62,46 @@ never gets one; the `rules/geneseed.md` shadow above covers the same
62
62
  project-bypasses-global need. An older Bob install that wrote a stale exclude is
63
63
  **self-healed** (the entry is removed) on the next emit.
64
64
 
65
- ## Hooks — best-effort
66
-
67
- The `settings.json` hooks use the **Claude dialect** (a `PreToolUse:Bash`
68
- git-gate for Doctrine process 5 consent — present only when the `process` pack is
69
- built in — `SessionStart` context injection, and `Stop`/`SubagentStop` learn/memory
70
- capture, the same `geneseed-hook` subcommands the OpenCode plugins drive). Bob's hook execution is **unverified**:
71
- if Bob honours Claude-shaped hooks they fire; if not, they are inert and the
72
- harness still works fully through the `AGENTS.md`/`rules` preamble prose. This is
73
- the same behaviour-contract parity every non-OpenCode host gets — the mechanism
74
- differs, the discipline does not.
65
+ ## Hooks — Bob's own protocol
66
+
67
+ Bob documents lifecycle hooks (its hooks page and the 2.0.2 changelog; see
68
+ `docs/reviews/bob-global-injection-2026-09.md` for the reading): **five events** —
69
+ `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `Stop` — with Claude's
70
+ names but **not Claude's protocol**, and the global file at the **nested**
71
+ `~/.bob/settings/settings.json`. Two differences decide the emit:
72
+
73
+ - **stdout is read as context on `SessionStart` only.** On `PreToolUse` it is ignored
74
+ and the one way to refuse a call is **exit code 2**. Claude's `permissionDecision:
75
+ "ask"` on stdout is therefore invisible to Bob — the groups earlier versions wrote were
76
+ permissive on every call.
77
+ - **`SubagentStop` and `PreCompact` are not Bob events**, so they are not written.
78
+
79
+ What the global emit writes into `~/.bob/settings/settings.json`:
80
+
81
+ | event | verb | how the verdict travels |
82
+ | --- | --- | --- |
83
+ | `SessionStart` | `context --host bob` | plain stdout, as on Claude |
84
+ | `PreToolUse` | `tool-gate --host bob` — one group, no matcher | Laws I/IV: **exit 2**, reason on stderr; process 1/5: a stderr line, exit 0 |
85
+ | `Stop` | `learn` | never a verdict |
86
+
87
+ No matcher on `PreToolUse` because Bob's tool names are undocumented (its IDE and its
88
+ Shell differ); `tool-gate` reads the payload's shape instead — a `command` field gets
89
+ the git checks, a path gets the rule checks — and defers on anything it does not
90
+ recognise. Bob has **no "ask the user" tier**, so the two Laws that admit no judgement
91
+ call are refused outright and the two consent rules become a warning. Because that
92
+ warning is all process 5 can be here, the `process` pack toggle changes nothing in
93
+ Bob's hooks: the one group only ever exits 2 for Laws I and IV, which every build carries.
94
+
95
+ **Migration.** A re-emit over an older install unwires Geneseed's groups from the flat
96
+ `~/.bob/settings.json` (your own keys there are kept) and writes the new set to the
97
+ nested file. `geneseed mcp` targets the nested file too.
98
+
99
+ **Unverified live.** There is no Bob install on the authoring machine; this is Bob's
100
+ documented contract, with the payload field names assumed to be Claude's. If a field
101
+ name differs the gate defers rather than misfires, and the harness still holds through
102
+ the `rules/geneseed.md` preamble — the same behaviour-contract parity every
103
+ non-OpenCode host gets. `/hooks` in Bob Shell 2.0.2+ lists the hooks it loaded and
104
+ from which file; if Geneseed's are absent there, the path is wrong.
75
105
 
76
106
  ### ⚠️ Shared-repo caveat
77
107
 
@@ -51,6 +51,11 @@ absolute-path hooks instead — `geneseed setup` does that for you.) It:
51
51
  the payload does not name the subagent, the step is a silent no-op — never a
52
52
  crash, never a wrong write. This is the parity twin of the OpenCode learn plugin's
53
53
  child-session branch.
54
+ - on **PreCompact**, runs the same `harness learn` once more. `learn` distils the
55
+ *tail* of the transcript, so per-turn Stop is a sliding window over the session —
56
+ and auto-compaction is the moment that window is about to be summarised away.
57
+ This captures memory *before* the summary; the SessionStart `compact` matcher
58
+ re-seeds context *after* it.
54
59
 
55
60
  This step is **opt-in on a model CLI**: set `GENESEED_LLM` (e.g. `claude -p`,
56
61
  `llm`, `ollama run …`) for `learn` to actually distil. With it unset the hook is
@@ -23,7 +23,7 @@
23
23
  ]
24
24
  },
25
25
  {
26
- "matcher": "resume",
26
+ "matcher": "resume|compact",
27
27
  "hooks": [
28
28
  { "type": "command", "command": "node bin/geneseed-hook.mjs context" }
29
29
  ]
@@ -42,6 +42,13 @@
42
42
  { "type": "command", "command": "node bin/geneseed-hook.mjs learn || exit 0" }
43
43
  ]
44
44
  }
45
+ ],
46
+ "PreCompact": [
47
+ {
48
+ "hooks": [
49
+ { "type": "command", "command": "node bin/geneseed-hook.mjs learn || exit 0" }
50
+ ]
51
+ }
45
52
  ]
46
53
  }
47
54
  }
@@ -9,8 +9,9 @@ Agent Skills), custom agents are markdown-with-frontmatter, and the repo-root
9
9
  a `host="copilot"` flag. Nothing to install by hand: `geneseed setup` (or
10
10
  `geneseed build --emit copilot` / `--emit copilot-global`) writes everything.
11
11
 
12
- Copilot is a **deliberately reduced host** — it has no hook mechanism — so this
13
- page is mostly about *what it does not automate* and why the harness still holds.
12
+ Copilot is a **reduced host**: it has lifecycle hooks, but one command per event and
13
+ no "ask the user" tier, so this page is mostly about what those hooks *can* and
14
+ *cannot* automate and why the harness still holds where they cannot.
14
15
 
15
16
  ## What the emit writes
16
17
 
@@ -37,20 +38,45 @@ page is mostly about *what it does not automate* and why the harness still holds
37
38
  the preamble rides it as a managed block. (Unlike Bob, Copilot *has* a real
38
39
  personal instructions carrier, so no rules-folder workaround is needed.)
39
40
  - `agents/<name>.agent.md` + `skills/` under `~/.copilot`.
40
-
41
- ## No hooks — the reduced-host reality
42
-
43
- Copilot has **no `settings.json` and no lifecycle-hook mechanism**, so the whole
44
- settings/hooks/excludes stage is skipped. Concretely, Copilot does **not** get:
45
-
46
- - **automated context injection** — the project-context load falls back
47
- to the preamble instructing the agent to read the docs itself;
48
- - **automated memory / learn write-back** — no `Stop`/`SubagentStop` hook; the
49
- memory convention rides the preamble's instructions (the agent writes its own
50
- memories) rather than a session-end distiller;
51
- - **the git-gate consent backstop** — Doctrine process 5 (*consent before every commit and
52
- push*) is enforced by the **preamble prose only**, not at the tool boundary.
53
- There is no hook to force the prompt;
41
+ - **`settings.json` hooks** — two entries under `hooks`, see below.
42
+
43
+ ## Hooks — one command per event, block or warn
44
+
45
+ The Copilot CLI reads lifecycle hooks from **`~/.copilot/settings.json`** as a map of
46
+ event → one `{command}` object (no matcher groups, no list). The global emit wires
47
+ two of them, each calling the machine-wide `geneseed-hook` shim with `--host copilot`
48
+ so the verdict is spoken in Copilot's dialect:
49
+
50
+ | event | verb | what it does |
51
+ | --- | --- | --- |
52
+ | `sessionStart` | `context` | eager project-context injection, as on Claude Code, returned as `{"additionalContext": …}` |
53
+ | `toolCall` | `tool-gate` | the git gate and the rule gate **fused** — one command is all the event allows — dispatched on the payload: a `command` gets the git checks, a path gets the rule checks; the `after` phase is ignored |
54
+
55
+ Copilot's `toolCall` has **no "ask the user" tier** — its stdout contract is
56
+ `{"block": true|false}` — so the verdict tiers are:
57
+
58
+ - **Laws I and IV block.** A credential-shaped string headed for a tracked file, or
59
+ a history-discarding git act (`reset --hard`, `clean -f`, `branch -D`,
60
+ `checkout --`, `push --force`), is refused outright with the reason. Same stance
61
+ as the OpenCode guard plugin, whose `tool.execute.before` can only allow or throw.
62
+ - **Process 1 and process 5 warn.** The two consent rules are the *user's* calls; a
63
+ hard block would make Copilot unable to commit at all. They log a `[geneseed] …`
64
+ line on stderr (which Copilot records) and allow. A gate error warns the same way
65
+ rather than locking every tool call behind a block nobody can clear.
66
+
67
+ **A hook you already set on one of those events is left alone**, and that event is
68
+ not claimed — one slot per event means Geneseed could only displace it. Uninstall
69
+ removes exactly the hooks it claimed. Comments in the file mean it is never
70
+ rewritten.
71
+
72
+ **What is not wired, and why:**
73
+
74
+ - **no `learn`** — Copilot's `sessionEnd` and `agentStop` payloads carry no
75
+ `transcript_path`, so a distiller would have nothing to read. The memory
76
+ convention rides the preamble's instructions (the agent writes its own memories);
77
+ - **no hooks at project scope** — the CLI documents hooks in the personal settings
78
+ file only, and a machine-absolute command committed into a shared `.github/`
79
+ would fail on every teammate's machine;
54
80
  - **sovereign-repo excludes** — Copilot documents no exclude/shadow mechanism
55
81
  (nothing like Claude's `claudeMdExcludes` or Bob's same-named workspace rule),
56
82
  so a global install's `copilot-instructions.md` and a repo's own `AGENTS.md`
@@ -59,19 +85,19 @@ settings/hooks/excludes stage is skipped. Concretely, Copilot does **not** get:
59
85
 
60
86
  None of this is a build gap — it is Copilot's ceiling. The harness still applies
61
87
  in full: the Ontology, every Rule, every doctrine rule the install built in, and
62
- every agent and skill are present; only the *automation* of a few of them degrades
63
- to instruction-only. This is behaviour-contract parity, the same
64
- principle every non-OpenCode host follows.
88
+ every agent and skill are present; where the host offers no prompt, a Rule's
89
+ *automation* degrades to a block, a warning, or instruction-only. This is
90
+ behaviour-contract parity, the same principle every non-OpenCode host follows.
65
91
 
66
92
  ## MCP
67
93
 
68
94
  MCP servers are wired at runtime by `geneseed mcp` into `~/.copilot/mcp-config.json`
69
- (with a `tools` allowlist) — see `rituals/_harness_mcp.py`.
95
+ (with a `tools` allowlist) — see `js/hosts/mcp.mjs`.
70
96
 
71
97
  ## What Copilot does not get
72
98
 
73
99
  As a Claude-shaped host Copilot has none of the OpenCode-only extras (colour
74
- themes, JS plugins, workflow runner, primary-agent, LSP, `/`-commands), **and**
75
- — uniquely among the four hosts — none of the hook-driven automation Claude and
76
- Bob get. See the [host matrix](../../README.md#-supported-harnesses) for the full
77
- comparison.
100
+ themes, JS plugins, workflow runner, primary-agent, LSP, `/`-commands), and of the
101
+ hook-driven automation it gets context injection and the boundary gates but not
102
+ the learn distiller or a consent *prompt*. See the
103
+ [host matrix](../../README.md#-supported-harnesses) for the full comparison.
@@ -383,7 +383,11 @@ behaviour** — nothing changes the machine's current agent/model unless you opt
383
383
  files (Law I), catastrophic shell like `rm -rf /` (Law IV), and any mutation under
384
384
  a declared wiki's `protected` folders (AGENT.md §8, from `wiki.jsonc`); **warns** on
385
385
  `.env` writes and force-push. `GENESEED_GUARD=off` disables it, `=warn` downgrades
386
- blocks to warnings.
386
+ blocks to warnings. Every **block** appends one line to the install's
387
+ `notebook/gates.jsonl` (rule `law-1` / `law-4` / `process-1` / `wiki`, a timestamp,
388
+ never the path or command) — the same ledger the Claude-family hooks write, so
389
+ `geneseed status` counts what the boundary caught on every host. Warnings are not
390
+ recorded.
387
391
  - **Invisible context injection** (the default). The context plugin delivers via
388
392
  `experimental.chat.messages.transform`, so the PROJECT CONTEXT block never appears in
389
393
  the conversation and survives compaction inherently; on a build without the hook it