geneseed 3.1.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/DESIGN.md +31 -20
  3. package/README.md +2 -2
  4. package/SETUP.md +30 -3
  5. package/SHIPPED.md +1 -1
  6. package/bin/{geneseed.mjs → build-driver.mjs} +88 -42
  7. package/bin/geneseed-cli.mjs +33 -32
  8. package/bin/geneseed-hook.mjs +7 -7
  9. package/docs/design-history.md +40 -40
  10. package/docs/extending.md +113 -85
  11. package/docs/web/rules.md +2 -0
  12. package/docs/web/setup-choices.md +13 -1
  13. package/harness.config.json +1 -1
  14. package/js/README.md +203 -0
  15. package/js/build/bundle.mjs +231 -0
  16. package/js/{catalog.mjs → build/catalog.mjs} +7 -7
  17. package/js/build/emit-claude.mjs +287 -0
  18. package/js/build/emit-common.mjs +255 -0
  19. package/js/build/emit-opencode.mjs +214 -0
  20. package/js/{generate.mjs → build/generate.mjs} +45 -31
  21. package/js/{render.mjs → build/render.mjs} +72 -11
  22. package/js/{checkout.mjs → build/source.mjs} +52 -11
  23. package/js/build/stubs.mjs +256 -0
  24. package/js/{themes.mjs → build/themes.mjs} +24 -24
  25. package/js/build/version.mjs +155 -0
  26. package/js/{hooks.mjs → hosts/hooks.mjs} +47 -44
  27. package/js/{hosts.mjs → hosts/hosts.mjs} +30 -28
  28. package/js/{installs.mjs → hosts/installs.mjs} +68 -16
  29. package/js/{link.mjs → hosts/link.mjs} +29 -28
  30. package/js/{mcp.mjs → hosts/mcp.mjs} +10 -9
  31. package/js/{native.mjs → hosts/native.mjs} +24 -22
  32. package/js/{opencode.mjs → hosts/opencode.mjs} +12 -7
  33. package/js/{settings.mjs → hosts/settings.mjs} +94 -69
  34. package/js/inspect/checks-authoring.mjs +761 -0
  35. package/js/inspect/checks-build.mjs +231 -0
  36. package/js/inspect/checks-repo.mjs +308 -0
  37. package/js/{diff.mjs → inspect/diff.mjs} +34 -31
  38. package/js/inspect/doctor.mjs +286 -0
  39. package/js/{excludes.mjs → inspect/excludes.mjs} +30 -33
  40. package/js/{inventory.mjs → inspect/inventory.mjs} +34 -20
  41. package/js/{registry.mjs → inspect/registry.mjs} +12 -10
  42. package/js/inspect/scan.mjs +125 -0
  43. package/js/{status.mjs → inspect/status.mjs} +27 -25
  44. package/js/inspect/validate.mjs +199 -0
  45. package/js/lib/fs.mjs +37 -759
  46. package/js/lib/json.mjs +344 -0
  47. package/js/lib/paths.mjs +242 -0
  48. package/js/lib/proc.mjs +3 -3
  49. package/js/lib/text.mjs +181 -0
  50. package/js/lib/udiff.mjs +1 -1
  51. package/js/{memory.mjs → maintain/memory.mjs} +10 -10
  52. package/js/{migrate.mjs → maintain/migrate.mjs} +28 -26
  53. package/js/{setup.mjs → maintain/setup.mjs} +49 -37
  54. package/js/{uninstall.mjs → maintain/uninstall.mjs} +62 -58
  55. package/js/{update.mjs → maintain/update.mjs} +51 -51
  56. package/js/{anim.mjs → ui/anim.mjs} +30 -28
  57. package/js/{cli.mjs → ui/cli.mjs} +10 -10
  58. package/js/{menu.mjs → ui/menu.mjs} +10 -9
  59. package/js/{tui.mjs → ui/tui.mjs} +36 -34
  60. package/js/web/actions.mjs +124 -89
  61. package/js/web/activity.mjs +22 -21
  62. package/js/web/api.mjs +88 -58
  63. package/js/web/daemon.mjs +319 -0
  64. package/js/web/docs.mjs +24 -21
  65. package/js/web/graph.mjs +0 -0
  66. package/js/web/handler.mjs +442 -0
  67. package/js/web/jobs.mjs +29 -21
  68. package/js/web/routes.mjs +223 -0
  69. package/js/web/server.mjs +56 -987
  70. package/package.json +11 -3
  71. package/src/AGENT.md.tmpl +1 -1
  72. package/src/doctrines/README.md +1 -1
  73. package/themes/opencode/README.md +1 -1
  74. package/web/dist/assets/{About-_MnJcwb_.js → About-CPvIFZGh.js} +1 -1
  75. package/web/dist/assets/{Activity-DpjqO-FR.js → Activity-z0uXdS7J.js} +1 -1
  76. package/web/dist/assets/{ActivityDetail-Bvrjv8oW.js → ActivityDetail-CEj5IJpF.js} +1 -1
  77. package/web/dist/assets/{Diff-DZ0IehBV.js → Diff-BMR8fPuG.js} +1 -1
  78. package/web/dist/assets/{Doctor-cu4krooO.js → Doctor-Bz5qE4r9.js} +1 -1
  79. package/web/dist/assets/{Graph-DUOPY65j.js → Graph-BV6a6t2l.js} +1 -1
  80. package/web/dist/assets/{Harnesses-B5Osg-BF.js → Harnesses-C5Ag5PaQ.js} +1 -1
  81. package/web/dist/assets/Laws-BK07NKw9.js +3 -0
  82. package/web/dist/assets/{Library-8y69I3_z.js → Library-CiXN9sC3.js} +1 -1
  83. package/web/dist/assets/{Markdown-Ds5GS1OD.js → Markdown-DG6FjUz-.js} +1 -1
  84. package/web/dist/assets/{Profile-7Jxa7ZUX.js → Profile-B2oSvdxH.js} +1 -1
  85. package/web/dist/assets/{Rules-UZ0jWkjg.js → Rules-DobVPd80.js} +1 -1
  86. package/web/dist/assets/{Skills-Bgkl0xLd.js → Skills-DQPsvsn9.js} +1 -1
  87. package/web/dist/assets/{StatusBadge-Cx4caF_2.js → StatusBadge-BhJ3HhdI.js} +1 -1
  88. package/web/dist/assets/{Themes-De8az7Sv.js → Themes-BP38FBU9.js} +1 -1
  89. package/web/dist/assets/{activity-VDAdVQ3q.js → activity-D9mfv7pY.js} +1 -1
  90. package/web/dist/assets/index-BE226Gv6.css +1 -0
  91. package/web/dist/assets/{index-BKrjBRV4.js → index-BZ7cPLMI.js} +1 -1
  92. package/web/dist/assets/index-BgIs5eIa.js +12 -0
  93. package/web/dist/assets/{index-wNiNzFFQ.js → index-DGM5j2hB.js} +1 -1
  94. package/web/dist/index.html +2 -2
  95. package/web/src/pages/Laws.jsx +135 -69
  96. package/docs/declined.md +0 -200
  97. package/docs/limits.md +0 -329
  98. package/js/doctor.mjs +0 -1712
  99. package/js/emit.mjs +0 -1410
  100. package/web/dist/assets/Laws-D8DJgTBp.js +0 -3
  101. package/web/dist/assets/index-1pPSz7Kl.css +0 -1
  102. package/web/dist/assets/index-C-gb-q-r.js +0 -12
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
+ ## [3.1.2] — 2026-08-24
10
+
11
+ ### Changed
12
+
13
+
14
+ - **`js/` is now domain folders, not a flat drawer.** The tree splits into `build/`, `hosts/`,
15
+ `inspect/`, and `main/`, and the four largest modules split with it: `server.mjs` into
16
+ routes / handler / daemon / server, `emit.mjs` into common / stubs / version / bundle,
17
+ `doctor.mjs` into scan plus three check groups, and `fs.mjs` into fs / json / paths / text.
18
+ The Node generator driver is renamed to `bin/build-driver.mjs`; the `geneseed-build` bin name
19
+ is unchanged, so nothing on an installed harness has to move.
20
+
21
+ **No emitted byte moves.** The 261-cell corpus replays identical across all 14 themes, which
22
+ is the whole warrant for a reorganization this size — 151 files, 8502 insertions, 6481
23
+ deletions, and zero observable difference for anyone who installed 3.1.1.
24
+
25
+ - **Docs point at the code that exists.** A module map and front door in `js/README.md`, with a
26
+ doctor arm that keeps them honest; 201 stale paths repointed in prose, the 35 split-module
27
+ pointers resolved, and 69 docblock claims corrected where the code had stopped supporting
28
+ what the comment promised. The vestigial `emit.mjs` is deleted.
29
+
30
+ - **The Python-era `py` identifier prefix is retired**, and `js/ bin/ tests/ adapters/` are now
31
+ linted — the half of the tree that never was.
32
+
33
+
34
+ - **Two of "the four things that are genuinely hard" are resolved rather than rewritten.**
35
+ `docs/extending.md` §5.2 — seven theme keys pinned byte-for-byte, so restyling a tagline was a
36
+ red gate — and §5.4 — *a snapshot red is a finding, not a step*. Theme text is free to edit,
37
+ and a red test is a step again. Editing a CLI verb's help text or flags is an ordinary edit.
38
+
39
+ - **The standing rule is now "expected values are written out, not recorded"**: change the
40
+ expectation and the sentence that explains it in the same commit, and say why in the message.
41
+
42
+ ### Added
43
+
44
+
45
+ - **A gate on how a runtime dependency may enter**: vendored under `js/vendor/<name>/` as tracked
46
+ source behind a relative import, never installed. Fixtures run from the OS temp root and cannot
47
+ resolve a bare specifier, and neither can a fresh `git clone`; devDependencies are unaffected.
48
+
49
+ ### Removed
50
+
51
+
52
+ - **The recorded corpora, and the notion of a corpus with them.** `tests/__snapshots__/` held
53
+ 2.6 MB of transcripts taken from the CPython reference before it was deleted, replayed by
54
+ `tests/snapshot/`. With no recorder left they could never be re-blessed, so a red run could
55
+ only ever mean *you changed something*, never *you broke something* — and every deliberate
56
+ change to emitted text arrived as a red gate. The 1381-cell retirement of 2026-08-17 had
57
+ already made the argument; this finishes it.
58
+
59
+ **What they owned is now written out.** `wrapText`, `dwidth` and `formatHelp` had zero unit
60
+ coverage — the recordings were their only gate — so `tests/unit/text_layout.test.mjs` states
61
+ their rules explicitly and renders all 29 verbs. The JSONC, Windows-PATH and token-report
62
+ tables moved inline; `sync_themes` became an owned fixture with plain-text expectations. The
63
+ "corpus contains what it is named for" gates went with them: when the apostrophe is visible in
64
+ the file, nothing has to assert it is there.
65
+
66
+ Recoverable at tag `corpus-reference-v3.1.2`. Nothing can regenerate them.
67
+
68
+ - **Three migration statistics.** "N of the M subcommands run from Node", "0 commands have no
69
+ Node twin", and a native-verb list duplicated across two files purely so each could police the
70
+ other. A migration percentage is not a fact about a finished tool. The durable property under
71
+ the first two — every declared verb is dispatched by an entry point — is now asserted directly.
72
+
73
+ - **`golden.mjs --against`**, which could not have worked since the emit corpus was retired, and
74
+ `orphanCheck`, `narrowingReason` and `tests/helpers/snapshot_io.mjs` with it. The three
75
+ surviving modes are all self-comparisons.
76
+
77
+
78
+ ## [3.1.1] — 2026-08-21
79
+
80
+ ### Added
81
+
82
+ - **Doctrines toggle per RULE, not per pack.** A second build-time axis,
83
+ `geneseed-build --exclude-rules "process 7"`, drops individual rules from the packs that
84
+ survive `--doctrines`. Either spelling of an address is accepted — `process 7` as the
85
+ constitution writes it, `process.7` as the console addresses it — and `none` is the explicit
86
+ empty selection.
87
+
88
+ **This supersedes the pack-level switch 3.1.0 shipped one day earlier.** That control could
89
+ only say *all of Observance or none of it*; the case it could not express — *keep the pack,
90
+ drop one rule* — is the one people actually have. The console's switches are now one per rule,
91
+ still staged locally and still applied in a single rebuild, and there is no pack switch at all:
92
+ the pack axis is DERIVED at Apply time from whichever rules survive. A pack whose every rule is
93
+ off leaves `--doctrines` entirely, so `AGENT.md` never renders a pack header with nothing under
94
+ it.
95
+
96
+ An install records its exclusions in a second marker line, `Excluded rules: process 7`,
97
+ **written only when something is excluded**. That asymmetry is load-bearing rather than tidy: a
98
+ default build is byte-identical to one from before this axis existed — 261 golden cells, nine
99
+ emit modes, both footprints, all unmoved, nothing re-blessed — and the line's absence is a
100
+ complete answer, so `excludedRulesOfDir` reads a missing line as `[]` where `doctrinesOfDir`
101
+ must read one as `null`. Every rebuild path (`upgrade`, `rebuild-all`, the console's Build and
102
+ Deploy) reads the line back and preserves it, or an upgrade would silently hand back every rule
103
+ its owner switched off.
104
+
105
+ ⚠ **The consent gate is re-keyed from the pack to the rule.** `process 5` carries commit/push
106
+ consent, and the `git-gate` hook plus the `git commit*` / `git push*` permission are now wired
107
+ on that rule rather than on its pack — so excluding it alone unwires them, and excluding
108
+ `process 7` beside it does not. Prompt and boundary are never allowed to disagree; `doctor`
109
+ gained a gate that fails if `js/settings.mjs`'s hardcoded `process.5` ever stops naming the
110
+ consent rule. `rm -rf` and force-push refusals remain Rule IV's territory and ride no axis.
111
+
112
+ ### Fixed
113
+
114
+ - **Independent doctrine controls.** Each Constitution doctrine row now keeps its detail
115
+ disclosure separate from the right-aligned switch. Toggling a staged rule no longer opens or
116
+ closes its detail, and the existing bottom **Apply** action remains the sole point at which a
117
+ selection changes the deployed harness.
118
+
9
119
  ## [3.1.0] — 2026-08-20
