geneseed 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/DESIGN.md +13 -10
  3. package/README.md +4 -4
  4. package/SHIPPED.md +3 -3
  5. package/adapters/opencode/GLOBAL-HARNESS-SPEC.md +1 -1
  6. package/adapters/opencode/README.md +3 -3
  7. package/adapters/opencode/plugins/geneseed-guard.js +9 -9
  8. package/adapters/opencode/plugins/geneseed-workflow.js +1 -1
  9. package/adapters/opencode/workflows/_runtime.js +2 -2
  10. package/adapters/opencode/workflows/dispatch.js +1 -1
  11. package/docs/extending.md +72 -40
  12. package/docs/web/harnesses.md +2 -2
  13. package/docs/web/plugin-guard.md +2 -2
  14. package/docs/web/rules.md +1 -1
  15. package/docs/web/themes-author.md +2 -2
  16. package/docs/web-ui.md +1 -1
  17. package/harness.config.json +1 -1
  18. package/js/README.md +4 -3
  19. package/js/build/render.mjs +6 -3
  20. package/js/build/source.mjs +105 -1
  21. package/js/build/stubs.mjs +7 -2
  22. package/js/cli-table.json +3 -3
  23. package/js/hosts/hooks.mjs +5 -5
  24. package/js/hosts/native.mjs +1 -1
  25. package/js/inspect/checks-authoring.mjs +149 -116
  26. package/js/inspect/inventory.mjs +1 -2
  27. package/js/ui/tui.mjs +1 -1
  28. package/js/web/api.mjs +6 -1
  29. package/package.json +1 -1
  30. package/registry.json +7 -0
  31. package/src/AGENT.md.tmpl +19 -15
  32. package/src/agents/_template.md +3 -3
  33. package/src/agents/advocate.md +3 -3
  34. package/src/agents/architect.md +3 -3
  35. package/src/agents/developer.md +4 -4
  36. package/src/agents/docs.md +3 -3
  37. package/src/agents/empiricist.md +4 -4
  38. package/src/agents/explorer.md +4 -4
  39. package/src/agents/framer.md +3 -3
  40. package/src/agents/historian.md +3 -3
  41. package/src/agents/operator.md +3 -3
  42. package/src/agents/pragmatist.md +3 -3
  43. package/src/agents/researcher.md +7 -7
  44. package/src/agents/reviewer.md +3 -3
  45. package/src/agents/security.md +3 -3
  46. package/src/agents/skeptic.md +3 -3
  47. package/src/agents/steward.md +3 -3
  48. package/src/agents/tester.md +3 -3
  49. package/src/agents/user-advocate.md +3 -3
  50. package/src/agents/visionary.md +3 -3
  51. package/src/doctrines/README.md +4 -3
  52. package/src/doctrines/comms.md +4 -4
  53. package/src/doctrines/craft.md +20 -20
  54. package/src/doctrines/ops.md +19 -19
  55. package/src/doctrines/process.md +20 -20
  56. package/src/doctrines/rigor.md +22 -22
  57. package/src/laws/universal.md +23 -44
  58. package/src/memory/README.md +3 -3
  59. package/src/modes/foreman.md +1 -1
  60. package/src/notebook/README.md +1 -1
  61. package/src/ontology/universal.md +7 -9
  62. package/src/postures/artisan.md +2 -2
  63. package/src/skills/_self-improvement.md +4 -4
  64. package/src/skills/_template.md +1 -1
  65. package/src/skills/brainstorm.md +3 -3
  66. package/src/skills/bruno-collection-generator.md +6 -6
  67. package/src/skills/bruno-test-writer.md +5 -5
  68. package/src/skills/ci-fix.md +4 -4
  69. package/src/skills/commit.md +11 -10
  70. package/src/skills/consolidate-memory.md +4 -4
  71. package/src/skills/council.md +2 -2
  72. package/src/skills/debug.md +4 -4
  73. package/src/skills/deps-audit.md +3 -3
  74. package/src/skills/develop.md +7 -7
  75. package/src/skills/document-project.md +6 -6
  76. package/src/skills/explain-changes/SKILL.md +69 -0
  77. package/src/skills/explain-changes/VENDOR.md +20 -0
  78. package/src/skills/explain-changes/scripts/render_changes.mjs +558 -0
  79. package/src/skills/forge-mcp.md +4 -4
  80. package/src/skills/fresh-eyes.md +4 -4
  81. package/src/skills/frontend-design.md +1 -1
  82. package/src/skills/geneseed.md +3 -3
  83. package/src/skills/git-archaeology.md +5 -5
  84. package/src/skills/git-rescue.md +7 -7
  85. package/src/skills/handoff.md +3 -3
  86. package/src/skills/ingest.md +5 -5
  87. package/src/skills/migrate.md +8 -8
  88. package/src/skills/parallel-agents.md +2 -2
  89. package/src/skills/pipeline.md +2 -2
  90. package/src/skills/plan.md +3 -3
  91. package/src/skills/ponytail.md +2 -2
  92. package/src/skills/profile.md +2 -2
  93. package/src/skills/prose.md +1 -1
  94. package/src/skills/prototype.md +1 -1
  95. package/src/skills/refactor.md +3 -3
  96. package/src/skills/release.md +5 -5
  97. package/src/skills/repo-map.md +4 -4
  98. package/src/skills/research.md +9 -9
  99. package/src/skills/review-response.md +4 -4
  100. package/src/skills/rule.md +3 -3
  101. package/src/skills/security-audit.md +5 -5
  102. package/src/skills/ship.md +5 -5
  103. package/src/skills/skill-forge.md +4 -4
  104. package/src/skills/wiki.md +5 -5
  105. package/src/skills/workflow.md +2 -3
  106. package/src/skills/worktree.md +4 -4
  107. package/themes/_TEMPLATE.json +37 -39
  108. package/themes/biker.json +37 -39
  109. package/themes/commentator.json +37 -39
  110. package/themes/cyberpunk.json +37 -39
  111. package/themes/gamer.json +37 -39
  112. package/themes/imperial.json +37 -39
  113. package/themes/joker.json +37 -39
  114. package/themes/marvin.json +37 -39
  115. package/themes/mean.json +37 -39
  116. package/themes/military.json +37 -39
  117. package/themes/neutral.json +37 -39
  118. package/themes/pirate.json +37 -39
  119. package/themes/sports.json +37 -39
  120. package/themes/verstappen.json +37 -39
  121. package/themes/wizard.json +37 -39
  122. package/web/dist/assets/Activity-DAzV-qQj.js +1 -0
  123. package/web/dist/assets/ActivityDetail-CwEwMtKl.js +1 -0
  124. package/web/dist/assets/Dashboard-C2ELBW-X.js +2 -0
  125. package/web/dist/assets/{Docs-DrJNDPwI.js → Docs-iZrvQP8J.js} +2 -2
  126. package/web/dist/assets/FilterInput-CVyqmYWM.js +1 -0
  127. package/web/dist/assets/GreenhouseView-Bq1QmaGO.js +2 -0
  128. package/web/dist/assets/Installs-BiqR8xdO.js +2 -0
  129. package/web/dist/assets/JournalView-6wilG5Zd.js +1 -0
  130. package/web/dist/assets/Laws-D8YXD_XE.js +3 -0
  131. package/web/dist/assets/Library-DwuTZ2-L.js +1 -0
  132. package/web/dist/assets/{Markdown-BQAvN8xx.js → Markdown-CkFyfgNf.js} +14 -14
  133. package/web/dist/assets/OperatorHudView-B-HVBqfY.js +2 -0
  134. package/web/dist/assets/Personal-BbUTmZQR.js +1 -0
  135. package/web/dist/assets/Tabs-_pjUFIEI.js +1 -0
  136. package/web/dist/assets/drawable-CoIulEH2.js +1 -0
  137. package/web/dist/assets/geist-mono-latin-400-normal-DTRLJnHl.woff2 +0 -0
  138. package/web/dist/assets/geist-mono-latin-500-normal-YINYabwD.woff2 +0 -0
  139. package/web/dist/assets/index-BMwSp8BP.js +2 -0
  140. package/web/dist/assets/index-CDPDk2vj.css +1 -0
  141. package/web/dist/assets/{react-DbkvxWG5.js → react-B9Z_tXRV.js} +1 -1
  142. package/web/dist/assets/sections-GalMS0Zv.js +1 -0
  143. package/web/dist/assets/usePoll-O0sJEw1Y.js +1 -0
  144. package/web/dist/index.html +4 -4
  145. package/web/src/pages/Laws.jsx +616 -361
  146. package/web/dist/assets/Activity-CH-7cPIM.js +0 -1
  147. package/web/dist/assets/ActivityDetail-DZvz0xS5.js +0 -1
  148. package/web/dist/assets/CatalogRow-DftXpZHY.js +0 -1
  149. package/web/dist/assets/Diff-DvwdG0L9.js +0 -2
  150. package/web/dist/assets/Doctor-OTmwmYZg.js +0 -1
  151. package/web/dist/assets/FilterInput-BakJLvaV.js +0 -1
  152. package/web/dist/assets/GreenhouseView-XxrAdriX.js +0 -2
  153. package/web/dist/assets/Harness-C_LYNk5c.js +0 -1
  154. package/web/dist/assets/JournalView-BcuyeEaR.js +0 -1
  155. package/web/dist/assets/Laws-CWHrTKY4.js +0 -3
  156. package/web/dist/assets/Library-CA9IHghn.js +0 -1
  157. package/web/dist/assets/OperatorHudView-BMRY1Tt4.js +0 -2
  158. package/web/dist/assets/Profile-wceET1JY.js +0 -1
  159. package/web/dist/assets/Rules-BEXvY4Tx.js +0 -1
  160. package/web/dist/assets/Settings-DS2zF0Ez.js +0 -1
  161. package/web/dist/assets/Skills-DahlOJuI.js +0 -1
  162. package/web/dist/assets/StatusBadge-Cg6j-NJL.js +0 -1
  163. package/web/dist/assets/activity-iWaWVtsR.js +0 -1
  164. package/web/dist/assets/audiowide-latin-400-normal-6GFCX7ni.woff2 +0 -0
  165. package/web/dist/assets/drawable-BTHjTzF7.js +0 -1
  166. package/web/dist/assets/geist-mono-latin-400-normal-DKaoCDn5.woff2 +0 -0
  167. package/web/dist/assets/geist-mono-latin-500-normal-C3sF8Y1B.woff2 +0 -0
  168. package/web/dist/assets/geist-mono-latin-600-normal-BHzjB6_C.woff2 +0 -0
  169. package/web/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  170. package/web/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  171. package/web/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
  172. package/web/dist/assets/ibm-plex-serif-latin-400-normal-BIGslYFI.woff2 +0 -0
  173. package/web/dist/assets/ibm-plex-serif-latin-500-normal-REKAfBVG.woff2 +0 -0
  174. package/web/dist/assets/ibm-plex-serif-latin-600-normal-DAxitmSu.woff2 +0 -0
  175. package/web/dist/assets/index-AN5Emnrg.js +0 -2
  176. package/web/dist/assets/index-Ruh93A3A.css +0 -1
  177. package/web/dist/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  178. package/web/dist/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  179. package/web/dist/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
  180. package/web/dist/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
  181. package/web/dist/assets/lawCats-Btjp3Hk_.js +0 -1
  182. package/web/dist/assets/oswald-latin-400-normal-BAFo8sP8.woff2 +0 -0
  183. package/web/dist/assets/oswald-latin-500-normal-B-eyT0Dw.woff2 +0 -0
  184. package/web/dist/assets/oswald-latin-600-normal-plFmSr5g.woff2 +0 -0
  185. package/web/dist/assets/oswald-latin-700-normal-vy6hmKSr.woff2 +0 -0
  186. package/web/dist/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
  187. package/web/dist/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
  188. package/web/dist/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
  189. package/web/dist/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
  190. package/web/dist/assets/rolldown-runtime-CbXtAM7H.js +0 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,45 @@ label. For the capability ↔ spec map, see [SHIPPED.md](SHIPPED.md).
