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
@@ -34,7 +34,7 @@ import { readText } from '../lib/fs.mjs';
34
34
  import { parseJson } from '../lib/json.mjs';
35
35
  import { normcase, comparePaths } from '../lib/paths.mjs';
36
36
  // `js/build/source.mjs` imports nothing but node builtins, so this direction cannot cycle.
37
- import { PACK_ORDER, readPackText } from './source.mjs';
37
+ import { PACK_ORDER, readPackText, resolveRuleIds } from './source.mjs';
38
38
 
39
39
  /** Document STRUCTURE is theme-INDEPENDENT — mirrors `_build_render.STRUCTURE`. */
40
40
  export const STRUCTURE = {
@@ -216,7 +216,8 @@ function readSource(cfg, p) {
216
216
  let seen = SRC_TEXT.get(cfg);
217
217
  if (!seen) SRC_TEXT.set(cfg, (seen = new Map()));
218
218
  let text = seen.get(p);
219
- if (text === undefined) seen.set(p, (text = readText(p)));
219
+ // Rule ids resolve here, once per file, so every stage after sees the addressed form.
220
+ if (text === undefined) seen.set(p, (text = resolveRuleIds(readText(p), cfg.src)));
220
221
  return text;
221
222
  }
222
223
 
@@ -250,7 +251,9 @@ export function renderFile(cfg, filePath, theme, footprint = 'full', lawsPrefix
250
251
  function registerBody(cfg, theme, dir, selected, fallback) {
251
252
  for (const name of [selected, fallback]) {
252
253
  const p = path.join(cfg.src, dir, `${name}.md`);
253
- if (existsSync(p) && statSync(p).isFile()) return substitute(readText(p), theme).trim();
254
+ if (existsSync(p) && statSync(p).isFile()) {
255
+ return substitute(resolveRuleIds(readText(p), cfg.src), theme).trim();
256
+ }
254
257
  }
255
258
  return '';
256
259
  }
@@ -37,7 +37,7 @@ import path from 'node:path';
37
37
  * on raw or folded text, so one behaviour serves both.
38
38
  */
39
39
  const _packTexts = new Map();
40
- export function readPackText(file) {
40
+ export function readRawText(file) {
41
41
  const key = path.resolve(file);
42
42
  const mtime = statSync(file).mtimeMs;
43
43
  const hit = _packTexts.get(key);
@@ -47,6 +47,110 @@ export function readPackText(file) {
47
47
  return text;
48
48
  }
49
49
 
50
+ /**
51
+ * A doctrine pack as the render sees it: ids resolved, so the heading reads
52
+ * `### {{DOCTRINE}} process 5 — {{DOC_CONSENT_BEFORE_PUSH}}` again. Every reader that keys on the
53
+ * `<pack> <n>` address (`knownRuleIds`, the exclusion filter, the survives gate) goes through
54
+ * here and keeps working unchanged — the address is computed, only the source stopped typing it.
55
+ */
56
+ export function readPackText(file) {
57
+ return resolveRuleIds(readRawText(file), path.dirname(path.dirname(file)));
58
+ }
59
+
60
+ /**
61
+ * STABLE RULE IDS. Every law and doctrine rule is DECLARED by its heading —
62
+ * `### {{LAW:verify-before-asserting}} Verify Before Asserting` — and CITED by its id —
63
+ * `{{LAW:verify-before-asserting}}`. The id is permanent; the number is not stored anywhere. It
64
+ * is the rule's position (Roman in `laws/universal.md`, Arabic within its pack), computed here
65
+ * at render time, so removing or reordering a rule renumbers every heading after it and rewires
66
+ * nothing: no citation ever named the number.
67
+ *
68
+ * WHY CITATIONS RENDER AS THE NAME, NOT THE NUMBER. A citation is the one place a number would
69
+ * leak into text that outlives the build — an agent reads "Law III", writes "Law III" into its
70
+ * memory, and the memory is wrong the day the canon moves. The rendered harness models what it
71
+ * asks of the agent: a rule is cited by its principle, in English, identical in every voice. The
72
+ * number survives only where it is structural — the rule's own heading.
73
+ *
74
+ * Retired ids live in `RETIRED_RULE_IDS` so the doctor can refuse their reuse: an id that meant
75
+ * one thing in someone's notes must never come back meaning another.
76
+ */
77
+ const DECL_RE = /^### \{\{(LAW|DOCTRINE):([a-z0-9]+(?:-[a-z0-9]+)*)\}\}[ \t]+(\S[^\n]*?)[ \t]*$/gm;
78
+ const CITE_RE = /\{\{(LAW|DOCTRINE):([a-z0-9-]+)\}\}/g;
79
+ export const RULE_ID_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
80
+
81
+ /**
82
+ * Ids that no longer name a live rule, and must never be declared again. `liveAs` names the one
83
+ * tier that may still declare it — `external-gate` was Law IX before it became a rigor rule, so
84
+ * the doctrine keeps it and `{{LAW:external-gate}}` is refused; `null` means gone for good.
85
+ */
86
+ export const RETIRED_RULE_IDS = {
87
+ 'external-gate': { was: 'Law IX', liveAs: 'DOCTRINE', note: 'moved to the rigor pack' },
88
+ 'absence-is-a-claim': { was: 'Law XI', liveAs: null, note: 'folded into verify-before-asserting' },
89
+ };
90
+
91
+ const ROMAN = [[1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], [100, 'C'], [90, 'XC'],
92
+ [50, 'L'], [40, 'XL'], [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I']];
93
+ export function toRoman(n) {
94
+ let out = '';
95
+ for (const [v, r] of ROMAN) while (n >= v) { out += r; n -= v; }
96
+ return out;
97
+ }
98
+
99
+ /** `sealed-secrets` -> `LEX_SEALED_SECRETS`; a doctrine rule's title key is `DOC_<ID>`. */
100
+ export const titleKey = (kind, id) =>
101
+ `${kind === 'LAW' ? 'LEX' : 'DOC'}_${id.toUpperCase().replaceAll('-', '_')}`;
102
+
103
+ /** The files that may declare rules, in canon order: `[kind, pack|null, file]`. */
104
+ export function canonFiles(src = SRC) {
105
+ return [['LAW', null, path.join(src, 'laws', 'universal.md')],
106
+ ...PACK_ORDER.map((p) => ['DOCTRINE', p, path.join(src, 'doctrines', `${p}.md`)])];
107
+ }
108
+
109
+ const _canons = new Map();
110
+ /**
111
+ * Every declared rule, in render order: `{ kind, pack, id, name, n, label }`, where `label` is
112
+ * the address the heading renders (`III`, `process 5`). Memoised on the raw texts' identity —
113
+ * `readRawText` hands back the same string until a file's mtime moves.
114
+ */
115
+ export function ruleCanon(src = SRC) {
116
+ const files = canonFiles(src);
117
+ const texts = files.map(([, , f]) => { try { return readRawText(f); } catch { return ''; } });
118
+ const key = path.resolve(src);
119
+ const hit = _canons.get(key);
120
+ if (hit && hit.texts.every((t, i) => t === texts[i])) return hit.canon;
121
+ const rules = [];
122
+ files.forEach(([kind, pack], i) => {
123
+ let n = 0;
124
+ for (const m of texts[i].matchAll(DECL_RE)) {
125
+ if (m[1] !== kind) continue; // a wrong-tier declaration is the doctor's to name
126
+ n += 1;
127
+ rules.push({ kind, pack, id: m[2], name: m[3], n, label: kind === 'LAW' ? toRoman(n) : `${pack} ${n}` });
128
+ }
129
+ });
130
+ const byId = new Map();
131
+ for (const r of rules) if (!byId.has(r.id)) byId.set(r.id, r);
132
+ const canon = { rules, byId };
133
+ _canons.set(key, { texts, canon });
134
+ return canon;
135
+ }
136
+
137
+ /**
138
+ * Resolve every declaration and citation in `text` against the canon under `src`. An unknown id
139
+ * is left exactly as written — visible in the output, and named by the doctor — rather than
140
+ * guessed at.
141
+ */
142
+ export function resolveRuleIds(text, src = SRC) {
143
+ if (!text.includes('{{LAW:') && !text.includes('{{DOCTRINE:')) return text;
144
+ const { byId } = ruleCanon(src);
145
+ const known = (kind, id) => { const r = byId.get(id); return r && r.kind === kind ? r : null; };
146
+ return text
147
+ .replace(DECL_RE, (whole, kind, id) => {
148
+ const r = known(kind, id);
149
+ return r ? `### {{${kind}}} ${r.label} — {{${titleKey(kind, id)}}}` : whole;
150
+ })
151
+ .replace(CITE_RE, (whole, kind, id) => { const r = known(kind, id); return r ? `*${r.name}*` : whole; });
152
+ }
153
+
50
154
  /**
51
155
  * `_build_core.ROOT` — the checkout, from this file's own location (`js/..`).
52
156
  *
@@ -137,8 +137,13 @@ the project's own. Follow the root \`AGENTS.md\`.
137
137
  /** `_build_render.PROFILE_FILE`. */
138
138
  const PROFILE_FILE = 'PROFILE.md';
139
139
 
140
- /** `_build_render.PROFILE_STUB`. */
141
- const PROFILE_STUB = `\
140
+ /**
141
+ * `_build_render.PROFILE_STUB`.
142
+ *
143
+ * Exported for the web console: `/api/profile` answers `seeded: true` while PROFILE.md is
144
+ * still byte-for-byte this text, which is how the Overview knows to ask for it to be filled in.
145
+ */
146
+ export const PROFILE_STUB = `\
142
147
  # Your profile
143
148
 
144
149
  Who you are and how you like to work — so the agent can meet you where you are
package/js/cli-table.json CHANGED
@@ -488,7 +488,7 @@
488
488
  },
489
489
  {
490
490
  "name": "git-gate",
491
- "help": "PreToolUse hook: ASK before a history-discarding git act (Law IV) and before every git commit/push (process 5 backstop)",
491
+ "help": "PreToolUse hook: ASK before a history-discarding git act (Deletion Is Deliberate) and before every git commit/push (Consent Before Push backstop)",
492
492
  "description": "",
493
493
  "positionals": [],
494
494
  "options": [
@@ -528,7 +528,7 @@
528
528
  ],
529
529
  "dest": "no_consent",
530
530
  "metavar": null,
531
- "help": "skip the process-5 commit/push ask (set by the emit when the process pack or process 5 is off); Law IV still asks",
531
+ "help": "skip the Consent Before Push commit/push ask (set by the emit when the process pack or that rule is off); Deletion Is Deliberate still asks",
532
532
  "choices": null,
533
533
  "default": false,
534
534
  "required": false,
@@ -766,7 +766,7 @@
766
766
  },
767
767
  {
768
768
  "name": "rule-gate",
769
- "help": "PreToolUse hook: force an ASK before any write to user-rules.md or memory (process 1 backstop)",
769
+ "help": "PreToolUse hook: force an ASK before any write to user-rules.md or memory (Persist Insight backstop)",
770
770
  "description": "",
771
771
  "positionals": [],
772
772
  "options": [
@@ -700,14 +700,14 @@ function gitDecide(args, payload) {
700
700
  const command = ((payload && payload.tool_input) || {}).command;
701
701
  if (typeof command !== 'string') return 0;
702
702
  if (DESTRUCTIVE_GIT_RE.test(command)) {
703
- return ask(args, 'git-gate', 'law-4', 'Geneseed Law IV \u2014 a history-rewriting or '
703
+ return ask(args, 'git-gate', 'law-4', 'Geneseed (Deletion Is Deliberate) \u2014 a history-rewriting or '
704
704
  + 'discarding git act needs confirmation bound to this specific command');
705
705
  }
706
706
  // `--no-consent`: the process pack (or process 5 alone) is off, so the commit/push ask has
707
707
  // no rule behind it — but Law IV above is universal and has already run.
708
708
  if (args.noConsent || !GIT_GATE_RE.test(command)) return 0;
709
- return ask(args, 'git-gate', 'process-5', 'Geneseed Doctrine process 5 \u2014 every git '
710
- + 'commit/push needs explicit approval');
709
+ return ask(args, 'git-gate', 'process-5', 'Geneseed (Consent Before Push) \u2014 every git '
710
+ + 'commit/push needs explicit approval; to see the change first, use the explain-changes skill');
711
711
  }
712
712
  export const cmdGitGate = guardGate(gitGate, 'git-gate');
713
713
 
@@ -764,13 +764,13 @@ function ruleDecide(args, payload) {
764
764
  const body = [ti.content, ti.new_string, ti.file_text, ti.new_str]
765
765
  .find((v) => typeof v === 'string') || '';
766
766
  if (body && !DOTENV_RE.test(p) && SECRET_RE.test(body)) {
767
- return ask(args, 'rule-gate', 'law-1', `Geneseed Law I — ${p} would carry a `
767
+ return ask(args, 'rule-gate', 'law-1', `Geneseed (Sealed Secrets) — ${p} would carry a `
768
768
  + 'credential-shaped string. Secrets live in .env or a secret manager, never in a '
769
769
  + 'tracked file.');
770
770
  }
771
771
  const target = ruleGateTarget(p, args.root);
772
772
  if (!target) return 0;
773
- return ask(args, 'rule-gate', 'process-1', `Geneseed Doctrine process 1 \u2014 writing to `
773
+ return ask(args, 'rule-gate', 'process-1', `Geneseed (Persist Insight) \u2014 writing to `
774
774
  + `${target}: a standing rule, or a fact to remember? That choice is the user's. Run the `
775
775
  + 'rule skill first.');
776
776
  }
@@ -34,7 +34,7 @@ import { writeText, copyFile, readText } from '../lib/fs.mjs';
34
34
  import { jsonDumps, parseJson, formatValue, formatRepr, isTruthy, isDict } from '../lib/json.mjs';
35
35
 
36
36
  /** Mirrors `_build_core.VENDORED_SKILL_DIRS`. */
37
- export const VENDORED_SKILL_DIRS = new Set(['react-view-transitions', 'daydream', 'token-report']);
37
+ export const VENDORED_SKILL_DIRS = new Set(['react-view-transitions', 'daydream', 'token-report', 'explain-changes']);
38
38
 
39
39
  /**
40
40
  * `PurePath(rel).parts` — split on EITHER separator, empties dropped.