10
120
 
11
121
  ### Added
package/DESIGN.md CHANGED
@@ -35,7 +35,7 @@ vault or a specific tool's hooks.
35
35
  names (`ONT_TELOS`, `ONT_EVIDENCE`, `ONT_DECISIONS`, `ONT_CONDUCT`), a few rare
36
36
  technical nouns (`Context`, `Scripts`, `Charter`), and the folder names (`laws/`,
37
37
  `ontology/`, `doctrines/`, `agents/`, `skills/`, `memory/`, `notebook/` via `DIR_*`).
38
- These live in the `STRUCTURE` map in `js/render.mjs` and are laid on top of every
38
+ These live in the `STRUCTURE` map in `js/build/render.mjs` and are laid on top of every
39
39
  render, so a theme can never move a path, a link, or a heading number. No theme file
40
40
  defines a `DIR_*` or an `ONT_*` key, and none may: an ontology citation spells the
41
41
  section on both sides (`{{ONTOLOGY}}: {{ONT_TELOS}}`), so heading and reference move
@@ -102,22 +102,33 @@ vault or a specific tool's hooks.
102
102
  Rules, `I`..`IX`, headed `### {{LAW}} <roman> — {{LEX_<roman>}}`.
103
103
  - **Doctrines** (`src/doctrines/{craft,rigor,ops,process}.md`) — *how work is done
