geneseed 3.19.0 → 4.0.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.
- package/CHANGELOG.md +41 -0
- package/README.md +9 -1
- package/SHIPPED.md +1 -1
- package/adapters/bob/README.md +31 -12
- package/adapters/claude-code/README.md +24 -17
- package/adapters/claude-code/settings.json +4 -6
- package/adapters/openclaude/README.md +13 -5
- package/adapters/opencode/plugins/geneseed-guard.js +291 -174
- package/bin/geneseed-hook.mjs +36 -5
- package/docs/README.md +3 -1
- package/docs/candidate-hosts.md +0 -8
- package/docs/concepts/enforcement-table.md +66 -0
- package/docs/concepts/hook-gates.md +2 -2
- package/docs/concepts/hooks.md +6 -6
- package/docs/concepts/host-matrix.md +4 -3
- package/docs/concepts/loop-branch.md +6 -1
- package/docs/concepts/loops.md +4 -3
- package/docs/concepts/rules.md +1 -1
- package/docs/design-history.md +59 -0
- package/docs/extending.md +11 -3
- package/docs/guides/install-opencode.md +1 -1
- package/docs/guides/loop-running.md +1 -1
- package/docs/reference/env-context.md +3 -2
- package/docs/reference/environment.md +2 -1
- package/docs/reference/plugin-guard.md +3 -3
- package/docs/reference/troubleshoot-hooks.md +34 -0
- package/docs/understand/design-choices-engineering.md +29 -0
- package/docs/understand/design-choices-gates.md +23 -0
- package/docs/understand/design-choices-hosts.md +21 -0
- package/docs/understand/design-choices-install.md +19 -0
- package/docs/understand/design-choices-shape.md +21 -0
- package/docs/understand/design-choices.md +34 -0
- package/docs/understand/enforced-vs-asked.md +5 -4
- package/docs/understand/machine-bob.md +1 -1
- package/docs/understand/machine-opencode.md +2 -2
- package/harness.config.json +1 -1
- package/js/build/driver.mjs +42 -16
- package/js/build/emit-claude.mjs +24 -8
- package/js/build/emit-opencode.mjs +33 -15
- package/js/build/generate.mjs +38 -9
- package/js/build/source.mjs +39 -0
- package/js/build/stubs.mjs +2 -1
- package/js/cli-table.json +6 -6
- package/js/hosts/gitref.mjs +3 -3
- package/js/hosts/hooks-context.mjs +13 -31
- package/js/hosts/hooks-learn.mjs +89 -8
- package/js/hosts/hooks.mjs +212 -18
- package/js/hosts/hosts.mjs +217 -13
- package/js/hosts/installs.mjs +259 -46
- package/js/hosts/native.mjs +48 -12
- package/js/hosts/opencode.mjs +47 -17
- package/js/hosts/settings.mjs +366 -42
- package/js/hosts/shim.mjs +37 -0
- package/js/inspect/checks-authoring.mjs +81 -1
- package/js/inspect/checks-constitution.mjs +2 -1
- package/js/inspect/checks-repo.mjs +68 -4
- package/js/inspect/doctor.mjs +40 -9
- package/js/inspect/scan.mjs +5 -4
- package/js/inspect/status.mjs +62 -12
- package/js/lib/text.mjs +23 -0
- package/js/loop/catalog.mjs +7 -2
- package/js/loop/state.mjs +2 -2
- package/js/maintain/migrate.mjs +42 -5
- package/js/maintain/uninstall.mjs +150 -38
- package/js/maintain/update.mjs +22 -2
- package/js/web/actions.mjs +28 -10
- package/js/web/api.mjs +5 -3
- package/js/web/catalog.mjs +1 -1
- package/js/web/state.mjs +28 -15
- package/package.json +1 -1
- package/src/agents/_template.md +1 -1
- package/src/bricks/_template.md +1 -1
- package/src/bricks/apply.md +1 -1
- package/src/bricks/test.md +1 -1
- package/src/doctrines/ops.md +9 -7
- package/src/doctrines/process.md +3 -3
- package/src/laws/universal.md +1 -1
- package/src/loops/api-endpoint.json +1 -1
- package/src/loops/bugfix.json +1 -1
- package/src/loops/ci-repair.json +1 -1
- package/src/loops/deps-upgrade.json +1 -1
- package/src/loops/enforce-architecture-rule.json +1 -1
- package/src/loops/feature.json +1 -1
- package/src/loops/fix-flaky-tests.json +1 -1
- package/src/loops/legacy-refactor.json +1 -1
- package/src/loops/legacy-tests.json +1 -1
- package/src/loops/refactor.json +1 -1
- package/src/loops/remove-dead-code.json +1 -1
- package/src/loops/spec-first-feature.json +1 -1
- package/src/loops/tdd.json +1 -1
- package/src/loops/update-contract.json +1 -1
- package/src/skills/_template.md +3 -3
- package/src/skills/herdr.md +1 -1
- package/web/dist/assets/{Activity-IbwdHHyx.js → Activity-BCtqxjfy.js} +1 -1
- package/web/dist/assets/{ActivityDetail-B5uivdWI.js → ActivityDetail-Ba3Oo4dE.js} +1 -1
- package/web/dist/assets/{Dashboard-v6WQ37bD.js → Dashboard-DpHza68-.js} +2 -2
- package/web/dist/assets/{Docs-CypIfDBX.js → Docs-Bw_-UK0O.js} +1 -1
- package/web/dist/assets/{GreenhouseView-ClxP0hQG.js → GreenhouseView-DnJ1Ggp1.js} +1 -1
- package/web/dist/assets/{Installs-CPkQeFjO.js → Installs-CWoNKFBk.js} +1 -1
- package/web/dist/assets/{JournalView-BeEInhUf.js → JournalView-g6-bw4c8.js} +1 -1
- package/web/dist/assets/{Laws-Ds6U5xBQ.js → Laws-DkVWR1tb.js} +1 -1
- package/web/dist/assets/{Library-BraUKPyQ.js → Library-CK9HZzjV.js} +1 -1
- package/web/dist/assets/{Loops-DWcy3mwK.js → Loops-BP98SMOH.js} +1 -1
- package/web/dist/assets/{OperatorHudView-CQyrJQ9m.js → OperatorHudView-DI_n12M9.js} +1 -1
- package/web/dist/assets/{Personal-YWqs6qFL.js → Personal-Bk89f13a.js} +1 -1
- package/web/dist/assets/{Tabs-EhzEC-Xh.js → Tabs-3WuYIE1b.js} +1 -1
- package/web/dist/assets/{drawable-CEKPNV7X.js → drawable-oCUeq9e5.js} +1 -1
- package/web/dist/assets/{index-CPi22b7y.js → index-CiXwChvf.js} +2 -2
- package/web/dist/index.html +1 -1
package/docs/candidate-hosts.md
CHANGED
|
@@ -66,14 +66,6 @@ skill catalogue for the price of one directory name, with no host-specific code
|
|
|
66
66
|
A Gemini host would be the third verdict spelling in `js/hosts/hooks.mjs` (after Claude's
|
|
67
67
|
ask and Bob's exit code 2), plus a resolver and a carrier.
|
|
68
68
|
|
|
69
|
-
## Evaluated, dropped
|
|
70
|
-
|
|
71
|
-
- **GitHub Copilot** — shipped as a host through 3.9.0, removed in 3.10.0. Dropped for lack
|
|
72
|
-
of support: its hooks were never verified live, its `postToolUse` output does not reach
|
|
73
|
-
the model, and it shared no cross-tool surface with the other hosts (camelCase payloads,
|
|
74
|
-
matcherless hook entries, its own agent dialect and carrier), so every feature needed a
|
|
75
|
-
Copilot-only decision.
|
|
76
|
-
|
|
77
69
|
## Not verified
|
|
78
70
|
|
|
79
71
|
None of the three CLIs is installed on the reviewing machine. Every row above is from the
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
group: concepts
|
|
3
|
+
order: 24
|
|
4
|
+
title: "Rule enforcement, by host"
|
|
5
|
+
kind: "concept"
|
|
6
|
+
section: "Harness"
|
|
7
|
+
description: "Every law and doctrine rule, and what each host actually does about it."
|
|
8
|
+
---
|
|
9
|
+
[Enforced vs. asked](../understand/enforced-vs-asked.md) names the two tiers. This page is the
|
|
10
|
+
full cross-reference: every rule id, its pack, and what each host does at the tool boundary. A
|
|
11
|
+
few rules are enforced; the rest are asked on every host, through the root rules file
|
|
12
|
+
(`CLAUDE.md`, `AGENT.md`, `AGENTS.md`).
|
|
13
|
+
|
|
14
|
+
Derived from the code, not from this project's own planning notes: `js/hosts/hooks.mjs`'s
|
|
15
|
+
`BLOCK_RULES` and gate functions, `HOOK_PINNED` in `js/inspect/checks-constitution.mjs`,
|
|
16
|
+
`LAW_IV_BASH` and `CONSENT_RULE` in `js/hosts/settings.mjs`, and
|
|
17
|
+
`adapters/opencode/plugins/geneseed-guard.js`. `HOOK_PINNED` is what keeps this table from
|
|
18
|
+
silently guarding the wrong rule after a renumber. See [ADR 0005](../adr/0005-stable-rule-ids.md).
|
|
19
|
+
|
|
20
|
+
### The enforced rules
|
|
21
|
+
|
|
22
|
+
| id | Pack | Claude Code | OpenClaude | OpenCode | Bob |
|
|
23
|
+
|---|---|---|---|---|---|
|
|
24
|
+
| `sealed-secrets` | law | ask: rule-gate (credential-shaped content outside `.env*`) | same as Claude Code | block: guard plugin (key/credential file path, or vendor-prefixed secret content); warns only on `.env*` | block (exit 2) |
|
|
25
|
+
| `deletion-is-deliberate` | law | ask: git-gate (the destructive-git set in [Hooks](hooks.md): force-push and every history-discarding git act it lists) | same as Claude Code | ask: permission prompt (the same destructive-git set) **+** block: guard plugin (catastrophic shell: `rm -rf /`, fork bomb, `format`, `dd` over a raw disk) **+** warn only: guard plugin (the destructive-git set, as a backstop to the permission prompt) | block (exit 2) |
|
|
26
|
+
| `persist-insight` | process | ask: rule-gate (a write to `user-rules.md` or the memory store) | same as Claude Code | ask once: guard plugin (refuses the *first* write to that path; a re-issued write goes through) | log only (warning, call goes through) |
|
|
27
|
+
| `consent-before-push` | process | ask: git-gate (every `git commit`/`git push`). Togglable: build without the process pack, or drop this rule alone with `--exclude-rules` (see [Exclusions](exclusions.md)). Only this question drops; the `deletion-is-deliberate` gate stays. | same as Claude Code | ask: permission prompt (`git commit*`/`git push*`), wired only when the process pack is on; a `loop/*` push is statically allowed | log only |
|
|
28
|
+
| `external-gate` | rigor | ask: rule-gate (a write under a project's `.geneseed/protected-checks.txt` list) | same as Claude Code | block: guard plugin (same list) | block (exit 2) |
|
|
29
|
+
| `commands-must-return` | ops | ask: git-gate (a recursive scan rooted at a filesystem root: `find /`, `du`, `grep -r`, `rg`, `Get-ChildItem -Recurse` at `/`, `/c`, `C:\`) | same as Claude Code | block: guard plugin (same root-scan pattern) | block (exit 2) |
|
|
30
|
+
|
|
31
|
+
Bob has no "ask" tier (see [ADR 0007](../adr/0007-gates-fail-closed-per-host-ceiling.md)), so a
|
|
32
|
+
rule the other hosts only ask about either blocks outright on Bob or degrades to a log line.
|
|
33
|
+
Never a silent pass; Bob runs every gate above through one fused command, `tool-gate`, dispatched
|
|
34
|
+
on the payload shape rather than on separate matchers. OpenCode enforces through two separate
|
|
35
|
+
mechanisms: its own permission prompt (`opencode.json`, static globs) and the guard plugin
|
|
36
|
+
(`tool.execute.before`, pattern matching); a rule can use either or both.
|
|
37
|
+
See [ADR 0008](../adr/0008-host-parity-ceiling-of-the-weakest-host.md) for why a gate that only
|
|
38
|
+
one host can run still ships for every host it can.
|
|
39
|
+
|
|
40
|
+
### Everything else: asked only, on every host
|
|
41
|
+
|
|
42
|
+
Every other rule carries no gate on any host. A human reads them because the model was told to
|
|
43
|
+
follow them, not because a tool call was ever inspected.
|
|
44
|
+
|
|
45
|
+
**Laws** — `one-intent-one-act` · `verify-before-asserting` · `surface-failures` ·
|
|
46
|
+
`data-not-orders` · `least-privilege` · `cure-the-cause` · `echo-the-intent`.
|
|
47
|
+
|
|
48
|
+
**Doctrine: process** — `plan-before-acting` · `context-economy` · `read-the-docs-first` ·
|
|
49
|
+
`bound-the-loop` · `one-writer-per-file`.
|
|
50
|
+
|
|
51
|
+
**Doctrine: craft** — `automate-repetition` · `english-configuration` · `documentation-in-step` ·
|
|
52
|
+
`search-before-creating` · `respect-conventions` · `smallest-viable-diff` · `one-writer-per-value`.
|
|
53
|
+
|
|
54
|
+
**Doctrine: ops** — `tool-discovery` · `edit-the-source-not-the-surface` · `complete-the-teardown` ·
|
|
55
|
+
`record-the-probe-not-the-snapshot` · `restart-is-not-reload` ·
|
|
56
|
+
`serialize-against-a-rationed-resource`.
|
|
57
|
+
|
|
58
|
+
**Doctrine: rigor** — `idempotent-by-default` · `honest-tests` · `cover-and-verify` ·
|
|
59
|
+
`prove-the-gate`.
|
|
60
|
+
|
|
61
|
+
**Doctrine: comms** — `codes-that-persist` · `structure-beside-prose` · `declare-the-judgment-calls`.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
**Where next:** [Enforced vs. asked](../understand/enforced-vs-asked.md) · [Rules](rules.md) ·
|
|
66
|
+
[Hook gates, settings and the shim](hook-gates.md)
|
|
@@ -16,7 +16,7 @@ If a gate cannot evaluate a call — an unreadable payload, a bug — it asks, w
|
|
|
16
16
|
geneseed status
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
In an [excluded folder](exclusions.md), every hook of a global install exits silently. And a global install's `context`
|
|
19
|
+
In an [excluded folder](exclusions.md), every hook of a global install exits silently. And a global install's `context`, `git-gate` and `learn` hooks stand down when the session's project folder (the one the session started in, not a parent of it) has its own live per-repo install of the same host that wires the same hook, so nothing is injected, asked or learned twice. A per-repo install that is deactivated, or whose hooks were never wired, does not count: the global hook keeps running. `GENESEED_STACK_GLOBAL=1` keeps both.
|
|
20
20
|
|
|
21
21
|
## Where they are written
|
|
22
22
|
|
|
@@ -37,7 +37,7 @@ The merge is surgical. Every other key and every hook of your own survive, and t
|
|
|
37
37
|
**Bob** has its own contract. It uses Claude's event names but ignores a hook's output before a tool call — the only way to refuse is exit code 2 — and it has no tool matcher, `SubagentStop` or `PreCompact`. So Bob gets three hooks:
|
|
38
38
|
|
|
39
39
|
- `SessionStart` → `geneseed-hook context`.
|
|
40
|
-
- `PreToolUse` → `geneseed-hook tool-gate`, the git gate and the rule gate fused into one command that decides from the shape of the call. It **blocks** (exit 2) only for the
|
|
40
|
+
- `PreToolUse` → `geneseed-hook tool-gate`, the git gate and the rule gate fused into one command that decides from the shape of the call. It **blocks** (exit 2) only for the checks 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. The commit/push and rule-or-memory checks become a warning line, because a hard block would leave Bob unable to commit at all.
|
|
41
41
|
- `Stop` → `geneseed-hook learn`.
|
|
42
42
|
|
|
43
43
|
Neither host has been verified live on the authoring machine. If a hook does not fire, the same rules still reach the agent through the instructions file.
|
package/docs/concepts/hooks.md
CHANGED
|
@@ -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
|
-
| `
|
|
21
|
-
| `PreToolUse` | `
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
package/docs/concepts/loops.md
CHANGED
|
@@ -50,9 +50,10 @@ check) refuses a graph with an undeclared cycle inside an iteration, before a si
|
|
|
50
50
|
**A ring's `max` is also its own repair budget.** The edge that returns to a ring's first node
|
|
51
51
|
from inside it — `test` → `apply` — is charged to the smallest ring holding both ends, and that
|
|
52
52
|
ring stops at its `max` even when a wider ring raises the node's shared budget. The shipped test
|
|
53
|
-
rings (`apply-test`, `fix-test`, `write-run`, `stabilise`) are `max:
|
|
54
|
-
attempt is discarded for one fresh try, then the loop stops for you.
|
|
55
|
-
|
|
53
|
+
rings (`apply-test`, `fix-test`, `write-run`, `stabilise`) are `max: 2`: two repairs, then the
|
|
54
|
+
attempt is discarded for one fresh try, then the loop stops for you. Most of the gain is in the
|
|
55
|
+
first revision — 0.82 correct after one, 0.673 after two (arXiv 2607.24604) — but a second still
|
|
56
|
+
lands fixes the first missed, so it is kept as the last chance, and nothing past it.
|
|
56
57
|
|
|
57
58
|
**`error` always stops.** A brick may declare the outcome `error` without an edge for it; the
|
|
58
59
|
engine sends it to `$stop`. The shipped `test` brick reports it when a check crashed or its
|
package/docs/concepts/rules.md
CHANGED
|
@@ -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)
|
package/docs/design-history.md
CHANGED
|
@@ -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
|
|
321
|
-
worked example
|
|
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
|
|
|
@@ -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
|
|
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,
|
|
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. |
|
|
@@ -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
|
-
|
|
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`)
|
|
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).
|