10
10
 
11
11
  _Nothing yet._
12
12
 
13
+ ## [3.9.0] — 2026-09-30
14
+
15
+ **Upgrading:** re-emit after installing. Laws IX and XI are gone and Echo the Intent is now
16
+ **Law IX**: a note, memory or commit that says "Law IX" or "Law XI" from before this release
17
+ means the retired External Gate (now a rigor rule) or Absence Is a Claim (folded into Verify
18
+ Before Asserting). Nothing in the harness cites a rule by number any more.
19
+
20
+ ### Changed
21
+ - **Stable rule ids.** Every law and doctrine rule is declared by a permanent id on its heading
22
+ (`### {{LAW:verify-before-asserting}} Verify Before Asserting`) and cited by it. The number is
23
+ the rule's position, computed at render time, so removing or reordering a rule rewires no
24
+ citation. Theme title keys are keyed by id too (`LEX_VERIFY_BEFORE_ASSERTING`,
25
+ `DOC_CONSENT_BEFORE_PUSH`), so a renumber never touches a theme.
26
+ - **Citations render as principle names.** Every cross-reference in AGENT.md, the laws, the
27
+ doctrines, skills and agents now reads *Verify Before Asserting* rather than "Rule III" — the
28
+ same in every voice. The hook and guard-plugin messages name the principle too. Numbers remain
29
+ only on each rule's own heading.
30
+ - The memory section of AGENT.md (always on, both footprints) tells the agent to cite a rule by
31
+ its principle, never its number, in anything that outlives the session.
32
+ - **Web console redesign (Slate + Graphite).** New Overview (health, three charts, what needs
33
+ you), Constitution, Library, Personal and Installs pages; global and per-project installs are
34
+ shown apart; skill types head the Library as a full-width banner (#162). The console's flavours
35
+ are cut to Cultivar, Operator, Matrix and Atlas (#161). The Overview view choice now lives only
36
+ in the Appearance palette, not in the page header.
37
+
38
+ ### Removed
39
+ - Laws IX (External Gate, a rigor rule since 3.x) and XI (Absence Is a Claim, folded into Law III),
40
+ both retired in place until now. Their ids are recorded in `RETIRED_RULE_IDS` and can never be
41
+ reused.
42
+
43
+ ### Added
44
+ - **`explain-changes` folder skill — a visual walkthrough of a diff before it is committed**:
45
+ the agent writes a short brief (intent, risks, logical units) and
46
+ `scripts/render_changes.mjs` renders one offline HTML page with the exact git diff side by
47
+ side, then opens it. Offered by the `commit` skill and the git-gate prompt; never a gate.
48
+ - `doctor` refuses a citation by number, an unknown id, a duplicate id, a rule heading with no id,
49
+ a retired id declared again, a console `LAW_META`/`DOCTRINE_META` row pinned to a different rule
50
+ than its number now holds, and a hook-gated rule moving away from the address its hook keys.
51
+
13
52
  ## [3.8.0] — 2026-09-30
14
53
 
15
54
  **Upgrading:** re-emit (or `geneseed rebuild-all`) after installing, so each install carries the
package/DESIGN.md CHANGED
@@ -44,7 +44,7 @@ vault or a specific tool's hooks.
44
44
  and the prose words the docs use: the core nouns `LAW(S)`/`DOCTRINE(S)`/`ONTOLOGY`/
45
45
  `AGENT(S)`/`SKILL(S)`/`MEMORY`/`NOTEBOOK`/`VAULT`/`WIKI`, plus `TAGLINE`,
46
46
  `LOADED_SIGIL`, `EPI_*`, `BENEDICTION`, `DESC_*`, `ROAST_PERSONA`, the invariant
47
- titles `LEX_I`..`LEX_XI`, the 28 doctrine-rule titles `DOC_<PACK>_<n>`, the five pack
47
+ titles `LEX_<ID>`, the 28 doctrine-rule titles `DOC_<ID>` (keyed by stable rule id), the five pack
48
48
  names `PACK_CRAFT`/`PACK_RIGOR`/`PACK_OPS`/`PACK_PROCESS`/`PACK_COMMS`, and the section intros
49
49
  `INTRO_*`. Each theme defines its own nouns; **neutral keeps the plain words** (Rule,
50
50
  Doctrine, Agent, Skill, Memory, Workspace), so neutral output is unchanged.
@@ -98,11 +98,13 @@ vault or a specific tool's hooks.
98
98
  and tier by reversibility; show the real forks), **Conduct** (answer what was asked,
99
99
  once). It absorbed the Pact, which is now stated *inside* Telos rather than standing
100
100
  as a peer of the rules.
101
- - **Invariants** (`src/laws/universal.md`) — *what is never traded*. Eleven numbered
102
- Rules, `I`..`XI`, nine in force — IX and XI are retired in place, their numbers kept so
103
- existing citations resolve — headed `### {{LAW}} <roman> — {{LEX_<roman>}}`.
101
+ - **Invariants** (`src/laws/universal.md`) — *what is never traded*. Nine Rules, `I`..`IX`,
102
+ each declared `### {{LAW:<id>}} <Principle>` with a permanent id; the numeral is its
103
+ position, computed at render time (the retired IX and XI were removed on 2026-09-30, and
104
+ Echo the Intent became IX without a single citation changing).
104
105
  - **Doctrines** (`src/doctrines/{craft,rigor,ops,process,comms}.md`) — *how work is done
105
- here*. Practice rules addressed by pack and number, cited as `Doctrine process 5`.
106
+ here*. Practice rules addressed by pack and position (`process 5`), declared and cited by
107
+ id the same way; a citation of any rule renders as its principle's name.
106
108
  `PACK_ORDER` (`js/build/source.mjs`) fixes the order craft → rigor → ops → process → comms, which
107
109
  is narrative and deliberately not alphabetical.
108
110
 
@@ -145,12 +147,12 @@ vault or a specific tool's hooks.
145
147
 
146
148
  | Satellite | A new invariant | A new doctrine rule | A whole new pack |
147
149
  |---|---|---|---|
148
- | 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` |
149
- | 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_*` |
150
+ | body | `### {{LAW:<id>}} <Principle>` in `src/laws/universal.md`; citations resolve by id, so no position rewires one | `### {{DOCTRINE:<id>}} <Principle>` in `src/doctrines/<pack>.md`; the number is its position | a new `src/doctrines/<pack>.md` with its `**Name** — lead line` |
151
+ | themed title | `LEX_<ID>` in all 15 theme files | `DOC_<ID>` in all 15 theme files | `PACK_<NAME>` in all 15, plus each rule's `DOC_*` |
150
152
  | 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 | — |
151
153
  | 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 |
152
154
  | registration | the numeral is the registration | the number is the registration | the pack name in `PACK_ORDER`, `js/build/source.mjs` |
153
- | renumber risk | high — appending is the only safe move | none — packs are numbered independently | none |
155
+ | renumber risk | none for citations (by id); `LAW_META`/`LAW_CLASS` are number-keyed and doctor-pinned, so append by preference | none for citations; `DOCTRINE_META`, exclusions and hook ledger keys are `<pack>.<n>` — append by preference | none |
154
156
  | 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 |
155
157
  | changelog | `CHANGELOG.md` | `CHANGELOG.md` | `CHANGELOG.md` |
156
158
 
@@ -164,8 +166,9 @@ vault or a specific tool's hooks.
164
166
  Almost all of it is gated, across six named checks in `js/inspect/checks-build.mjs` —
165
167
  `themeParityProblems` (key parity across the voices), `lawMetaProblems` and
166
168
  `doctrineMetaProblems` (the console's Principle column, in both directions),
167
- `constitutionProblems` (pack numbering and filing, `LEX_I..LEX_XI` as an *equality*, the
168
- `DOC_*` vocabulary, and every `{{DOCTRINE}}` citation in `src/`), and
169
+ `constitutionProblems` (rule ids declared, unique and never retired, `LEX_*` as an *equality*,
170
+ the `DOC_*` vocabulary, the hook-gated addresses, and every `{{LAW:…}}`/`{{DOCTRINE:…}}`
171
+ citation in `src/` — a citation by number is refused), and
169
172
  `countTableProblems` / `proseMirrorProblems` (badges and prose). So `geneseed doctor
170
173
  --all` is the check; the table above only says what it will ask for. `CHANGELOG.md` has
171
174
  no gate and never has.
package/README.md CHANGED
@@ -10,9 +10,9 @@
10
10
  [![Node >= 22.3](https://img.shields.io/badge/node-%3E%3D22.3-5fa04e)](package.json)
11
11
  [![zero dependencies](https://img.shields.io/badge/deps-zero-success)](package.json)
12
12
  [![Themes](https://img.shields.io/badge/themes-14-9cf)](themes/)
13
- [![Skills](https://img.shields.io/badge/skills-52-blueviolet)](src/skills/)
13
+ [![Skills](https://img.shields.io/badge/skills-53-blueviolet)](src/skills/)
14
14
  [![Agents](https://img.shields.io/badge/agents-18-orange)](src/agents/)
15
- [![Laws](https://img.shields.io/badge/laws-11-critical)](src/laws/universal.md)
15
+ [![Laws](https://img.shields.io/badge/laws-9-critical)](src/laws/universal.md)
16
16
  [![Plugins](https://img.shields.io/badge/plugins-7-teal)](adapters/opencode/plugins/)
17
17
  [![OpenCode · Claude Code · Bob · Copilot · OpenClaude · AGENT.md](https://img.shields.io/badge/works%20with-OpenCode%20·%20Claude%20Code%20·%20Bob%20·%20Copilot%20·%20OpenClaude%20·%20AGENT.md-1f6feb)](#-supported-harnesses)
18
18
 
@@ -163,10 +163,10 @@ The harness ships as a small set of layers, and the web console's rail is the sa
163
163
  | Layer | What it is |
164
164
  | --- | --- |
165
165
  | **🧭 Ethos** (`ontology/`) | the mind the rules govern, in four prose sections — **Telos** (the Pact: three ranked laws — protect the user, serve their intent, keep your own honesty), **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 |
166
- | **🛡️ Rules** (`laws/`) | 11 universal laws, nine of them in force — always, never toggleable: sealed-secrets, one-intent-one-act, verify-before-assert, deletion-is-deliberate, surface-failures, data-not-orders, least-privilege, root-cause, echo-the-intent. Laws IX and XI are retired, their numbers kept so old references resolve |
166
+ | **🛡️ Rules** (`laws/`) | 9 universal laws — always in force, never toggleable: sealed-secrets, one-intent-one-act, verify-before-asserting, deletion-is-deliberate, surface-failures, data-not-orders, least-privilege, cure-the-cause, echo-the-intent. Each rule has a permanent id; its number is only its position, and the harness cites rules by name |
167
167
  | **📐 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), **comms** (how answers are presented). 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 five pack files ship on disk either way, so a citation into a pack you left out still resolves |
168
168
  | **🤖 Agents** (18) | capability specialists: `reviewer`, `tester`, `architect`, `docs`, `security`, `explorer`, `researcher`, `developer` — plus a debate **council** the `council` skill convenes: `advocate`, `skeptic`, `pragmatist`, `steward`, `visionary`, `user-advocate`, `framer`, `empiricist`, `operator`, `historian` |
169
- | **🛠 Skills** (52) | repeatable workflows: brainstorm · plan · **codebase-design** · **domain-modeling** · **wayfinder** · **tickets** · **develop** · **worktree** · debug · **prototype** · refactor · **ponytail** · **forge-mcp** · **bruno-collection-generator** · **bruno-test-writer** · geneseed-code-review · **fresh-eyes** · **security-audit** · **ci-fix** · **deps-audit** · **review-response** · commit · **ship** · **release** · **migrate** · **git-archaeology** · **git-rescue** · repo-map · document-project · **frontend-design** · **prose** · **ingest** · **research** · **teach** · **quiz** · handoff · roast-me · **council** · parallel-agents · **workflow** · **wiki** · **geneseed** · **rule** · **profile** · **consolidate-memory** · **skill-forge** · **opencode-theme** · **herdr** · **pipeline** · **daydream** · **react-view-transitions** · **token-report** |
169
+ | **🛠 Skills** (53) | repeatable workflows: brainstorm · plan · **codebase-design** · **domain-modeling** · **wayfinder** · **tickets** · **develop** · **worktree** · debug · **prototype** · refactor · **ponytail** · **forge-mcp** · **bruno-collection-generator** · **bruno-test-writer** · geneseed-code-review · **fresh-eyes** · **security-audit** · **ci-fix** · **deps-audit** · **review-response** · commit · **ship** · **release** · **migrate** · **git-archaeology** · **git-rescue** · repo-map · document-project · **frontend-design** · **prose** · **ingest** · **research** · **teach** · **quiz** · handoff · roast-me · **council** · parallel-agents · **workflow** · **wiki** · **geneseed** · **rule** · **profile** · **consolidate-memory** · **skill-forge** · **opencode-theme** · **herdr** · **pipeline** · **daydream** · **react-view-transitions** · **token-report** · **explain-changes** |
170
170
  | **🔌 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 |
171
171
  | **🧠 Memory** (`memory/`) | one-fact-per-file durable knowledge, indexed by `MEMORY.md` (git-ignored, personal) |
172
172
  | **📓 Notebook** (`notebook/`) | the agent's sovereign space — any medium (code, tools, data, notes), self-ruled via a seed-once charter, always git-ignored; only its `.gitignore` is build-asserted |
package/SHIPPED.md CHANGED
@@ -20,7 +20,7 @@ historical references, 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.8.0**.
23
+ > string. Current label: **3.9.0**.
24
24
 
25
25
  ## Capabilities
26
26
 
@@ -29,8 +29,8 @@ Every row below is present in the tree today; "Spec" names the design record.
29
29
  | Capability | What it is | Spec |
30
30
  | --- | --- | --- |
31
31
  | **Themed generator** | One neutral `src/` → 14 themed bundles via `geneseed-build` (zero dependencies); structure stays theme-independent | [DESIGN.md](DESIGN.md) |
32
- | **Laws / Agents / Skills / Memory / Notebook** | 11 laws, 18 agents, 52 skills, the memory convention, and the agent's sovereign notebook | `2026-06-11-notebook-sovereign-space` · `2026-06-10-notebook-agent-own-space` |
33
- | **Three-tier constitution** | The governance surface splits into an always-on **Ontology** (Telos, Evidence, Decisions, Conduct — the Pact absorbed into Telos), the eleven always-on **Invariants** (IX and XI retired in place — folded into rigor 5 and Law III, numbers kept), and five toggleable **Doctrine** packs (craft, rigor, ops, process, comms) addressed `<pack> <n>`. Packs are chosen at build time with `--doctrines` or the setup wizard; the whole catalogue ships on disk either way, so a citation into an inactive pack still resolves. Precedence, in order: Ontology + Invariants, then the user's `user-rules.md`, then the active Doctrines, then `PROFILE.md`. `doctor`'s `constitutionProblems` gates pack numbering, the per-rule Principle column, every `{{DOCTRINE}}` citation in `src/`, and the rule that an always-on tier may never cite a toggleable one | `2026-08-19-three-tier-constitution-design` |
32
+ | **Laws / Agents / Skills / Memory / Notebook** | 9 laws, 18 agents, 53 skills, the memory convention, and the agent's sovereign notebook | `2026-06-11-notebook-sovereign-space` · `2026-06-10-notebook-agent-own-space` |
33
+ | **Three-tier constitution** | The governance surface splits into an always-on **Ontology** (Telos, Evidence, Decisions, Conduct — the Pact absorbed into Telos), the nine always-on **Invariants**, and five toggleable **Doctrine** packs (craft, rigor, ops, process, comms) addressed `<pack> <n>`. Every rule carries a permanent id; its number is its position, computed at render time, and a citation renders as the rule's principle name. Packs are chosen at build time with `--doctrines` or the setup wizard; the whole catalogue ships on disk either way, so a citation into an inactive pack still resolves. Precedence, in order: Ontology + Invariants, then the user's `user-rules.md`, then the active Doctrines, then `PROFILE.md`. `doctor`'s `constitutionProblems` gates rule ids (declared, unique, never a retired one), the per-rule Principle column, every `{{LAW:<id>}}` / `{{DOCTRINE:<id>}}` citation in `src/`, and the rule that an always-on tier may never cite a toggleable one | `2026-08-19-three-tier-constitution-design` |
34
34
  | **`doctor`** | One check: unresolved tokens, dead/non-hermetic links, theme-key parity, authoring gates, rendered-bundle drift | `2026-06-07-theme-aware-doctor-diff` |
35
35
  | **`diff` + improvements export** | Deployed-vs-source drift report; export an `improvements.md` back-port | `2026-06-12-improvements-export` |
36
36
  | **`status` / `version`** | Headless dashboard + fingerprint verdict (no TUI needed) | `2026-06-07-status-command` · `2026-06-07-version-and-uninstall` |
@@ -276,7 +276,7 @@ It is not one now: the invariants are a closed set of nine, and project-context
276
276
  discovery is one of the two old laws absorbed into AGENT.md's own **§9 Context**
277
277
  section, which every build carries as prose. Nothing an adapter spec writes is
278
278
  added to the invariants. The text below survives as the *instruction* tier behind
279
- the *injection* tier, and `{{DOCTRINE}} process 4` (Read the Docs First) is its
279
+ the *injection* tier, and `{{DOCTRINE:read-the-docs-first}}` (Read the Docs First) is its
280
280
  nearest live citation:
281
281
 
282
282
  > **Rule (Project Context Discovery).** When project context has not already been
@@ -384,7 +384,7 @@ behaviour** — nothing changes the machine's current agent/model unless you opt
384
384
  `2026-06-17-opencode-color-themes` spec).
385
385
  - **Runtime guard plugin** (`geneseed-guard.js`, installed with the others). Enforces
386
386
  the safety Laws at the tool boundary: **blocks** writes to private-key/credential
387
- files (Law I), catastrophic shell like `rm -rf /` (Law IV), and any mutation under
387
+ files (Sealed Secrets), catastrophic shell like `rm -rf /` (Deletion Is Deliberate), and any mutation under
388
388
  a declared wiki's `protected` folders (AGENT.md §8, from `wiki.jsonc`); **warns** on
389
389
  `.env` writes and force-push. `GENESEED_GUARD=off` disables it, `=warn` downgrades
390
390
  blocks to warnings. Every **block** appends one line to the install's
@@ -400,7 +400,7 @@ behaviour** — nothing changes the machine's current agent/model unless you opt
400
400
  - **Default permissions.** A fresh `opencode.json` gets a minimal policy that **asks**
401
401
  before `rm -rf *` and a force-push — those are **Rule IV**'s territory and are in every
402
402
  build — and, **on an install that built the `process` pack in**, before every
403
- `git commit` and `git push` (the host-level backstop for **Doctrine process 5**: the
403
+ `git commit` and `git push` (the host-level backstop for **Consent Before Push**: the
404
404
  agent never records or shares code unprompted, on any branch). Build with
405
405
  `--doctrines` leaving `process` out and those two entries are simply not written.
406
406
  The whole block is added only when you have no `permission` key; an existing policy is
@@ -534,7 +534,7 @@ install `jsonc-parser` yourself — you lose OCX's dependency management and
534
534
  auto-updates, and you own the upgrade-by-re-copy. Prefer the OCX path.
535
535
 
536
536
  **Consent note.** `worktree_delete` auto-commits before removal. The harness's
537
- consent-before-commit rule (**Doctrine process 5**, when the `process` pack is built in)
537
+ consent-before-commit rule (**Consent Before Push**, when the `process` pack is built in)
538
538
  is enforced for *shell* `git commit` via `opencode.json`
539
539
  permissions, but this plugin commits through its own tool path — so review what it
540
540
  will commit before invoking delete, or keep the worktree and commit yourself.
@@ -2,9 +2,9 @@
2
2
  //
3
3
  // Enforces the safety Laws at the tool boundary (`tool.execute.before`), the same
4
4
  // "enforce by injection, don't just instruct" stance as the context plugin:
5
- // - Law I (Sealed Secrets): block writes to private-key / credential files.
6
- // - Law IV (Deletion Is Deliberate): block catastrophic shell commands.
7
- // - Doctrine process 1 (rule vs memory): speed-bump the first write to user-rules.md
5
+ // - Sealed Secrets: block writes to private-key / credential files.
6
+ // - Deletion Is Deliberate: block catastrophic shell commands.
7
+ // - Persist Insight (rule vs memory): speed-bump the first write to user-rules.md
8
8
  // memory file — that choice belongs to the user, via the rule skill.
9
9
  // - Wiki (AGENT.md §8): block mutations under a declared wiki's `protected`
10
10
  // folders — the user's knowledge base sets its own no-go zones in wiki.jsonc.
@@ -238,7 +238,7 @@ async function protectedPrefixes() {
238
238
  return prefixes
239
239
  }
240
240
 
241
- // ---- the rule / memory stores (Doctrine process 1) -----------------------------
241
+ // ---- the rule / memory stores (Persist Insight) --------------------------------
242
242
  // Whether something the user wants kept is a standing rule or a durable fact is THEIR
243
243
  // call, settled through the rule skill. `tool.execute.before` can only allow or throw —
244
244
  // it has no "ask the user" tier like the Claude/Bob rule-gate hook —
@@ -285,13 +285,13 @@ export const GeneseedGuard = async () => {
285
285
  try {
286
286
  if (hasAny(tool, WRITE_TOOLS)) {
287
287
  const p = pickPath(args)
288
- if (p && SECRET_RE.some((re) => re.test(p))) { await deny(`write to secret/key file ${p} (Law I)`, "law-1"); return }
289
- if (p && SECRET_WARN_RE.some((re) => re.test(p))) log(`WARN: writing ${p} — keep secrets out of tracked files (Law I)`)
288
+ if (p && SECRET_RE.some((re) => re.test(p))) { await deny(`write to secret/key file ${p} (Sealed Secrets)`, "law-1"); return }
289
+ if (p && SECRET_WARN_RE.some((re) => re.test(p))) log(`WARN: writing ${p} — keep secrets out of tracked files (Sealed Secrets)`)
290
290
  const store = p && ruleStoreTarget(p)
291
291
  if (store && !RULE_STORE_BUMPED.has(p)) {
292
292
  RULE_STORE_BUMPED.add(p)
293
293
  await deny(`writing to ${store} — a standing rule, or a fact to remember? That ` +
294
- `choice is the user's (Doctrine process 1). Settle it through the rule skill, ` +
294
+ `choice is the user's (Persist Insight). Settle it through the rule skill, ` +
295
295
  `then re-issue this write`, "process-1")
296
296
  return
297
297
  }
@@ -309,8 +309,8 @@ export const GeneseedGuard = async () => {
309
309
  // classes, and the shell check must still run after the write check.
310
310
  if (hasAny(tool, SHELL_TOOLS)) {
311
311
  const c = pickCommand(args)
312
- if (c && SHELL_BLOCK_RE.some((re) => re.test(c))) { await deny(`catastrophic command (Law IV): ${c.slice(0, 80)}`, "law-4"); return }
313
- if (c && SHELL_WARN_RE.some((re) => re.test(c))) log(`WARN: irreversible op — confirm intent (Law IV): ${c.slice(0, 80)}`)
312
+ if (c && SHELL_BLOCK_RE.some((re) => re.test(c))) { await deny(`catastrophic command (Deletion Is Deliberate): ${c.slice(0, 80)}`, "law-4"); return }
313
+ if (c && SHELL_WARN_RE.some((re) => re.test(c))) log(`WARN: irreversible op — confirm intent (Deletion Is Deliberate): ${c.slice(0, 80)}`)
314
314
  }
315
315
  } catch (err) {
316
316
  // Our own deny must propagate; any inspection error must never break a tool call.
@@ -89,7 +89,7 @@ function summarize(name, result, stats, tracePath) {
89
89
  }
90
90
 
91
91
  // Kept worktrees are work awaiting review, and an overlap is a conflict awaiting ONE
92
- // merger (process 7) — both are said out loud, never left for the trace to hold.
92
+ // merger (One Writer Per File) — both are said out loud, never left for the trace to hold.
93
93
  function isolationReport(stats) {
94
94
  const kept = stats.worktrees || []
95
95
  if (!kept.length) return ""
@@ -182,8 +182,8 @@ async function runChildSession(client, { prompt, agent, model, title, directory
182
182
 
183
183
  // ---- worktree isolation ------------------------------------------------------
184
184
  // `agent(prompt, { isolation: "worktree" })` — the same option Claude Code's Workflow
185
- // takes. Parallel editors in one tree lose each other's writes (process 7, one writer per
186
- // file), so each isolated agent gets its own branch + worktree off the root's HEAD, and
185
+ // takes. Parallel editors in one tree lose each other's writes (One Writer Per File),
186
+ // so each isolated agent gets its own branch + worktree off the root's HEAD, and
187
187
  // the files it changed are recorded for `overlaps()`. A worktree with no change is
188
188
  // removed with its branch; one with changes is KEPT for the parent to review and merge —
189
189
  // the runtime never merges, commits, or deletes work. `git(argv, cwd)` is injected by the
@@ -3,7 +3,7 @@
3
3
  //
4
4
  // The model-driven fallback is the parallel-agents skill; this runs the same
5
5
  // shape on rails: the script, not the model, enforces routing and the handoff
6
- // envelope (context in, output contract back, no commits — Doctrine process 5 stays with
6
+ // envelope (context in, output contract back, no commits — Consent Before Push stays with
7
7
  // the caller).
8
8
  //
9
9
  // args: { goal: string, context?: string, agents?: string[] }
package/docs/extending.md CHANGED
@@ -155,36 +155,43 @@ Notes that matter:
155
155
 
156
156
  ## 2 — Changing the constitution
157
157
 
158
- Three tiers, four different costs. The addresses are `{{LAW}} <roman>` for an invariant and
159
- `{{DOCTRINE}} <pack> <n>` for a doctrine rule; the ontology is cited by section name and adding a
160
- section to it is not a checklist, it is a design change.
158
+ Three tiers, four different costs. Every law and doctrine rule has a **permanent id** — kebab-case,
159
+ derived from its English principle when it is created (`verify-before-asserting`,
160
+ `consent-before-push`) — declared by its heading, `### {{LAW:<id>}} <Principle Name>` or
161
+ `### {{DOCTRINE:<id>}} <Principle Name>`, and cited by it: `{{LAW:<id>}}`, `{{DOCTRINE:<id>}}`.
162
+ **The number is not stored anywhere.** `ruleCanon` in `js/build/source.mjs` computes it from the
163
+ rule's position (Roman in `laws/universal.md`, Arabic within its pack) at render time; the heading
164
+ renders `Rule III — <themed title>` and a citation renders as the principle's name, in italics,
165
+ identical in every voice — the same string an agent is told to write into its memory. So adding,
166
+ removing or reordering a rule rewires nothing. `constitutionProblems` refuses a citation by number,
167
+ an unknown id, a duplicate id, a heading with no id, and a retired id declared again. The ontology is
168
+ cited by section name, and adding a section to it is not a checklist, it is a design change.
161
169
 
162
170
  To count the cross-references either tier already carries, from the repo root:
163
171
 
164
172
  ```bash
165
- grep -rhoE "\{\{LAW\}\} [IVXL]+" src/ | wc -l
166
- grep -rhoE "\{\{DOCTRINE\}\} [a-z]+ [0-9]+" src/ | wc -l
173
+ grep -rhoE "\{\{LAW:[a-z0-9-]+\}\}" src/ | wc -l
174
+ grep -rhoE "\{\{DOCTRINE:[a-z0-9-]+\}\}" src/ | wc -l
167
175
  ```
168
176
 
169
- On this checkout that reports 153 and 209. Both numbers move on every wave of editing, which is
170
- why the commands are here and the answers are not load-bearing.
177
+ Both numbers move on every wave of editing, which is why the commands are here and the answers are
178
+ not.
171
179
 
172
180
  ### 2a — Adding a DOCTRINE RULE (the common case)
173
181
 
174
182
  Five files, plus the written-out rule totals in four test files. This is the slot most new
175
183
  material belongs in, and it is cheap on purpose so that the invariant slot stays scarce.
176
184
 
177
- 1. `src/doctrines/<pack>.md` — **append** `### {{DOCTRINE}} <pack> <n> — {{DOC_<PACK>_<n>}}` plus
178
- the body, wrapped in a `LEAN` block with **both halves authored** — the heading stays outside the
179
- block, the full text goes in `LEAN:begin`, and the lean half is the rule *and its mechanism*, no
180
- maxims (the style rule is in `src/doctrines/README.md`, and `leanBlockProblems` refuses a rule
181
- with no block or an empty half). Two gates in `constitutionProblems`
182
- (`js/inspect/checks-authoring.mjs`) sit on this too: the pack named in
183
- the *heading* is what addresses the rule (not the filename, so a rule filed in the wrong file is
184
- reported rather than silently renamed), and ids must run **contiguously from 1**, so appending
185
- at the end is the only place `n` is free. Packs are numbered independently — nothing outside
186
- this file renumbers.
187
- 2. `themes/_TEMPLATE.json` — add `DOC_<PACK>_<n>`. ⚠ **This file *is* gated for this key family.**
185
+ 1. `src/doctrines/<pack>.md` — add `### {{DOCTRINE:<new-id>}} <Principle Name>` plus the body,
186
+ wrapped in a `LEAN` block with **both halves authored** — the heading stays outside the block,
187
+ the full text goes in `LEAN:begin`, and the lean half is the rule *and its mechanism*, no maxims
188
+ (the style rule is in `src/doctrines/README.md`, and `leanBlockProblems` refuses a rule with no
189
+ block or an empty half). The id is kebab-case, unique across laws **and** doctrines, and never
190
+ one listed in `RETIRED_RULE_IDS` (`js/build/source.mjs`). Where it goes in the file decides its
191
+ number; appending is still the kind move, because `excludeRules`, `DOCTRINE_META` and the
192
+ hook-gated addresses are keyed `<pack>.<n>` (see §2e).
193
+ 2. `themes/_TEMPLATE.json` — add `DOC_<ID>` (`consent-before-push` → `DOC_CONSENT_BEFORE_PUSH`).
194
+ ⚠ **This file *is* gated for this key family.**
188
195
  `themeFiles()` filters `_`-prefixed names and theme parity never sees the template, but
189
196
  `constitutionProblems` reads it alongside the fourteen voices for `DOC_*` and `LEX_*`
190
197
  specifically — because it is what `--sync-themes` seeds the *next* voice from. Every other key
@@ -199,7 +206,9 @@ material belongs in, and it is cheap on purpose so that the invariant slot stays
199
206
  is one of the two. See §7.2 before you copy a command from anywhere else. Then restyle all
200
207
  fourteen by hand: `--sync-themes` writes the template's placeholder and only *prints*
201
208
  `RESTYLE these`; a shipped placeholder passes every gate.
202
- 4. `web/src/pages/Laws.jsx` — a `DOCTRINE_META` row keyed `'<pack>.<n>'`. Its first field is the
209
+ 4. `web/src/pages/Laws.jsx` — a `DOCTRINE_META` row keyed `'<pack>.<n>'`, whose third field is the
210
+ rule's id — `doctrineMetaProblems` holds that pin to the canon, so a row left behind by a
211
+ renumber is a failure rather than a description of the neighbour. Its first field is the
203
212
  **pack**, not one of `LAW_CLASSES`' six; `doctrineMetaProblems` requires an *equality* between
204
213
  that field and the key's own pack, which catches the copy error a six-way vocabulary cannot — a
205
214
  row pasted from the pack above it, keeping the wrong pack. Its second field is the one-line
@@ -213,19 +222,18 @@ material belongs in, and it is cheap on purpose so that the invariant slot stays
213
222
  there is no second taxonomy over it.
214
223
  - **No count in the product.** No badge, no `SHIPPED.md` triple, no prose mirror. The console spends
215
224
  `{N_PACKS}` / `{N_PACKS_ACTIVE}` / `{N_DOCTRINE_RULES}`, all computed at request time.
216
- - **No renumber risk.** But there *are* written-out rule totals to bump — expected values are
225
+ - **No citation to rewire.** But there *are* written-out rule totals to bump — expected values are
217
226
  written out, never recorded — in `tests/unit/emit_smoke.test.mjs`, `harness.test.mjs`,
218
227
  `setup.test.mjs` and `web_api.test.mjs`. Each fails loudly with the new count; change the
219
228
  number *and* the comment that explains it. A rule in **craft** moves more: `setup` and
220
229
  `web_api` also pin the craft-only narrowed counts, `generate.test.mjs` excludes every craft
221
- rule by address to empty the pack, and `harness.test.mjs`'s contiguity fixture plants a rule at
222
- the first free craft id — the doctrine twin of §2c step 8. Grep `tests/` for the previous
230
+ rule by address to empty the pack. Grep `tests/` for the previous
223
231
  rule's address before trusting this list. A rule long enough to matter can also breach the
224
232
  `full` carrier ceiling in `emit_smoke` — raise it with a dated note there, as every earlier
225
233
  move did.
226
234
 
227
235
  ⚠ **Cite only downward.** An always-on tier may never reference a toggleable one:
228
- `constitutionProblems` refuses a `{{DOCTRINE}}` token anywhere under `src/ontology/` or
236
+ `constitutionProblems` refuses a `{{DOCTRINE:…}}` token anywhere under `src/ontology/` or
229
237
  `src/laws/`, because a `--doctrines craft` build would then ship an invariant pointing at text its
230
238
  `AGENT.md` does not contain. Doctrine→doctrine citations are fine and stay — every pack file ships
231
239
  on disk whether or not it was built in. If the build's own default is narrowed,
@@ -248,26 +256,25 @@ Steps 1 and 9 of §2c, plus `LAW_META` if the Principle line moved. Two cautions
248
256
  Nine steps, and only the first is the rule itself. This is the expensive one, and it should be:
249
257
  the bar is in `DESIGN.md` Decision 7, and most candidates belong in a doctrine pack instead.
250
258
 
251
- 1. `src/laws/universal.md` — **append** `### {{LAW}} <roman> — {{LEX_<roman>}}` plus the body.
252
- Never insert: the `{{LAW}}` cross-references counted above live all over `src/`, plus hardcoded
253
- citations in `js/hosts/hooks.mjs` and two OpenCode plugins, and **nothing resolves a cross-reference
254
- against the canon**. Renumbering silently rewires every one of them.
255
- 2. `themes/_TEMPLATE.json` — add `LEX_<roman>` in template order. Gated, as in §2a step 2:
256
- `constitutionProblems` holds `LEX_I..LEX_XI` as an **equality** across all fifteen files (the
257
- fourteen voices and the template), so both a missing key *and* a leftover one are reported. That
258
- arm exists because the renumber left `LEX_XXII`, `LEX_XXIII`, `LEX_XXIV` and `LEX_XXXVI` behind
259
- in every file and parity was silent — they were absent from nowhere.
259
+ 1. `src/laws/universal.md` — add `### {{LAW:<new-id>}} <Principle Name>` plus the body. Citations
260
+ are by id and resolve against the canon, so position is safe to choose — but append unless the
261
+ order is the point: `LAW_CLASS` and `LAW_META` are keyed by the rendered number (see §2e).
262
+ 2. `themes/_TEMPLATE.json` — add `LEX_<ID>` in template order. Gated, as in §2a step 2:
263
+ `constitutionProblems` holds the `LEX_*` keys as an **equality** with the declared law ids across
264
+ all fifteen files (the fourteen voices and the template), so both a missing key *and* a leftover
265
+ one are reported. That arm exists because an old renumber left `LEX_XXII`, `LEX_XXIII`,
266
+ `LEX_XXIV` and `LEX_XXXVI` behind in every file and parity was silent.
260
267
  3. `--sync-themes`, then restyle all fourteen by hand (§2a step 3).
261
268
  4. `LAW_CLASS` in `js/inspect/inventory.mjs` — one of the six in `LAW_CLASSES` beside it.
262
- 5. `LAW_META` in `web/src/pages/Laws.jsx` — same class, plus the one-line Principle. Note the map
263
- is keyed by **arabic** number where `universal.md` heads with a Roman numeral. **This copy
269
+ 5. `LAW_META` in `web/src/pages/Laws.jsx` — same class, the one-line Principle, and the rule's id
270
+ as the pin. Note the map is keyed by **arabic** number where the heading renders a Roman numeral. **This copy
264
271
  exists nowhere else**; a rule absent here renders with a blank description. It is why that one
265
272
  React file ships alone out of `web/src` in `package.json` — `doctor` regex-parses its literal.
266
273
  6. `README.md` — badge `badge/laws-N` and the `N universal laws` sentence.
267
274
  7. `SHIPPED.md` — the `N laws, N agents, N skills` triple.
268
- 8. `tests/unit/harness.test.mjs` — the negative fixture for the `LAW_CLASS` gate plants a rule at
269
- the *first free numeral*, which is `XII` now that the invariants are I..XI. Bump it, or it stops
270
- being a fixture and starts being a duplicate.
275
+ 8. `tests/unit/harness.test.mjs` — the negative fixture for the `LAW_CLASS` gate appends a law and
276
+ expects the next numeral (`X` while there are nine), and `ROMANS` there and in `emit_smoke`
277
+ lists the numerals written out. Bump both.
271
278
  9. `CHANGELOG.md`, then rebuild and commit `web/dist` (step 5 touched `web/src`).
272
279
 
273
280
  ⚠ While restyling, `{{LAW}}` must stay a **single word** in every theme (and so must
@@ -288,8 +295,8 @@ the only configuration it can speak for.
288
295
  Rare, and mechanical. On top of §2a for each rule the pack carries:
289
296
 
290
297
  1. `src/doctrines/<pack>.md`, opening with its lead line — `**{{PACK_<NAME>}}** — how … .` A pack
291
- file `PACK_ORDER` names but cannot read, or one carrying no `### {{DOCTRINE}} <pack> <n>`
292
- heading at all, is a refusal.
298
+ file `PACK_ORDER` names but cannot read, or one declaring no `### {{DOCTRINE:<id>}}` rule at
299
+ all, is a refusal.
293
300
  2. `PACK_ORDER` in `js/build/source.mjs` — the registration, and the render order. It is **narrative,
294
301
  not alphabetical** (craft → rigor → ops → process → comms); discovery sorts alphabetically and the
295
302
  renderer refuses a `.md` under `src/doctrines/` that `PACK_ORDER` does not name, rather than
@@ -315,6 +322,31 @@ Rare, and mechanical. On top of §2a for each rule the pack carries:
315
322
 
316
323
  ---
317
324
 
325
+ ### 2e — Removing, retiring or reordering a rule
326
+
327
+ Delete the rule's heading and both LEAN halves, and every rule after it renumbers on the next
328
+ render — no citation moves, because none names a number. Then:
329
+
330
+ 1. `RETIRED_RULE_IDS` in `js/build/source.mjs` — add the id with what it was and where its text
331
+ went. `constitutionProblems` refuses a retired id declared again, so an id that meant one rule in
332
+ someone's notes can never come back meaning another. `liveAs` names the one tier that may still
333
+ declare it (Law IX's `external-gate` lives on as a rigor rule).
334
+ 2. Every citation of the removed id — the doctor names each one; point it at the rule that now
335
+ carries the text.
336
+ 3. Its `LEX_<ID>`/`DOC_<ID>` key in all fifteen theme files (the equality arm names each).
337
+ 4. The number-keyed tables, which the doctor checks against the canon: `LAW_CLASS`
338
+ (`js/inspect/inventory.mjs`), `LAW_META`/`DOCTRINE_META` (their id pins fail on every shifted
339
+ row), and — only if a hook-gated rule moved — `HOOK_PINNED` in `checks-authoring.mjs` names the
340
+ ledger keys, `CONSENT_RULE` and `HOOK_GATED` to move with it. `--exclude-rules` addresses are
341
+ `<pack>.<n>` too, and an install that excluded a shifted doctrine rule excludes its neighbour
342
+ after a re-emit: prefer appending in a pack, and say so in the CHANGELOG when you cannot.
343
+ 5. The count mirrors: README badge and prose, `SHIPPED.md`, and the written-out totals in tests.
344
+
345
+ Laws IX (External Gate) and XI (Absence Is a Claim) were removed this way on 2026-09-30; Echo the
346
+ Intent became IX.
347
+
348
+ ---
349
+
318
350
  ## 3 — Adding a SKILL or an AGENT
319
351
 
320
352
  **Order matters at the first two steps.** `missingReferencedSpecs` (in `js/build/bundle.mjs`) *aborts the
@@ -574,7 +606,7 @@ only, at best, on its presence.
574
606
  |---|---|---|
575
607
  | `§N` cross-references into `AGENT.md`'s anatomy | `src/skills/*`, `src/agents/*`, `adapters/**` | **range only.** `constitutionProblems` refuses a `§N` the template declares no section for — which catches a pointer past the end, the shape *removing* a section leaves. It cannot catch one that still resolves and now means something else, which is the shape *inserting* a section leaves, and is what actually happened when `## 2. Doctrines` pushed every later section down one. **Renumbering the anatomy means `grep -rn '§' src/ adapters/` and reading every hit.** Four satellites and a live OpenCode deny message shipped stale because the sweep stopped at `AGENT.md.tmpl` |
576
608
  | Per-law Principle lines | `web/src/pages/Laws.jsx` | presence and class only, never accuracy |
577
- | 588 themed constitution titles (42 keys × 14 voices) — `LEX_I..LEX_XI` (11), `DOC_<PACK>_<n>` (27), `PACK_<NAME>` (4) | `themes/*.json` | key presence only — a shipped placeholder is green. `LEX_*` and `DOC_*` are held across the template too, and in both directions; `PACK_*` only across the voices |
609
+ | 588 themed constitution titles (42 keys × 14 voices) — `LEX_<ID>` (one per law), `DOC_<ID>` (one per doctrine rule), `PACK_<NAME>` | `themes/*.json` | key presence only — a shipped placeholder is green. `LEX_*` and `DOC_*` are held across the template too, and in both directions; `PACK_*` only across the voices |
578
610
  | The README keyword enumerations | `README.md`, `docs/web/rules.md` | nothing |
579
611
  | The plugin capability enumeration | `docs/web/model.md` | nothing (the number substitutes; the list does not) |
580
612
  | Section labels and page subtitles | `web/src/lib/sections.js`, `web/src/pages/Docs/index.jsx` | nothing |
@@ -13,7 +13,7 @@ The harness *content* — the constitution (Ethos, Rules, Doctrines), Agents, Sk
13
13
  | Agents · Skills · Memory | ✅ | ✅ | ✅ | ✅ | ✅ |
14
14
  | Context injection | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble | 🪝 hook² |
15
15
  | Memory write-back (learn) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble | 🪝 hook² |
16
- | Git-gate consent (Doctrine process 5) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble | 🪝 hook² |
16
+ | Git-gate consent (Consent Before Push) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble | 🪝 hook² |
17
17
  | Sovereign-repo excludes | ⚙️ plugin | ✅ | ✅ | ➖ | ✅ |
18
18
  | MCP server wiring | ✅ | ✅ | ✅ | ✅ | ✅ |
19
19
  | Colour themes | ✅ | ➖ | ➖ | ➖ | ➖ |
@@ -21,7 +21,7 @@ The harness *content* — the constitution (Ethos, Rules, Doctrines), Agents, Sk
21
21
 
22
22
  ✅ native · ⚙️ OpenCode plugin · 🪝 `settings.json` hook · 📄 preamble prose only · ➖ no host mechanism (harness discipline still applies). ¹ Bob honours Claude-dialect hooks best-effort — inert if unsupported, harness still holds via the preamble. ² OpenClaude is a Claude Code fork: Claude's hooks verbatim, under `~/.openclaude` or a repo's `.openclaude/`.
23
23
 
24
- **The git-gate row is the one capability that is not always present**, on any host. It implements **Doctrine process 5**, so it is wired only on an install that built the **process** [doctrine pack](#/docs/setup-choices) in — drop that pack and the boundary drops with it, because a gate that keeps asking for a rule the install never adopted is a gate arguing with its own harness. One asymmetry: on OpenCode that holds for a *fresh* install, but a pack-off rebuild of an install whose `opencode.json` already carries a `git commit*` entry leaves it in place and reports it — Geneseed cannot tell its own entry from one you typed, so it removes neither ([doctrine packs](#/docs/setup-choices)). The invariant-territory refusals in the same neighbourhood (`rm -rf`, a force-push) are the Rules' territory, not the pack's, and stay in every build.
24
+ **The git-gate row is the one capability that is not always present**, on any host. It implements **Consent Before Push** (`process 5`), so it is wired only on an install that built the **process** [doctrine pack](#/docs/setup-choices) in — drop that pack and the boundary drops with it, because a gate that keeps asking for a rule the install never adopted is a gate arguing with its own harness. One asymmetry: on OpenCode that holds for a *fresh* install, but a pack-off rebuild of an install whose `opencode.json` already carries a `git commit*` entry leaves it in place and reports it — Geneseed cannot tell its own entry from one you typed, so it removes neither ([doctrine packs](#/docs/setup-choices)). The invariant-territory refusals in the same neighbourhood (`rm -rf`, a force-push) are the Rules' territory, not the pack's, and stay in every build.
25
25
 
26
26
  **No host drops an Agent, Skill, or the memory convention.** The asymmetry is otherwise entirely in *automation mechanism*: OpenCode's plugins and Claude/Bob/OpenClaude's hooks enforce a few of these rules for you at the tool boundary, where **Copilot** (no hook mechanism) enforces them through preamble discipline. The OpenCode-only extras (themes, LSP, workflow runner, primary-agent) have no analogue on a Claude-shaped host.
27
27
 
@@ -6,9 +6,9 @@ kind: "concept"
6
6
  ---
7
7
  Enforces the safety Laws at the tool boundary (`tool.execute.before`) — the same *enforce by injection, don't just instruct* stance as the context plugin. The guard ships whole on every install, whatever [doctrine packs](#/docs/setup-choices) it built in — its blocking arms are **invariant** territory, and the speed-bump below is about which store a note belongs in, which is your call either way. The one boundary that does follow a pack is the commit/push consent gate, and that lives in the permission block rather than here. High-confidence patterns only, so legitimate work is never caught:
8
8
 
9
- - **Blocks** — writes to private-key / credential files (**Law I**), catastrophic shell like `rm -rf /` (**Law IV**), and any mutation under a declared wiki's `protected` folders (AGENT.md §8, from `wiki.jsonc`).
9
+ - **Blocks** — writes to private-key / credential files (**Sealed Secrets**), catastrophic shell like `rm -rf /` (**Deletion Is Deliberate**), and any mutation under a declared wiki's `protected` folders (AGENT.md §8, from `wiki.jsonc`).
10
10
  - **Warns** (logged, allowed) — `.env` writes and force-push.
11
- - **Speed-bumps** — the *first* write to `user-rules.md` or a memory file is refused, naming **Doctrine process 1**: a standing rule or a durable fact is the user's call, settled through the rule skill. A re-issued write goes through, so a legitimate write is never trapped. (On Claude Code and Bob the same guard is a `geneseed-hook rule-gate` hook, which can *ask* the user instead of refusing — OpenCode's `tool.execute.before` has no ask tier.)
11
+ - **Speed-bumps** — the *first* write to `user-rules.md` or a memory file is refused, naming **Persist Insight**: a standing rule or a durable fact is the user's call, settled through the rule skill. A re-issued write goes through, so a legitimate write is never trapped. (On Claude Code and Bob the same guard is a `geneseed-hook rule-gate` hook, which can *ask* the user instead of refusing — OpenCode's `tool.execute.before` has no ask tier.)
12
12
 
13
13
  ### Install
14
14
 
package/docs/web/rules.md CHANGED
@@ -13,7 +13,7 @@ The agent's standing rules come in **three tiers**, and the tier decides how a r
13
13
 
14
14
  ### Invariants — always on
15
15
 
16
- {N_LAWS} rules in `src/laws/universal.md`, numbered in Roman from `I`: sealed secrets, one intent per act, verify before asserting, deliberate deletion, surface failures, data not orders, least privilege, cure the cause, and the external gate. They hold in every task and every repository, they are never traded away, and no install can switch one off. Cited by numeral — `Rule IV`.
16
+ {N_LAWS} rules in `src/laws/universal.md`, numbered in Roman from `I`: sealed secrets, one intent per act, verify before asserting, deliberate deletion, surface failures, data not orders, least privilege, cure the cause, and echo the intent. They hold in every task and every repository, they are never traded away, and no install can switch one off. Cited by name — *Deletion Is Deliberate* — never by numeral: the numeral is only the rule's position, and it moves when a rule before it is removed.
17
17
 
18
18
  ### Doctrines — chosen at build time
19
19
 
@@ -11,8 +11,8 @@ A theme is one JSON file under `themes/` declaring voice tokens only: `BANNER`,
11
11
 
12
12
  Every rule in the constitution gets its title from a theme key, and a theme that omits one ships a rule with a blank heading:
13
13
 
14
- - `LEX_I`…`LEX_IX` — one title per invariant. `doctor` holds this to an **equality**, not mere presence: a `LEX_*` outside `I`–`IX` is a title for a rule that does not exist and fails the check too.
15
- - `DOC_*` — one title per doctrine rule, named after its address (`DOC_CRAFT_1`, `DOC_PROCESS_5`, …). `doctor` reads the pack files under `src/doctrines/` and requires exactly the set they name — a missing one is a blank heading, an extra one is a dead key.
14
+ - `LEX_<ID>` — one title per invariant, keyed by the law's permanent id (`LEX_VERIFY_BEFORE_ASSERTING`), never by its number. `doctor` holds this to an **equality**, not mere presence: a `LEX_*` no law declares is a title for a rule that does not exist and fails the check too.
15
+ - `DOC_<ID>` — one title per doctrine rule, keyed by its id (`DOC_AUTOMATE_REPETITION`, `DOC_CONSENT_BEFORE_PUSH`, …). `doctor` reads the pack files under `src/doctrines/` and requires exactly the set they declare — a missing one is a blank heading, an extra one is a dead key. A title is the rule's heading only: a citation of the rule renders as its English principle name in every voice.
16
16
  - `PACK_*` — one name per pack (`PACK_CRAFT`, `PACK_RIGOR`, `PACK_OPS`, `PACK_PROCESS`, `PACK_COMMS`), used for the pack's own sub-heading.
17
17
 
18
18
  The Ethos section names are **not** theme keys: a citation reads `({{ONTOLOGY}}: {{ONT_TELOS}})`, token on both sides, so heading and reference always move together. They live in the build's fixed structure table and you neither declare nor rename them.
package/docs/web-ui.md CHANGED
@@ -160,7 +160,7 @@ streams the output of background jobs and keeps their history across reloads.
160
160
  global harness goes dormant (hooks stay silent, the global preamble never loads);
161
161
  add or remove one here, the web mirror of `harness exclude add|remove|list`.
162
162
  - **Settings** (`#/settings`) — a **Console direction** picker (the visual flavour of the
163
- console) with a **Dashboard layout** control (Auto follows each theme's designed Status
163
+ console: Cultivar, Operator, Matrix or Atlas) with a **Dashboard layout** control (Auto follows each theme's designed Status
164
164
  lens; Cultivar / Greenhouse / Operator / Journal force one regardless of skin), the
165
165
  install snapshot (deployed/target/theme/version/memory store), a Maintenance card (PATH
166
166
  link/unlink, uninstall), an offline package download, server controls (Stop), and the
@@ -2,5 +2,5 @@
2
2
  "_comment": "version is a human-readable release label only; the canonical identity of an installed harness is the source fingerprint stamped in .geneseed-version (see `harness version`), not this string.",
3
3
  "theme": "neutral",
4
4
  "product": "Geneseed",
5
- "version": "3.8.0"
5
+ "version": "3.9.0"
6
6
  }
package/js/README.md CHANGED
@@ -37,7 +37,7 @@ as a per-host install at project or global scope.
37
37
 
38
38
  | module | owns |
39
39
  |---|---|
40
- | `source.mjs` | Where the SOURCE is: `ROOT`/`SRC`/`THEMES`/`CONFIG`, `PACK_ORDER`, rule ids, the cfg object |
40
+ | `source.mjs` | Where the SOURCE is: `ROOT`/`SRC`/`THEMES`/`CONFIG`, `PACK_ORDER`, the rule canon (stable ids → positional numbers, citations → names), the cfg object |
41
41
  | `render.mjs` | The pure text pipeline: theme load, `INCLUDE` inlining, CATALOG blocks, `{{TOKENS}}`, dest paths |
42
42
  | `bundle.mjs` | `build` — render a theme into a bundle dir, plus the source-completeness refusal. Sits UNDER both emits |
43
43
  | `emit-claude.mjs` | The Claude/Bob/Copilot/OpenClaude emit — the render half plus the settings + CLAUDE.md managed-block wire |
@@ -182,8 +182,9 @@ and inline lists are held against a surface list written out in
182
182
 
183
183
  | I want to | open first | then |
184
184
  |---|---|---|
185
- | a doctrine rule | `src/doctrines/<pack>.md` — APPEND, ids run contiguously | `docs/extending.md` §2a — five files, `--sync-themes`, restyle 14 voices by hand. Author BOTH LEAN halves |
186
- | a law (invariant) | `src/laws/universal.md` — **APPEND, never insert** | `docs/extending.md` §2c. Author both LEAN halves: the lean footprint ships nothing else |
185
+ | a doctrine rule | `src/doctrines/<pack>.md` — `### {{DOCTRINE:<id>}} <Principle>`; the number is its position | `docs/extending.md` §2a — five files, `--sync-themes`, restyle 14 voices by hand. Author BOTH LEAN halves |
186
+ | a law (invariant) | `src/laws/universal.md` — `### {{LAW:<id>}} <Principle>`; cite by id, never by number | `docs/extending.md` §2c. Author both LEAN halves: the lean footprint ships nothing else |
187
+ | to remove or retire a rule | `docs/extending.md` §2e | `RETIRED_RULE_IDS` in `js/build/source.mjs` — a retired id is never reused |
187
188
  | a skill or an agent | `src/skills/<name>.md` or `src/agents/<name>.md` | `docs/extending.md` §3 — eight steps. Write the FILE before the table row or the build aborts |
188
189
  | a doctor check | `js/inspect/checks-{build,repo,authoring}.mjs` | register with `ran(...)` in `doctor.mjs`, then a PLANTED FAULT in `tests/unit/harness.test.mjs` |
189
190
  | a web endpoint | GET → `js/web/api.mjs`; POST → `js/web/actions.mjs` | declare it in `js/web/routes.mjs`; the partition test probes the real handler |