104
104
  here*. Practice rules addressed by pack and number, cited as `Doctrine process 5`.
105
- `PACK_ORDER` (`js/checkout.mjs`) fixes the order craft → rigor → ops → process, which
105
+ `PACK_ORDER` (`js/build/source.mjs`) fixes the order craft → rigor → ops → process, which
106
106
  is narrative and deliberately not alphabetical.
107
107
 
108
108
  **The Ontology and the Invariants are never toggleable** — every build carries both,
109
- whole. The Doctrine packs are the only tier a repository may narrow, chosen once at
110
- build time (`--doctrines craft,rigor`, `--doctrines none`, or the setup wizard) and
111
- defaulting to all four on. There is no runtime toggle. Every pack file ships on disk at
112
- both footprints whether or not it was built in, which is what lets a citation into an
113
- inactive pack still resolve.
109
+ whole. The Doctrine tier is the only one a repository may narrow, and it narrows on
110
+ **two axes**, both chosen once at build time and both defaulting to everything on:
111
+ `--doctrines craft,rigor` (or `none`, or the wizard) selects PACKS, and
112
+ `--exclude-rules "process 7"` drops individual RULES from the packs that survive. They
113
+ compose in one direction only — a pack whose every rule is excluded leaves the active
114
+ set, so no pack header is ever rendered empty. There is no runtime toggle for either.
115
+ Every pack file ships on disk at both footprints whether or not it was built in, which
116
+ is what lets a citation into an inactive pack still resolve.
117
+
118
+ Each axis leaves its own marker line in the carrier for later readers (`upgrade`,
119
+ `rebuild-all`, the console) to parse back: `Active packs:` always, and
120
+ `Excluded rules:` **only when something is excluded**. That asymmetry is deliberate and
121
+ load-bearing — the second line's absence is a complete answer ("nothing"), so a default
122
+ build is byte-identical to one from before the axis existed and no recording had to be
123
+ re-blessed; `excludedRulesOfDir` accordingly reads a missing line as `[]` where
124
+ `doctrinesOfDir` must read one as `null`.
114
125
 
