geneseed 2.1.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/DESIGN.md +112 -61
  3. package/QUICKSTART.md +1 -1
  4. package/README.md +24 -14
  5. package/SETUP.md +83 -17
  6. package/SHIPPED.md +5 -4
  7. package/adapters/bob/README.md +3 -3
  8. package/adapters/claude-code/README.md +6 -4
  9. package/adapters/claude-code/skills/geneseed/SKILL.md +10 -6
  10. package/adapters/copilot/README.md +5 -4
  11. package/adapters/opencode/GLOBAL-HARNESS-SPEC.md +11 -6
  12. package/adapters/opencode/HOW-OPENCODE-LOADS.md +1 -1
  13. package/adapters/opencode/README.md +15 -9
  14. package/adapters/opencode/agents/orchestrator.md +1 -1
  15. package/adapters/opencode/plugins/geneseed-context.js +7 -7
  16. package/adapters/opencode/plugins/geneseed-guard.js +6 -6
  17. package/adapters/opencode/workflows/dispatch.js +1 -1
  18. package/bin/geneseed.mjs +72 -6
  19. package/docs/extending.md +164 -55
  20. package/docs/limits.md +4 -4
  21. package/docs/token-footprint.md +15 -4
  22. package/docs/web/authoring.md +1 -1
  23. package/docs/web/claude-hooks.md +2 -2
  24. package/docs/web/collaboration.md +3 -3
  25. package/docs/web/first-session.md +2 -2
  26. package/docs/web/footprint.md +14 -7
  27. package/docs/web/foreman-mode.md +2 -2
  28. package/docs/web/harnesses.md +5 -3
  29. package/docs/web/install-quick.md +1 -1
  30. package/docs/web/model.md +3 -3
  31. package/docs/web/plugin-context.md +2 -2
  32. package/docs/web/plugin-guard.md +3 -3
  33. package/docs/web/plugin-ponytail.md +1 -1
  34. package/docs/web/rules.md +23 -3
  35. package/docs/web/setup-choices.md +20 -7
  36. package/docs/web/themes-author.md +13 -1
  37. package/docs/web-ui.md +36 -12
  38. package/harness.config.json +1 -1
  39. package/js/catalog.mjs +29 -4
  40. package/js/checkout.mjs +24 -1
  41. package/js/cli-table.json +8 -6
  42. package/js/diff.mjs +8 -3
  43. package/js/doctor.mjs +307 -4
  44. package/js/emit.mjs +48 -11
  45. package/js/generate.mjs +39 -5
  46. package/js/hooks.mjs +8 -7
  47. package/js/installs.mjs +101 -4
  48. package/js/inventory.mjs +150 -21
  49. package/js/migrate.mjs +10 -4
  50. package/js/render.mjs +110 -7
  51. package/js/settings.mjs +218 -27
  52. package/js/setup.mjs +95 -5
  53. package/js/status.mjs +45 -4
  54. package/js/tui.mjs +44 -1
  55. package/js/uninstall.mjs +25 -6
  56. package/js/web/actions.mjs +56 -5
  57. package/js/web/api.mjs +102 -16
  58. package/js/web/docs.mjs +33 -5
  59. package/js/web/jobs.mjs +12 -3
  60. package/js/web/server.mjs +6 -0
  61. package/package.json +1 -1
  62. package/src/AGENT.md.tmpl +75 -65
  63. package/src/agents/_template.md +4 -4
  64. package/src/agents/advocate.md +4 -4
  65. package/src/agents/architect.md +2 -2
  66. package/src/agents/developer.md +5 -5
  67. package/src/agents/docs.md +2 -2
  68. package/src/agents/empiricist.md +3 -3
  69. package/src/agents/explorer.md +3 -3
  70. package/src/agents/framer.md +3 -3
  71. package/src/agents/historian.md +3 -3
  72. package/src/agents/operator.md +3 -3
  73. package/src/agents/pragmatist.md +3 -3
  74. package/src/agents/reviewer.md +2 -2
  75. package/src/agents/security.md +2 -2
  76. package/src/agents/skeptic.md +4 -4
  77. package/src/agents/steward.md +3 -3
  78. package/src/agents/tester.md +2 -2
  79. package/src/agents/user-advocate.md +3 -3
  80. package/src/agents/visionary.md +3 -3
  81. package/src/doctrines/README.md +41 -0
  82. package/src/doctrines/craft.md +51 -0
  83. package/src/doctrines/ops.md +61 -0
  84. package/src/doctrines/process.md +87 -0
  85. package/src/doctrines/rigor.md +52 -0
  86. package/src/laws/universal.md +17 -336
  87. package/src/memory/README.md +2 -2
  88. package/src/modes/README.md +2 -2
  89. package/src/modes/foreman.md +1 -1
  90. package/src/notebook/README.md +2 -2
  91. package/src/ontology/universal.md +89 -0
  92. package/src/postures/README.md +3 -3
  93. package/src/postures/artisan.md +3 -3
  94. package/src/postures/assistant.md +1 -1
  95. package/src/postures/expert.md +1 -1
  96. package/src/postures/mentor.md +1 -1
  97. package/src/postures/peer.md +2 -2
  98. package/src/skills/_template.md +2 -2
  99. package/src/skills/brainstorm.md +2 -2
  100. package/src/skills/clarify.md +2 -2
  101. package/src/skills/codebase-design.md +1 -1
  102. package/src/skills/commit.md +5 -5
  103. package/src/skills/council.md +4 -4
  104. package/src/skills/crash-course.md +1 -1
  105. package/src/skills/daydream/SKILL.md +1 -1
  106. package/src/skills/debug.md +2 -2
  107. package/src/skills/decode.md +1 -1
  108. package/src/skills/develop.md +6 -6
  109. package/src/skills/document-project.md +4 -4
  110. package/src/skills/domain-modeling.md +1 -1
  111. package/src/skills/drill.md +1 -1
  112. package/src/skills/feynman.md +1 -1
  113. package/src/skills/forge-mcp.md +3 -3
  114. package/src/skills/fresh-eyes.md +4 -4
  115. package/src/skills/frontend-design.md +1 -1
  116. package/src/skills/gap-detector.md +1 -1
  117. package/src/skills/geneseed-code-review.md +1 -1
  118. package/src/skills/geneseed.md +4 -4
  119. package/src/skills/git-archaeology.md +1 -1
  120. package/src/skills/git-rescue.md +3 -3
  121. package/src/skills/handoff.md +4 -4
  122. package/src/skills/herdr.md +1 -1
  123. package/src/skills/ingest.md +3 -3
  124. package/src/skills/learning-path.md +1 -1
  125. package/src/skills/migrate.md +5 -5
  126. package/src/skills/opencode-theme.md +1 -1
  127. package/src/skills/parallel-agents.md +2 -2
  128. package/src/skills/pipeline.md +2 -2
  129. package/src/skills/plan.md +2 -2
  130. package/src/skills/ponytail.md +1 -1
  131. package/src/skills/profile.md +1 -1
  132. package/src/skills/prose.md +2 -2
  133. package/src/skills/prototype.md +2 -2
  134. package/src/skills/refactor.md +2 -2
  135. package/src/skills/release.md +4 -4
  136. package/src/skills/repo-map.md +5 -5
  137. package/src/skills/research.md +6 -6
  138. package/src/skills/review-response.md +3 -3
  139. package/src/skills/roast-me.md +1 -1
  140. package/src/skills/rule.md +5 -5
  141. package/src/skills/ship.md +3 -3
  142. package/src/skills/tdd.md +2 -2
  143. package/src/skills/tickets.md +1 -1
  144. package/src/skills/wayfinder.md +1 -1
  145. package/src/skills/wiki.md +4 -4
  146. package/src/skills/workflow.md +2 -2
  147. package/themes/README.md +17 -7
  148. package/themes/_TEMPLATE.json +37 -36
  149. package/themes/biker.json +37 -36
  150. package/themes/commentator.json +37 -36
  151. package/themes/cyberpunk.json +37 -36
  152. package/themes/gamer.json +37 -36
  153. package/themes/imperial.json +37 -36
  154. package/themes/joker.json +37 -36
  155. package/themes/marvin.json +37 -36
  156. package/themes/mean.json +37 -36
  157. package/themes/military.json +37 -36
  158. package/themes/neutral.json +37 -36
  159. package/themes/pirate.json +37 -36
  160. package/themes/sports.json +37 -36
  161. package/themes/verstappen.json +37 -36
  162. package/themes/wizard.json +37 -36
  163. package/web/dist/assets/{About-BYXlmbyo.js → About-_MnJcwb_.js} +1 -1
  164. package/web/dist/assets/{Activity-us5MPf4u.js → Activity-DpjqO-FR.js} +1 -1
  165. package/web/dist/assets/{ActivityDetail-CiaQXUk2.js → ActivityDetail-Bvrjv8oW.js} +1 -1
  166. package/web/dist/assets/{Diff-Bh0qbZ2s.js → Diff-DZ0IehBV.js} +1 -1
  167. package/web/dist/assets/{Doctor-BIlQME45.js → Doctor-cu4krooO.js} +1 -1
  168. package/web/dist/assets/{Graph-4dYD1dPG.js → Graph-DUOPY65j.js} +1 -1
  169. package/web/dist/assets/{Harnesses-PzYZ3LOC.js → Harnesses-B5Osg-BF.js} +1 -1
  170. package/web/dist/assets/Laws-D8DJgTBp.js +3 -0
  171. package/web/dist/assets/{Library-CjMbksJW.js → Library-8y69I3_z.js} +1 -1
  172. package/web/dist/assets/{Markdown-Bk81c3aZ.js → Markdown-Ds5GS1OD.js} +1 -1
  173. package/web/dist/assets/{Profile-Curui_-j.js → Profile-7Jxa7ZUX.js} +1 -1
  174. package/web/dist/assets/{Rules-Bm283wdv.js → Rules-UZ0jWkjg.js} +1 -1
  175. package/web/dist/assets/{Skills-gruqYuhl.js → Skills-Bgkl0xLd.js} +1 -1
  176. package/web/dist/assets/{StatusBadge-EiM_FPqv.js → StatusBadge-Cx4caF_2.js} +1 -1
  177. package/web/dist/assets/{Themes-DU99j4Hr.js → Themes-De8az7Sv.js} +1 -1
  178. package/web/dist/assets/{activity-0rvQ6Vra.js → activity-VDAdVQ3q.js} +1 -1
  179. package/web/dist/assets/index-1pPSz7Kl.css +1 -0
  180. package/web/dist/assets/{index-0b2hobOo.js → index-BKrjBRV4.js} +1 -1
  181. package/web/dist/assets/index-C-gb-q-r.js +12 -0
  182. package/web/dist/assets/{index-IiGFmCW_.js → index-wNiNzFFQ.js} +1 -1
  183. package/web/dist/index.html +2 -2
  184. package/web/src/pages/Laws.jsx +401 -93
  185. package/web/dist/assets/Laws-DtZweXal.js +0 -1
  186. package/web/dist/assets/index-D5VY3TY_.js +0 -12
  187. package/web/dist/assets/index-DUCsDv7z.css +0 -1