115
126
  **Conflict order, exactly:** Ontology + Invariants → the user's own `user-rules.md` →
116
127
  the active Doctrines → `PROFILE.md`. A doctrine rule may *tighten* an invariant, never
117
128
  repeal one; a user rule outranks a doctrine rule outright; nothing outranks an
118
129
  invariant. And an always-on tier may never cite a toggleable one — a `--doctrines craft`
119
130
  build would otherwise ship an invariant pointing at text its `AGENT.md` does not
120
- contain — so `constitutionProblems` (`js/doctor.mjs`) refuses a `{{DOCTRINE}}` token
131
+ contain — so `constitutionProblems` (`js/inspect/checks-authoring.mjs`) refuses a `{{DOCTRINE}}` token
121
132
  anywhere under `src/ontology/` or `src/laws/`.
122
133
 
123
134
  The bar for a **new invariant** is the highest in the tree: universal (it binds *every*
@@ -135,9 +146,9 @@ vault or a specific tool's hooks.
135
146
  |---|---|---|---|
136
147
  | body | append to `src/laws/universal.md` — never insert; nothing resolves a cross-reference against the canon | append to `src/doctrines/<pack>.md`, `### {{DOCTRINE}} <pack> <n> — {{DOC_<PACK>_<n>}}`; ids must run contiguously from 1 | a new `src/doctrines/<pack>.md` with its `**Name** — lead line` |
137
148
  | themed title | `LEX_<roman>` in all 15 theme files | `DOC_<PACK>_<n>` in all 15 theme files | `PACK_<NAME>` in all 15, plus each rule's `DOC_*` |
138
- | class | `LAW_CLASS` in `js/inventory.mjs`, one of the six in `LAW_CLASSES` | **none** — a doctrine rule's class *is* its pack, and there is no second taxonomy over it | — |
149
+ | class | `LAW_CLASS` in `js/inspect/inventory.mjs`, one of the six in `LAW_CLASSES` | **none** — a doctrine rule's class *is* its pack, and there is no second taxonomy over it | — |
139
150
  | console copy | a `LAW_META` row in `web/src/pages/Laws.jsx` (keyed by arabic number) | a `DOCTRINE_META` row in the same file (keyed `<pack>.<n>`) | one `DOCTRINE_META` row per rule |
140
- | registration | the numeral is the registration | the number is the registration | the pack name in `PACK_ORDER`, `js/checkout.mjs` |
151
+ | registration | the numeral is the registration | the number is the registration | the pack name in `PACK_ORDER`, `js/build/source.mjs` |
141
152
  | renumber risk | high — appending is the only safe move | none — packs are numbered independently | none |
142
153
  | counts | README badge + the `N universal laws` prose, `SHIPPED.md`'s triple, the web onboarding copy | **none** — the console spends the `N_PACKS` / `N_PACKS_ACTIVE` / `N_DOCTRINE_RULES` count tokens, all computed at request time | none, same reason |
143
154
  | changelog | `CHANGELOG.md` | `CHANGELOG.md` | `CHANGELOG.md` |
@@ -149,7 +160,7 @@ vault or a specific tool's hooks.
149
160
  synonym. `--sync-themes` writes the template's placeholder and only *prints* which keys
150
161
  to restyle; a shipped placeholder passes every gate.
151
162
 
152
- Almost all of it is gated, across six named checks in `js/doctor.mjs` —
163
+ Almost all of it is gated, across six named checks in `js/inspect/checks-build.mjs` —
153
164
  `themeParityProblems` (key parity across the voices), `lawMetaProblems` and
154
165
  `doctrineMetaProblems` (the console's Principle column, in both directions),
155
166
  `constitutionProblems` (pack numbering and filing, `LEX_I..LEX_IX` as an *equality*, the
@@ -184,11 +195,11 @@ theme file defines a `DIR_*` key, so `laws/` is `laws/` in all fourteen voices.
184
195
  | Notebook | `src/notebook/` | `notebook/` | the agent's sovereign space — any medium, seed-once charter the agent may rewrite; only `.gitignore` re-asserted |
185
196
  | Posture | `src/postures/` | inlined into `AGENT.md` | how much ceremony the agent brings to a task |
186
197
  | Mode | `src/modes/` | inlined into `AGENT.md` | the working axis (solo, foreman, …) — orthogonal to posture |
187
- | Context | `js/emit.mjs` | `context.json` | empty per-repo manifest, written once and never overwritten; git-ignore it |
198
+ | Context | `js/build/stubs.mjs` | `context.json` | empty per-repo manifest, written once and never overwritten; git-ignore it |
188
199
  | Themes | `themes/*.json` | — | token → label maps; `themes/opencode/` is a separate colour system |
189
200
  | Release label | `harness.config.json` | `.geneseed-version` | the theme and the human-readable version the driver reads; the canonical identity is the source fingerprint, not this string |
190
201
  | Lifecycle | `registry.json` | — | maintainer-side status/version/owner per agent and skill; never rendered into a bundle |
191
- | Generator | `bin/geneseed.mjs` (`geneseed-build`) | — | substitution + `<!-- INCLUDE: -->` inlining, and the nine `--emit` targets |
202
+ | Generator | `bin/build-driver.mjs` (`geneseed-build`) | — | substitution + `<!-- INCLUDE: -->` inlining, and the nine `--emit` targets |
192
203
  | CLI, as data | `js/cli-table.json` | — | the owned document the entry point parses its own argv from |
193
204
  | Automation | `bin/geneseed-cli.mjs` (`geneseed`) | — | 25 verbs — `build` / `doctor` / `status` / `prompt` / `diff` / `setup` / `web` / `upgrade` / … |
194
205
  | Hooks | `bin/geneseed-hook.mjs` (`geneseed-hook`) | — | the four verbs an emitted `settings.json` invokes: `context` / `git-gate` / `rule-gate` / `learn` |
@@ -201,28 +212,28 @@ theme file defines a `DIR_*` key, so `laws/` is `laws/` in all fourteen voices.
201
212
  - Unknown tokens are left visible (debugging aid); `doctor` flags them.
202
213
  - Node built-ins only; no third-party dependencies, ever. (`package.json` carries neither a
203
214
  `dependencies` nor a `devDependencies` key, and a test asserts it.)
204
- - **`js/settings.mjs` is the half of the emit that edits files you co-own** — the
215
+ - **`js/hosts/settings.mjs` is the half of the emit that edits files you co-own** — the
205
216
  `settings.json` / `opencode.json` merges, JSONC parsing, the hook shim and the managed
206
217
  CLAUDE.md block. It is a separate module because every function in it reconciles
207
218
  Geneseed's claim with content Geneseed did not write, and because eleven of its names are
208
- driven by the *runtime* as well as by an emit (`js/mcp.mjs` uses ten of them
219
+ driven by the *runtime* as well as by an emit (`js/hosts/mcp.mjs` uses ten of them
209
220
  for deactivate, remerge, reactivate and uninstall; `exclude` and `doctor` use the rest).
210
- Its dependency closure points one way only: nothing in it calls into `js/render.mjs` or
211
- `js/emit.mjs`. Keep it that way — that closure is what makes it a unit.
221
+ Its dependency closure points one way only: nothing in it calls into `js/build/render.mjs` or
222
+ `js/build/bundle.mjs`. Keep it that way — that closure is what makes it a unit.
212
223
  - **Every emit runs five stages in one order: `RENDER* → WIRE* → PRUNE → MANIFEST →
213
- VERIFY`.** RENDER writes files Geneseed owns wholesale; WIRE is `js/settings.mjs`
224
+ VERIFY`.** RENDER writes files Geneseed owns wholesale; WIRE is `js/hosts/settings.mjs`
214
225
  reconciling files you co-own; PRUNE removes what the previous manifest owned and this
215
226
  emit no longer produces; MANIFEST records both; VERIFY re-reads the merge to confirm it
216
227
  stuck. WIRE must precede MANIFEST because wiring is what fills the `managed` record the
217
228
  manifest stores, and no RENDER may follow a WIRE because a render writes wholesale a file
218
229
  a wire has just reconciled. `tests/unit/emit_phase_order.test.mjs` fails the build if any of
219
230
  the nine emits drifts out of that order, or if a new file-mutating routine in
220
- `js/settings.mjs` is called from an emit without being classified. RENDER and WIRE now run
231
+ `js/hosts/settings.mjs` is called from an emit without being classified. RENDER and WIRE now run
221
232
  in the *same process* — the seam that once spawned a second runtime per emit is gone — so
222
233
  the render and wire dispatchers stay two separate functions and two separate statements on
223
234
  purpose: that shape is the only thing left for the walker to check, and a gate refuses an
224
235
  emit whose sequence has lost its WIRE.
225
- `bin/geneseed.mjs`'s `main()` is classified too, as the one stage no per-emit gate can see: it
236
+ `bin/build-driver.mjs`'s `main()` is classified too, as the one stage no per-emit gate can see: it
226
237
  writes the `.geneseed-emit` / `-footprint` / `-theme` markers and the install-registry
227
238
  record *after* every emit returns, and nothing that reconciles a file you co-own may run
228
239
  there, because there is no manifest to record the claim and no teardown able to undo it.
package/README.md CHANGED
@@ -93,7 +93,7 @@ Both launchers are thin shims over the Node CLI: they need `node` (22.3+) on `PA
93
93
 
94
94
  Node ≥ 22.3 is the entire dependency list. Every subcommand, all four hooks, every web-console endpoint and both generators run from it, and every commit runs the whole unit suite plus `doctor --all` across every theme × host × footprint, on Linux and Windows both. So an install needs no second interpreter — not for the harness, and not for anything it ships. (Until 2026-08-17 the generator's output was also replayed byte for byte against recordings taken from the Python implementation. Those were retired — see [`docs/limits.md`](docs/limits.md) for the measurement that licensed it and the coverage it cost.)
95
95
 
96
- - **0 commands have no Node twin**: 25 of the 25 subcommands, and all four hook verbs, answer from the Node entry points. `catalog`, `mcp` and `memory` are Node-native faces the CLI table declares on top of that roster. There is no full-screen browse panel — `geneseed tui` and `geneseed menu` are verbs that refuse it by name and print the command list instead, off a terminal as well as on one.
96
+ - **One runtime, no second one to install**: every command and all four hook verbs answer from the Node entry points. `catalog`, `mcp` and `memory` are console-facing verbs the CLI table declares alongside them. There is no full-screen browse panel — `geneseed tui` and `geneseed menu` are verbs that refuse it by name and print the command list instead, off a terminal as well as on one.
97
97
  - **Nothing you install needs an interpreter.** The `token-report` skill is a script rather than prose, and it ships as `scripts/token_report.mjs`, run with `node`; `daydream` and `herdr` hand their inline code to `node -e`. So **every bundle carries** nothing that needs one: no Python file, and no skill that shells out to an interpreter, on any host, theme or footprint. Three tests freeze that — one scans every tracked file for inline code handed to an interpreter, one globs the bundle source, and one replays the report script over seeded transcripts for all four hosts and compares the bytes.
98
98
  - **`upgrade` / `update` / `sync-self` / `bootstrap` are for a git checkout.** They `git pull` the install's own origin. From an npm install they stop before touching anything and name `npm install -g geneseed@latest` as the update instead.
99
99
 
@@ -154,7 +154,7 @@ The harness ships as a small set of layers, and the web console's rail is the sa
154
154
  | --- | --- |
155
155
  | **🧭 Ontology** (`ontology/`) | the mind the rules govern, in four prose sections — **Telos** (the Pact: protect the user, the truth, the agent), **Evidence** (every claim graded by how it was obtained), **Decisions** (classify and tier by reversibility, show the real forks), **Conduct** (answer what was asked, once). Always in force, never toggleable |
156
156
  | **🛡️ Rules** (`laws/`) | 9 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 |
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`), or in the setup wizard; all four pack files ship on disk either way, so a citation into a pack you left out still resolves |
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
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** |
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 |
package/SETUP.md CHANGED
@@ -100,7 +100,7 @@ hand? Pick a path below.
100
100
  > **Every `geneseed-build …` below is the generator.** `npm install -g geneseed` puts it
101
101
  > on your `PATH`; without installing anything, `npx -p geneseed geneseed-build --emit
102
102
  > opencode-global` is the same command. From a checkout it is
103
- > `node bin/geneseed.mjs` with the same flags. Every commit re-emits with it across every
103
+ > `node bin/build-driver.mjs` with the same flags. Every commit re-emits with it across every
104
104
  > theme × host × footprint combination and checks the result with `doctor --all`, on Linux
105
105
  > and Windows both. Until 2026-08-17 that output was also compared byte for byte against
106
106
  > recordings taken from the Python implementation; those were retired, and `docs/limits.md`
@@ -168,8 +168,10 @@ It wires:
168
168
  for Doctrine `process 5`. The hook inspects the command and forces an `ask` on every
169
169
  `git commit` or `git push` (even chained or `-C`-flagged), un-suppressible by a
170
170
  one-time "don't ask again". Every other Bash command is deferred to the normal
171
- permission flow. It is wired only when the **process** pack is active — build with
172
- `--doctrines` leaving `process` out and this hook is not installed at all.
171
+ permission flow. It is wired only when **Doctrine process 5 itself** is active — build
172
+ with `--doctrines` leaving `process` out, or with `--exclude-rules "process 5"`, and this
173
+ hook is not installed at all. The prompt and the boundary move together by construction:
174
+ the gate is keyed on the rule, not on its pack.
173
175
  - **SessionStart** (`startup`/`clear`) — prints `AGENT.md` and injects the project
174
176
  context (`harness context`, which auto-discovers the repo's docs);
175
177
  - **SessionStart** (`resume`) — refreshes the project context only, without re-printing
@@ -313,6 +315,31 @@ the default is all four:
313
315
  refuses one that names a pack the checkout does not ship. Order is fixed by the harness
314
316
  (craft → rigor → ops → process) regardless of the order you type.
315
317
 
318
+ ### One rule at a time
319
+
320
+ A pack is coarse: `--doctrines` can only keep all of **process** or none of it.
321
+ `--exclude-rules` is the finer axis, and it takes rule addresses:
322
+
323
+ geneseed-build --exclude-rules "process 7" # keep the pack, drop one rule
324
+ geneseed-build --exclude-rules "process 5,craft 2" # several
325
+ geneseed-build --exclude-rules none # exclude nothing (the default)
326
+
327
+ Both spellings of an address work — `process 7` as the constitution writes it, or `process.7`
328
+ as the console addresses it. The two axes compose: a pack whose every rule is excluded drops
329
+ out of `Active packs:` entirely, so you never get a pack header with nothing under it.
330
+
331
+ An install records what it excluded in a second marker line, `Excluded rules: process 7`,
332
+ written **only when something is excluded** — so a build with the default keeps every byte it
333
+ had before this axis existed. `upgrade`, `rebuild-all` and the web console all read that line
334
+ back and preserve it, exactly as they preserve the pack list.
335
+
336
+ ⚠ **`process 5` is special**: excluding it also unwires the commit/push consent hook, because
337
+ the tool boundary is keyed on that rule. That is by design — the prompt and the boundary must
338
+ never disagree — and both the console and the wizard say so before applying it.
339
+
340
+ The web console has the switches: **Constitution → Doctrines**, one per rule, staged locally
341
+ with a single **Apply** that rebuilds the install once.
342
+
316
343
  The setup wizard asks too — one *all* / *choose* gate, then a yes/no per pack with its
317
344
  one-line description — and it **pre-selects what the install already carries**, read off the
318
345
  `Active packs:` marker line in the deployed `AGENT.md`. So re-running the wizard and holding
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.1.0**.
23
+ > string. Current label: **3.1.2**.
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);