package/bin/geneseed.mjs CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  // P5d moved these out of this file for the reason P5c moved the host resolvers: `harness
53
53
  // status` renders to count, so `bin/geneseed-cli.mjs` needs the same checkout paths and the
54
54
  // same cfg. golden.py's 259 cells all build one, which is what made the move safe.
55
- import { ROOT, CONFIG, THEMES, discoverNames, makeCfg } from '../js/checkout.mjs';
55
+ import { ROOT, CONFIG, THEMES, discoverNames, makeCfg, PACK_ORDER } from '../js/checkout.mjs';
56
56
 
57
57
  // P5f moved these, for the same arithmetic a third time: `harness rebuild-all` reads the
58
58
  // registry to find every install it must re-emit, and a CLI verb may not reach into a driver
@@ -122,7 +122,9 @@ export function resolveOut(raw) {
122
122
  * byte-for-byte by `tests/golden.py` (:316).
123
123
  */
124
124
  function configDefaults() {
125
- const d = { theme: 'neutral', posture: 'peer', mode: 'direct' };
125
+ const d = {
126
+ theme: 'neutral', posture: 'peer', mode: 'direct', doctrines: [...PACK_ORDER],
127
+ };
126
128
  if (!existsSync(CONFIG)) return d;
127
129
  try {
128
130
  const data = JSON.parse(readFileSync(CONFIG, 'utf8'));
@@ -130,6 +132,20 @@ function configDefaults() {
130
132
  if (data.theme !== undefined) d.theme = data.theme;
131
133
  if (data.posture !== undefined) d.posture = data.posture;
132
134
  if (data.mode !== undefined) d.mode = data.mode;
135
+ // `doctrines` is the only config key that is a LIST, so it is the only one that can be
136
+ // the right type and still nonsense. A bad value warns and falls back to all packs
137
+ // rather than dying: this file is hand-editable, and a typo here must not brick every
138
+ // rebuild of an install whose AGENT.md is otherwise fine. `[]` is a legitimate value
139
+ // (no packs) and must survive the check — hence an explicit Array test, not truthiness.
140
+ if (data.doctrines !== undefined) {
141
+ const known = discoverNames('doctrines', PACK_ORDER[0]);
142
+ if (Array.isArray(data.doctrines) && data.doctrines.every((x) => known.includes(x))) {
143
+ d.doctrines = PACK_ORDER.filter((p) => data.doctrines.includes(p));
144
+ } else {
145
+ process.stderr.write(`[geneseed] WARN: ${path.basename(CONFIG)} "doctrines" is not a `
146
+ + 'list of known pack names — using all packs.\n');
147
+ }
148
+ }
133
149
  }
134
150
  } catch {
135
151
  process.stderr.write(`[geneseed] WARN: ${path.basename(CONFIG)} is unreadable — `
@@ -146,6 +162,7 @@ function configDefaults() {
146
162
  */
147
163
  const VALUED = {
148
164
  '--theme': 'theme', '--posture': 'posture', '--mode': 'mode',
165
+ '--doctrines': 'doctrines',
149
166
  '--out': 'out', '--target': 'out', '--emit': 'emit',
150
167
  '--footprint': 'footprint', '--root': 'root',
151
168
  };
@@ -160,6 +177,13 @@ const FLAGS = {
160
177
  * A function and not a constant because two of the four read the checkout: `discoverNames`
161
178
  * scans `postures/` and `modes/`, so the answer depends on the source tree this driver is
162
179
  * standing in and cannot be frozen at import.
180
+ *
181
+ * `--doctrines` is DELIBERATELY not a fifth row. Every consumer of this table treats an
182
+ * entry as one value drawn from a closed list — `choice` refuses anything else, and
183
+ * `usage`'s metavar prints the list as the whole legal surface. `--doctrines` takes a comma
184
+ * LIST plus the sentinel `none`, so a row here would have refused `craft,rigor` outright and
185
+ * advertised a metavar that lies. It is validated by `parseDoctrines` instead and prints the
186
+ * plain `DOCTRINES` metavar, exactly as `--theme` (also open-ended) already does.
163
187
  */
164
188
  function choicesFor() {
165
189
  return {
@@ -170,6 +194,33 @@ function choicesFor() {
170
194
  };
171
195
  }
172
196
 
197
+ /**
198
+ * `--doctrines craft,rigor` / `--doctrines none` -> the cfg's pack array.
199
+ *
200
+ * Normalised into `PACK_ORDER` rather than kept in the order typed, which also dedupes: the
201
+ * rendered `Active packs:` line is a MARKER a later reader parses back out of a deployed
202
+ * carrier, and a marker whose contents depend on argument order is one that compares unequal
203
+ * to itself. Validation is against DISCOVERY, not `PACK_ORDER`, so the refusal names the
204
+ * packs the checkout actually has; a discovered pack missing from `PACK_ORDER` is a
205
+ * different fault and `js/render.mjs` refuses the whole build for it.
206
+ */
207
+ function parseDoctrines(value) {
208
+ const s = value.trim();
209
+ const known = discoverNames('doctrines', PACK_ORDER[0]);
210
+ if (s === 'none') return [];
211
+ const names = s.split(',').map((x) => x.trim()).filter(Boolean);
212
+ if (!names.length) {
213
+ die(2, "argument --doctrines: expected a comma-separated list of packs, or 'none'");
214
+ }
215
+ for (const n of names) {
216
+ if (!known.includes(n)) {
217
+ die(2, `argument --doctrines: invalid choice: '${n}' (choose from `
218
+ + `${known.map((c) => `'${c}'`).join(', ')}, or 'none')`);
219
+ }
220
+ }
221
+ return PACK_ORDER.filter((p) => names.includes(p));
222
+ }
223
+
173
224
  /**
174
225
  * `-h/--help` — the flag argparse handed the reference for free and this hand-rolled parser
175
226
  * did not, so the two entry points disagreed: the reference printed usage and exited 0 while
@@ -212,6 +263,7 @@ function usage() {
212
263
  function parseArgs(argv, defaults) {
213
264
  const args = {
214
265
  theme: defaults.theme, posture: defaults.posture, mode: defaults.mode,
266
+ doctrines: defaults.doctrines,
215
267
  out: 'Harness', emit: 'files', footprint: 'lean', root: null,
216
268
  syncThemes: false, validateOnly: false, verbose: false,
217
269
  };
@@ -241,6 +293,9 @@ function parseArgs(argv, defaults) {
241
293
  }
242
294
  const choices = choicesFor();
243
295
  for (const flag of Object.keys(choices)) choice(flag, args[VALUED[flag]], choices[flag]);
296
+ // Only when the flag was actually PASSED is this a string; left at its default it is
297
+ // already the array `configDefaults` built, and re-parsing an array would stringify it.
298
+ if (typeof args.doctrines === 'string') args.doctrines = parseDoctrines(args.doctrines);
244
299
  return args;
245
300
  }
246
301
 
@@ -897,9 +952,14 @@ const PROJECT_EMITS = {
897
952
  opencode: emitOpencode,
898
953
  };
899
954
 
900
- export function emitProjectInto(host, { theme, out, root, footprint = 'full' }) {
955
+ export function emitProjectInto(host, { theme, out, root, footprint = 'full', doctrines = null }) {
901
956
  const emit = PROJECT_EMITS[host];
902
- return withPyNewlines(() => emit(makeCfg(), { theme, footprint, root }, out));
957
+ // `doctrines` rides the same rail posture and mode ride into `emitGlobalInto`, and for the
958
+ // same reason: a drift reader that renders `expected` at the DEFAULT pack set reports the
959
+ // whole doctrines section as edited on every narrowed install. `null` means "no opinion" and
960
+ // `makeCfg` renders all four, which is what every validation caller wants.
961
+ return withPyNewlines(() => emit(makeCfg(doctrines ? { doctrines } : {}),
962
+ { theme, footprint, root }, out));
903
963
  }
904
964
 
905
965
  /**
@@ -917,7 +977,7 @@ export function buildInto({ theme, out, footprint = 'lean' }) {
917
977
  }
918
978
 
919
979
  export function emitGlobalInto(host, {
920
- theme, out, cfgDir, footprint, posture = null, mode = null,
980
+ theme, out, cfgDir, footprint, posture = null, mode = null, doctrines = null,
921
981
  }) {
922
982
  // `build.HOSTS.get(host, build.HOSTS["opencode"])` — an unknown host falls back rather than
923
983
  // raising, because the host comes from a marker file a user can edit.
@@ -935,7 +995,13 @@ export function emitGlobalInto(host, {
935
995
  // that chose a register — the same scar the footprint left in `diffCollect`, one axis over.
936
996
  return withPyNewlines(
937
997
  () => emit(
938
- makeCfg({ posture: posture || 'peer', mode: mode || 'direct' }),
998
+ // THE PACK SELECTION RIDES THE SAME RAIL, with one difference: `doctrinesOfDir` answers
999
+ // `null` for "no marker / undetectable", and that must render at ALL packs rather than
1000
+ // at `[]`, so the key is OMITTED instead of passed through — `makeCfg`'s own default is
1001
+ // the fail-closed answer and an explicit `doctrines: null` would not reach it.
1002
+ makeCfg({
1003
+ posture: posture || 'peer', mode: mode || 'direct', ...(doctrines ? { doctrines } : {}),
1004
+ }),
939
1005
  { theme, footprint, root: null, cfgDir }, out,
940
1006
  ),
941
1007
  );
package/docs/extending.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Extending — what it costs to change this tool
2
2
 
3
3
  `docs/limits.md` is the standing answer to *"what does this tool not prove about itself?"*.
4
- This file is its companion: **"what does it cost to add one thing?"** — one law, one skill, one
5
- plugin, one hook verb, one doc page, one theme, one screen.
4
+ This file is its companion: **"what does it cost to add one thing?"** — one invariant, one
5
+ doctrine rule, one skill, one plugin, one hook verb, one doc page, one theme, one screen.
6
6
 
7
7
  It exists because the answer is never one file. Every user-visible noun in Geneseed has
8
8
  *satellites* — a themed title in fourteen voices, a class in a hand-written map, a count in a
@@ -10,6 +10,11 @@ badge, a row in a ledger, a page in the console — and the gates that keep them
10
10
  across `doctor`, the Node suite and four CI jobs. A contributor who knows the satellites ships in
11
11
  one pass; one who does not discovers them one red gate at a time.
12
12
 
13
+ The governance surface is three tiers and they cost very different amounts. **Adding a doctrine
14
+ rule is the cheap, common case** (§2a); **amending an invariant** is next (§2b); **adding an
15
+ invariant** is the most expensive single edit in the repo after the CLI surface (§2c); **adding a
16
+ whole pack** is rare and mechanical (§2d).
17
+
13
18
  Written after the Python migration completed (2026-08-18), against `main`. Verified: every
14
19
  file:line below was opened, and the commands were run.
15
20
 
@@ -35,11 +40,22 @@ Two rules follow from the table and they cause most of the surprises:
35
40
 
36
41
  ### Counts are computed — never type one
37
42
 
38
- `{N_LAWS}` `{N_AGENTS}` `{N_SKILLS}` `{N_PLUGINS}` are substituted at request time from the live
39
- inventory (`js/web/docs.mjs:169-172`). Typing a literal number into a `docs/web` page is caught by
40
- `proseMirrorProblems` (`js/doctor.mjs:715-737`). The README badges and prose, and the
41
- `N laws, N agents, N skills` line in `SHIPPED.md`, are the hand-written exceptions — and each has
42
- its own doctor arm (`js/doctor.mjs:676`, `:739`, `:822-829`).
43
+ Eight tokens are substituted at request time from the live inventory, by `docCounts` in
44
+ `js/web/docs.mjs`: `{N_LAWS}` `{N_AGENTS}` `{N_SKILLS}` `{N_PLUGINS}`, plus the four the
45
+ three-tier split added — `{N_ONTOLOGY}` (the ontology's sections), `{N_PACKS}` (packs that exist),
46
+ `{N_PACKS_ACTIVE}` (packs this install built in) and `{N_DOCTRINE_RULES}` (rules inside the active
47
+ packs). `{N_LAWS}` is the **invariant** count and nothing else; there is deliberately no token
48
+ meaning "the whole constitution", because `proseMirrorProblems` holds README and `SHIPPED.md`
49
+ prose to that same number and a token that quietly meant something wider would put the docs and
50
+ the gate at odds with no way to re-bless either.
51
+
52
+ ⚠ **Only a `kind: "concept"` page is substituted.** A `markdown`-kind page renders `{N_PACKS}`
53
+ literally, on the screen, as those nine characters.
54
+
55
+ Typing a literal number into a `docs/web` page is caught by `proseMirrorProblems`. The README
56
+ badges and prose, and the `N laws, N agents, N skills` line in `SHIPPED.md`, are the hand-written
57
+ exceptions — and each has its own arm in `js/doctor.mjs` (`countTableProblems` walks the five
58
+ badge keys `agents`/`skills`/`laws`/`themes`/`plugins`; `proseMirrorProblems` holds the prose).
43
59
 
44
60
  ---
45
61
 
@@ -128,18 +144,38 @@ Notes that matter:
128
144
 
129
145
  ---
130
146
 
131
- ## 2 — Adding a LAW
147
+ ## 2 — Changing the constitution
132
148
 
133
- Ten files, of which five are pure satellites. Ground truth: `git show --stat 1b53701` (law XXXVIII)
134
- touched 21 files, 14 of them themes.
149
+ Three tiers, four different costs. The addresses are `{{LAW}} <roman>` for an invariant and
150
+ `{{DOCTRINE}} <pack> <n>` for a doctrine rule; the ontology is cited by section name and adding a
151
+ section to it is not a checklist, it is a design change.
135
152
 
136
- 1. `src/laws/universal.md` — **append** `### {{LAW}} <roman> — {{LEX_<roman>}}` plus the body.
137
- Never insert: 397 `{{LAW}} <numeral>` cross-references live in `src/`, plus hardcoded citations
138
- in `js/hooks.mjs` and two OpenCode plugins, and **nothing resolves a cross-reference against the
139
- canon**. Renumbering silently rewires every one of them.
140
- 2. `themes/_TEMPLATE.json` — add `LEX_<roman>` in template order. **Nothing gates this file**
141
- (`themeFiles()` filters `_`-prefixed names, `js/installs.mjs:113`), so forgetting it is green
142
- today and starves the *next* law's sync.
153
+ To count the cross-references either tier already carries, from the repo root:
154
+
155
+ ```bash
156
+ grep -rhoE "\{\{LAW\}\} [IVXL]+" src/ | wc -l
157
+ grep -rhoE "\{\{DOCTRINE\}\} [a-z]+ [0-9]+" src/ | wc -l
158
+ ```
159
+
160
+ On this checkout that reports 153 and 209. Both numbers move on every wave of editing, which is
161
+ why the commands are here and the answers are not load-bearing.
162
+
163
+ ### 2a — Adding a DOCTRINE RULE (the common case)
164
+
165
+ Five files, none of them a count. This is the slot most new material belongs in, and it is cheap
166
+ on purpose so that the invariant slot stays scarce.
167
+
168
+ 1. `src/doctrines/<pack>.md` — **append** `### {{DOCTRINE}} <pack> <n> — {{DOC_<PACK>_<n>}}` plus
169
+ the body. Two gates in `constitutionProblems` (`js/doctor.mjs`) sit on this: the pack named in
170
+ the *heading* is what addresses the rule (not the filename, so a rule filed in the wrong file is
171
+ reported rather than silently renamed), and ids must run **contiguously from 1**, so appending
172
+ at the end is the only place `n` is free. Packs are numbered independently — nothing outside
173
+ this file renumbers.
174
+ 2. `themes/_TEMPLATE.json` — add `DOC_<PACK>_<n>`. ⚠ **This file *is* gated for this key family.**
175
+ `themeFiles()` filters `_`-prefixed names and theme parity never sees the template, but
176
+ `constitutionProblems` reads it alongside the fourteen voices for `DOC_*` and `LEX_*`
177
+ specifically — because it is what `--sync-themes` seeds the *next* voice from. Every other key
178
+ family in that file is still ungated.
143
179
  3. Seed the fourteen themes:
144
180
 
145
181
  ```bash
@@ -147,37 +183,110 @@ touched 21 files, 14 of them themes.
147
183
  ```
148
184
 
149
185
  ⚠ Four spellings of this command are in circulation and two of them fail — `doctor`'s own tip
150
- is one of the two. See §7.2 before you copy a command from anywhere else.
151
- 4. Restyle all fourteen by hand. `--sync-themes` writes the template's `<themed title for …>`
152
- placeholder and only *prints* `RESTYLE these`; a shipped placeholder passes every gate.
153
- 5. `js/inventory.mjs:40` — `LAW_CLASS`, one of the six in `LAW_CLASSES` (`:56`).
154
- 6. `web/src/pages/Laws.jsx:28` — `LAW_META`: same class, plus the one-line Principle. **This copy
155
- exists nowhere else**; a law absent here renders with a blank description. It is why that one
156
- React file ships alone out of `web/src` in `package.json` — `doctor` regex-parses its literal.
157
- 7. `README.md` — badge `badge/laws-N` and the `N universal laws` sentence.
158
- 8. `SHIPPED.md` — the `N laws, N agents, N skills` triple.
159
- 9. `tests/unit/harness.test.mjs:293` — the negative fixture hardcodes the *next* free numeral;
160
- bump it or it stops being a fixture and starts being a duplicate.
161
- 10. `CHANGELOG.md`, then rebuild and commit `web/dist` (step 6 touched `web/src`).
162
-
163
- **Amending** a law is steps 1, 10 and — if the principle moved — 6. Two extra cautions:
186
+ is one of the two. See §7.2 before you copy a command from anywhere else. Then restyle all
187
+ fourteen by hand: `--sync-themes` writes the template's placeholder and only *prints*
188
+ `RESTYLE these`; a shipped placeholder passes every gate.
189
+ 4. `web/src/pages/Laws.jsx` — a `DOCTRINE_META` row keyed `'<pack>.<n>'`. Its first field is the
190
+ **pack**, not one of `LAW_CLASSES`' six; `doctrineMetaProblems` requires an *equality* between
191
+ that field and the key's own pack, which catches the copy error a six-way vocabulary cannot — a
192
+ row pasted from the pack above it, keeping the wrong pack. Its second field is the one-line
193
+ Principle, and **that copy exists nowhere else**; a rule absent here renders with a blank
194
+ description.
195
+ 5. `CHANGELOG.md`, then rebuild and commit `web/dist` (step 4 touched `web/src`).
196
+
197
+ **What a doctrine rule does *not* cost**, and each of these is a deliberate asymmetry:
198
+
199
+ - **No class entry.** There is no `LAW_CLASS` analogue: a doctrine rule's class *is* its pack, and
200
+ there is no second taxonomy over it.
201
+ - **No count, anywhere.** No badge, no `SHIPPED.md` triple, no prose mirror. The console spends
202
+ `{N_PACKS}` / `{N_PACKS_ACTIVE}` / `{N_DOCTRINE_RULES}`, all computed at request time.
203
+ - **No test fixture to bump**, and no renumber risk.
204
+
205
+ ⚠ **Cite only downward.** An always-on tier may never reference a toggleable one:
206
+ `constitutionProblems` refuses a `{{DOCTRINE}}` token anywhere under `src/ontology/` or
207
+ `src/laws/`, because a `--doctrines craft` build would then ship an invariant pointing at text its
208
+ `AGENT.md` does not contain. Doctrine→doctrine citations are fine and stay — every pack file ships
209
+ on disk whether or not it was built in. If the build's own default is narrowed,
210
+ `constitutionProblems` emits a `[note]` naming every citation that points at a shelved pack; notes
211
+ are printed and **not** counted, because a narrowed build is a configuration its owner chose, not
212
+ a defect.
213
+
214
+ ### 2b — Amending an INVARIANT
215
+
216
+ Steps 1 and 9 of §2c, plus `LAW_META` if the Principle line moved. Two cautions:
164
217
 
165
218
  - `--footprint` defaults to **lean**, and lean keeps only the heading plus the first sentence
166
- (`js/render.mjs:167`). A clause added to the second paragraph is invisible in the default build.
167
- Amend the first sentence, or the amendment does not exist for most installs.
168
- - ⚠ **Law I is frozen.** `LEX_I` is one of seven theme keys recorded byte-for-byte in the
169
- primitives corpus (§5). Retitling Law I in any theme reddens a snapshot that cannot be re-blessed.
219
+ (`terseBlocks` in `js/render.mjs`). A clause added to the second paragraph is invisible in the
220
+ default build. Amend the first sentence, or the amendment does not exist for most installs. The
221
+ same is true of a doctrine rule; it is **not** true of the ontology, which ships whole at both
222
+ footprints.
223
+ - ⚠ **Rule I is frozen.** `LEX_I` is one of seven theme keys recorded byte-for-byte in the
224
+ primitives corpus (§5.2). Retitling Rule I in any theme reddens a snapshot that cannot be
225
+ re-blessed.
226
+
227
+ ### 2c — Adding an INVARIANT
228
+
229
+ Nine steps, and only the first is the rule itself. This is the expensive one, and it should be:
230
+ the bar is in `DESIGN.md` Decision 7, and most candidates belong in a doctrine pack instead.
170
231
 
171
- The carrier itself is gated: `tests/unit/emit_smoke.test.mjs:211-265` checks every law reaches
172
- every one of nine emit modes in order and themed, under both footprints, with a ceiling **and** a
173
- floor on the rendered size. Headroom measured today: ~2 100 chars full, ~1 200 lean — about three
174
- average laws at full footprint, which is the binding one.
232
+ 1. `src/laws/universal.md` — **append** `### {{LAW}} <roman> — {{LEX_<roman>}}` plus the body.
233
+ Never insert: the `{{LAW}}` cross-references counted above live all over `src/`, plus hardcoded
234
+ citations in `js/hooks.mjs` and two OpenCode plugins, and **nothing resolves a cross-reference
235
+ against the canon**. Renumbering silently rewires every one of them.
236
+ 2. `themes/_TEMPLATE.json` — add `LEX_<roman>` in template order. Gated, as in §2a step 2:
237
+ `constitutionProblems` holds `LEX_I..LEX_IX` as an **equality** across all fifteen files (the
238
+ fourteen voices and the template), so both a missing key *and* a leftover one are reported. That
239
+ arm exists because the renumber left `LEX_XXII`, `LEX_XXIII`, `LEX_XXIV` and `LEX_XXXVI` behind
240
+ in every file and parity was silent — they were absent from nowhere.
241
+ 3. `--sync-themes`, then restyle all fourteen by hand (§2a step 3).
242
+ 4. `LAW_CLASS` in `js/inventory.mjs` — one of the six in `LAW_CLASSES` beside it.
243
+ 5. `LAW_META` in `web/src/pages/Laws.jsx` — same class, plus the one-line Principle. Note the map
244
+ is keyed by **arabic** number where `universal.md` heads with a Roman numeral. **This copy
245
+ exists nowhere else**; a rule absent here renders with a blank description. It is why that one
246
+ React file ships alone out of `web/src` in `package.json` — `doctor` regex-parses its literal.
247
+ 6. `README.md` — badge `badge/laws-N` and the `N universal laws` sentence.
248
+ 7. `SHIPPED.md` — the `N laws, N agents, N skills` triple.
249
+ 8. `tests/unit/harness.test.mjs` — the negative fixture for the `LAW_CLASS` gate plants a rule at
250
+ the *first free numeral*, which is `X` now that the invariants are I..IX. Bump it, or it stops
251
+ being a fixture and starts being a duplicate.
252
+ 9. `CHANGELOG.md`, then rebuild and commit `web/dist` (step 5 touched `web/src`).
253
+
254
+ ⚠ While restyling, `{{LAW}}` must stay a **single word** in every theme (and so must
255
+ `{{DOCTRINE}}`). Both heading parsers match the tier noun with `\S+`, so a two-word value does not
256
+ error — the heading simply stops matching and the whole tier parses to nothing, in silence.
257
+ `constitutionProblems` refuses one, which is the only reason it is visible at all.
258
+
259
+ The carrier itself is gated: `tests/unit/emit_smoke.test.mjs` checks every rule reaches every one
260
+ of nine emit modes in order and themed, under both footprints, with a `CEILING` **and** a floor
261
+ (half the ceiling) on the rendered size. Its docblock carries the current measurement: headroom is
262
+ 1,522 characters (2.8%) at full and 1,794 (4.8%) at lean, against the largest carrier — the
263
+ host-agnostic `files` bundle. Read that docblock before adding text to any tier; it is
264
+ re-measured, never nudged, and the ceiling is measured **with all four packs active**, which is
265
+ the only configuration it can speak for.
266
+
267
+ ### 2d — Adding a whole PACK
268
+
269
+ Rare, and mechanical. On top of §2a for each rule the pack carries:
270
+
271
+ 1. `src/doctrines/<pack>.md`, opening with its lead line — `**{{PACK_<NAME>}}** — how … .` A pack
272
+ file `PACK_ORDER` names but cannot read, or one carrying no `### {{DOCTRINE}} <pack> <n>`
273
+ heading at all, is a refusal.
274
+ 2. `PACK_ORDER` in `js/checkout.mjs` — the registration, and the render order. It is **narrative,
275
+ not alphabetical** (craft → rigor → ops → process); discovery sorts alphabetically and the
276
+ renderer refuses a `.md` under `src/doctrines/` that `PACK_ORDER` does not name, rather than
277
+ skipping it.
278
+ 3. `PACK_<NAME>` in `themes/_TEMPLATE.json` and all fourteen voices. Unlike `DOC_*`, this family is
279
+ held only by `themeParityProblems` — presence, across the voices, template excluded.
280
+ 4. `DOCTRINE_BLURBS` in `js/setup.mjs` — the one-line description an installer sees in the wizard,
281
+ and the only place a pack is ever explained to them. Missing entries fall back to the bare name.
282
+ 5. `harness.config.json` if the pack should be on by default; `constitutionProblems` refuses a
283
+ `doctrines` array naming a pack this checkout does not ship.
175
284
 
176
285
  ---
177
286
 
178
287
  ## 3 — Adding a SKILL or an AGENT
179
288
 
180
- **Order matters at the first two steps.** `missingReferencedSpecs` (`js/emit.mjs:493`) *aborts the
289
+ **Order matters at the first two steps.** `missingReferencedSpecs` (in `js/emit.mjs`) *aborts the
181
290
  build* when `AGENT.md.tmpl` names a spec with no file — so the file comes before the table row, or
182
291
  nothing builds at all. The refusal is clean: `tests/unit/emit_gates.test.mjs:265` proves a refused
183
292
  emit leaves the prior install intact.
@@ -185,14 +294,14 @@ emit leaves the prior install intact.
185
294
  1. `src/skills/<name>.md` or `src/agents/<name>.md`. The shape is load-bearing: `# {{SKILL}}: <name>`,
186
295
  a blank line, then a `>` blockquote holding `{{DESC_<NAME>}}` — title, blockquote, nothing
187
296
  between. `doctor` checks both the presence of the purpose line and that the blockquote is the
188
- *first* block (`js/doctor.mjs:899-908`).
297
+ *first* block (`authoringProblems`, via `firstBlockquote` and `descBlockProblem`).
189
298
  2. One row in the hand-authored table in `src/AGENT.md.tmpl` (agents `125-141`, skills `183-229`).
190
299
  Gated both ways: a row with no file, and a file with no row.
191
300
  3. `DESC_<NAME>` in **all fourteen** themes. Theme parity catches a missing one; nothing catches
192
301
  an unstyled placeholder.
193
- 4. **Skills only** — `SKILL_CLASS` in `js/inventory.mjs:59`. Gated both ways
194
- (`js/doctor.mjs:785-791`), which matters because the runtime fallback is silent: an unclassified
195
- skill renders as `build` (`js/inventory.mjs:166`).
302
+ 4. **Skills only** — `SKILL_CLASS` in `js/inventory.mjs`. Gated both ways, inside
303
+ `countTableProblems`, which matters because the runtime fallback is silent: an unclassified
304
+ skill renders as `build`.
196
305
  5. `registry.json` — a row keyed `skills/<name>` or `agents/<name>` with `status` ∈
197
306
  {`experimental`, `approved`, `deprecated`}, a semver `version`, a non-blank `owner`, an ISO
198
307
  `added`, and `last_verified: ""`. **This is the most forgettable step**: four hand-typed fields,
@@ -231,7 +340,7 @@ The `geneseed-` prefix and the `.js` suffix are mechanically load-bearing: the b
231
340
 
232
341
  1. `adapters/opencode/plugins/geneseed-<name>.js` — ESM, zero dependencies. The factory-export
233
342
  convention is real (OpenCode requires it) but **ungated**; the only generic gate on plugin
234
- source is `node --check` (`js/doctor.mjs:924-941`).
343
+ source is `node --check` (the one spawn in `authoringProblems`).
235
344
  2. `docs/web/plugin-<name>.md` — `group: plugins`, `title: "geneseed-<name>"`, `kind: "concept"`.
236
345
  3. `docs/web/plugins.md` — a bullet. The count above it renders from `{N_PLUGINS}`.
237
346
  4. `docs/web/model.md` — a second `{N_PLUGINS}` sentence whose **enumeration of the capabilities
@@ -396,8 +505,9 @@ only, at best, on its presence.
396
505
 
397
506
  | What | Where | Gated on |
398
507
  |---|---|---|
508
+ | `§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` |
399
509
  | Per-law Principle lines | `web/src/pages/Laws.jsx` | presence and class only, never accuracy |
400
- | 560 themed law titles (40 × 14) | `themes/*.json` | key presence only — a shipped placeholder is green |
510
+ | 504 themed constitution titles (36 keys × 14 voices) — `LEX_I..LEX_IX` (9), `DOC_<PACK>_<n>` (23), `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 |
401
511
  | The README keyword enumerations | `README.md`, `docs/web/rules.md` | nothing |
402
512
  | The plugin capability enumeration | `docs/web/model.md` | nothing (the number substitutes; the list does not) |
403
513
  | Section labels and page subtitles | `web/src/lib/sections.js`, `web/src/pages/Docs/index.jsx` | nothing |
@@ -540,20 +650,19 @@ to kill every pid on the port, then `geneseed web start`.
540
650
  Ordered by what unblocks the most work per unit of effort. Each is small; none is urgent enough to
541
651
  stop a change today, and all of them cost a contributor a wrong turn until they are done.
542
652
 
543
- 1. **Rewrite the authoring section of `DESIGN.md` (§7.1) and fix `doctor`'s tip (§7.2).** These are
544
- the two documents a contributor reads *first* when adding a law, and both name programs that no
545
- longer exist. Half an hour, and it removes the most likely first wrong turn.
546
- 2. **Extend the fenced-command gate to `DESIGN.md` and `docs/**/*.md` (§7.3).** One array literal.
547
- It would have caught item 1 the day it was written, and it keeps catching it.
548
- 3. **Decide the hook-verb question (§5.1) before it is asked under pressure.** Either mirror the
653
+ 1. **Extend the fenced-command gate to `DESIGN.md` and `docs/**/*.md` (§7.3).** One array literal.
654
+ Today it covers four documents, and both of the pointer-to-nothing defects §7 records — the
655
+ `DESIGN.md` authoring section and `doctor`'s theme-parity tip, since fixed — sat outside its
656
+ reach. It keeps catching them.
657
+ 2. **Decide the hook-verb question (§5.1) before it is asked under pressure.** Either mirror the
549
658
  CLI side's exemption list onto the hook half, or write down that the hook verb set is closed.
550
659
  Either answer is fine; discovering the question mid-feature is not.
551
- 4. **Write down the frozen-key list (§5.2) where a theme author will see it** — `themes/README.md`
660
+ 3. **Write down the frozen-key list (§5.2) where a theme author will see it** — `themes/README.md`
552
661
  is the natural home. Seven keys × fifteen files is a small enough surface to name explicitly,
553
662
  and today nothing warns you before the snapshot goes red.
554
- 5. **Gate `AGENT_LESSON_PROMPT` like its sibling (§7.7), and either gate or delete
663
+ 4. **Gate `AGENT_LESSON_PROMPT` like its sibling (§7.7), and either gate or delete
555
664
  `adapters/claude-code/settings.json` (§7.10).** Two silent failure modes, both cheap to close.
556
- 6. **Consider a `scripts` block in the root `package.json`.** There is currently no single command
665
+ 5. **Consider a `scripts` block in the root `package.json`.** There is currently no single command
557
666
  that runs what CI runs; every contributor reassembles the five by hand from this file. A
558
667
  `verify` script is three lines and makes §1 executable rather than descriptive.
559
668
 
package/docs/limits.md CHANGED
@@ -78,10 +78,10 @@ nothing else — **M1, M2, M3, M4 and M8** (`EMIT`, `EMIT_IMPERIAL`, `EMIT_CLAUD
78
78
  `CLI_CONTEXT`). All five are killed by `tests/unit/` alone, the control included, and each row now
79
79
  **declares** the unit file that kills it rather than a replay that can no longer run — M1 and M3
80
80
  `maintainer_tools`, M2 `node_driver`, M4 `hook_form`, M8 `hook_cli`. A gate a matrix names and
81
- cannot run guards nothing, and is worse than none, because it is trusted (law XXXVIII). For every
82
- defect the mutation matrix knows how to plant, the corpus was redundant. That is the whole
83
- argument, and it is the only one that licenses a deletion: a test goes because a living gate
84
- already proves the property, never because it is red.
81
+ cannot run guards nothing, and is worse than none, because it is trusted (Doctrine rigor 2,
82
+ Honest Tests). For every defect the mutation matrix knows how to plant, the corpus was
83
+ redundant. That is the whole argument, and it is the only one that licenses a deletion:
84
+ a test goes because a living gate already proves the property, never because it is red.
85
85
 
86
86
  **What was knowingly LOST: byte-level regression detection on emit, cli and web output.** Nothing
87
87
  now notices that a rendered bundle, a verb's stdout / stderr / exit code, or an HTTP response body
@@ -30,10 +30,21 @@ every host the same way instead of one silently falling behind.
30
30
 
31
31
  ## Where the tokens go
32
32
 
33
- - **The root file is ~76% of the bill**, and its Rules section alone is ~6.3k
34
- of the ~10.7k. The [footprint dial](#/docs/footprint) (`full` vs `lean`)
35
- exists precisely because this is the only lever that matters — trimming
36
- anything else is noise.
33
+ - **The root file is ~76% of the bill**, and the constitution inside it is the
34
+ bulk of that — no longer one section but three: the Ontology, the nine Rules,
35
+ and the doctrine packs this install built in. **Two** levers move it, not one.
36
+ The [footprint dial](#/docs/footprint) (`lean`, the default, keeps each Rule
37
+ and each doctrine rule as its heading plus first line, and ships the Ontology
38
+ *whole*; `full` inlines every rationale). And the
39
+ [doctrine packs](#/docs/setup-choices) — `geneseed-build --doctrines craft,rigor`
40
+ drops whole blocks of §2 at build time, which removes far more than the
41
+ footprint dial trims. Anything else is noise.
42
+
43
+ The three-tier split grew the root file: measured on `neutral`, `AGENT.md`
44
+ went from 31,457 bytes to 36,882 at `lean` (+17.2%) and 53,378 to 54,810 at
45
+ `full` (+2.7%). The lean growth is the larger one *by design* — the Ontology
46
+ is prose, and truncating four flowing sections to four orphan sentences would
47
+ have shipped a worldview nobody could read.
37
48
  - **Skill bodies (~53k) and agent bodies (~10k) are lazy** on every host —
38
49
  loaded only when invoked. A typical skill costs ≤3k per invocation; the
39
50
  heaviest (`react-view-transitions`, ~17.5k with its reference files) loads
@@ -4,7 +4,7 @@ order: 0
4
4
  title: "Edit the source"
5
5
  kind: "concept"
6
6
  ---
7
- Everything theme-independent lives under `src/` — laws, agents, skills, memory and notebook conventions, and the `AGENT.md.tmpl` entrypoint. Voice tokens live under `themes/` as one JSON file per theme. After editing, `geneseed build --emit opencode-global` (or `geneseed update`) re-renders the deployed bundle. The `doctor` action verifies the result: unresolved theme tokens, dead links, hermetic escapes, theme-key parity, and that the committed bundle matches a fresh render.
7
+ Everything theme-independent lives under `src/` — the constitution (`ontology/`, `laws/`, `doctrines/`), the `postures/` and `modes/` registers, agents, skills, memory and notebook conventions, and the `AGENT.md.tmpl` entrypoint. Voice tokens live under `themes/` as one JSON file per theme. After editing, `geneseed build --emit opencode-global` (or `geneseed update`) re-renders the deployed bundle. The `doctor` action verifies the result: unresolved theme tokens, dead links, hermetic escapes, theme-key parity, and that the committed bundle matches a fresh render.
8
8
 
9
9
  Each agent and skill also carries a lifecycle status — `experimental`, `approved` or `deprecated` — recorded in `registry.json` at the repo root alongside its version, owner and add-date. The file is maintainer-side: it is never rendered into a bundle, so a status costs no session context. Adding a spec means adding its row; `doctor` fails on a spec with no row and on a row with no spec, and the catalog badges anything that is not `approved`. The same pass refuses to let a credential ship, and holds every vendored skill folder to an immutable upstream commit rather than a moving branch.
10
10
 
@@ -9,8 +9,8 @@ Claude Code has no `instructions` array and no JS plugin dir — the harness rea
9
9
  ### The four hooks
10
10
 
11
11
  - **Context injection** — `SessionStart` (startup, clear, resume) runs `geneseed-hook context`: auto-discovers the repo's docs by convention and injects them before your first turn, plus your machine wiki. It honours the same `GENESEED_CONTEXT` manifest as the OpenCode context plugin.
12
- - **Git gate** — `PreToolUse` on Bash runs `geneseed-hook git-gate`: enforces the safety Laws at the tool boundary, before a shell command executes.
13
- - **Rule gate** — `PreToolUse` on the write tools runs `geneseed-hook rule-gate`: a write to `user-rules.md` or to a memory file asks first, because whether something you want kept is a standing rule or a durable fact is *your* call, settled through the [rule skill](#/docs/skills) (**Law VI**). Ordinary edits are untouched.
12
+ - **Git gate** — `PreToolUse` on Bash runs `geneseed-hook git-gate`: a `git commit` or `git push` anywhere in the command line — a compound one-liner and a `-C <path>` form included — asks first, at the tool boundary and before the shell runs it (**Doctrine process 5**). It is the one hook that rides a doctrine pack: an install built without **process** never gets this group, because a boundary that keeps asking for a rule the install did not adopt is a gate arguing with its own harness.
13
+ - **Rule gate** — `PreToolUse` on the write tools runs `geneseed-hook rule-gate`: a write to `user-rules.md` or to a memory file asks first, because whether something you want kept is a standing rule or a durable fact is *your* call, settled through the [rule skill](#/docs/skills) (**Doctrine process 1**). Ordinary edits are untouched.
14
14
  - **Learn** — `Stop` and `SubagentStop` run `geneseed-hook learn`: distils durable memories into the install's `memory/` store at session end; a subagent's stop routes to the per-agent lesson path (`memory/agents/<name>.md`).
15
15
 
16
16
  ### Where they live
@@ -4,7 +4,7 @@ order: 11
4
4
  title: "The collaboration layer"
5
5
  kind: "concept"
6
6
  ---
7
- Beyond the Laws, four mechanisms shape *how* the agent works with you — the register, the mutual contract, how memory binds, and who you are. All four are plain content in `AGENT.md` and its neighbours, so they ride to every host (OpenCode, Claude, Bob, Copilot) unchanged.
7
+ Beside the [constitution](#/docs/rules), a handful of mechanisms shape *how* the agent works with you — the two registers, the mutual contract, how memory binds, and who you are. All of them are plain content in `AGENT.md` and its neighbours, so they ride to every host (OpenCode, Claude, Bob, Copilot) unchanged.
8
8
 
9
9
  ### Postures — the register
10
10
 
@@ -16,7 +16,7 @@ A **mode** is the session's operating register — *how* work gets executed, as
16
16
 
17
17
  ### The Pact — a two-way contract
18
18
 
19
- Where the Laws bind the agent, the **Pact** binds the collaboration. It holds three co-equal protections (you, the truth, the agent) and — unusually — names what *you* owe back: don't punish candour that honours the pact, give context up front, decide when shown a fork. It is framing, not an enforced rule.
19
+ Where the Rules bind the agent, the **Pact** binds the collaboration — and it is not a layer beside them: it is stated inside the Ontology's **Telos**, the tier that is always in force, so it holds in every task and every repository. It carries three co-equal protections (you, the truth, the agent), never traded one for another, and — unusually — names what *you* owe back: don't punish candour that honours the pact, give context up front, decide when shown a fork. Your half is not enforceable against you; the agent's half is binding on it.
20
20
 
21
21
  ### Typed memory — binding force
22
22
 
@@ -24,4 +24,4 @@ A memory may carry an optional `force` — **constraint** (imposed, not the agen
24
24
 
25
25
  ### Your profile — identity
26
26
 
27
- `PROFILE.md`, seeded once beside `AGENT.md` and never overwritten, holds *who you are*: role, habits, register preferences. It is identity, not rules — it colours how the agent works but never binds (precedence is Laws, then `user-rules.md`, then the profile). Edit it here under the **Profile** tab, or in the file directly — or let the agent draft it: the [[profile]] skill interviews you (who you are, how you work, how you like answers pitched) and writes the file only with your consent, routing anything that is really a standing rule to `user-rules.md` instead.
27
+ `PROFILE.md`, seeded once beside `AGENT.md` and never overwritten, holds *who you are*: role, habits, register preferences. It is identity, not rules — it colours how the agent works but never binds (precedence is the Ontology and the Rules, then `user-rules.md`, then the active Doctrines, then the profile). Edit it here under the **Profile** tab, or in the file directly — or let the agent draft it: the [[profile]] skill interviews you (who you are, how you work, how you like answers pitched) and writes the file only with your consent, routing anything that is really a standing rule to `user-rules.md` instead.
@@ -8,7 +8,7 @@ Once installed, the agent doesn't change *how* you talk to your tool — it chan
8
8
 
9
9
  ### What loaded automatically
10
10
 
11
- - **`AGENT.md`** — {N_LAWS} universal Rules the agent obeys.
11
+ - **`AGENT.md`** — the [constitution](#/docs/rules): the Ontology's {N_ONTOLOGY} sections, {N_LAWS} always-on Rules, and the {N_DOCTRINE_RULES} doctrine rules from the packs this install built in.
12
12
  - **Your repo's docs** — `README.md`, `CONTRIBUTING.md`, anything under `docs/` the harness discovers.
13
13
  - **Your machine's wiki** (if you [set one up](#/docs/configure-wiki)) — eager entries and a lazy listing of the rest.
14
14
  - **The skill and agent catalogue** — invokable by name.
@@ -25,4 +25,4 @@ Skills are repeatable workflows; agents are capability specialists. You invoke t
25
25
 
26
26
  ---
27
27
 
28
- **Catalog:** [Skills](#/section/skills) · [Agents](#/section/agents) · [Rules](#/laws)
28
+ **Catalog:** [Skills](#/section/skills) · [Agents](#/section/agents) · [Constitution](#/laws)