geneseed 3.1.1 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +123 -1
- package/DESIGN.md +15 -15
- package/README.md +1 -1
- package/SETUP.md +1 -1
- package/SHIPPED.md +1 -1
- package/bin/{geneseed.mjs → build-driver.mjs} +39 -42
- package/bin/geneseed-cli.mjs +33 -32
- package/bin/geneseed-hook.mjs +7 -7
- package/docs/design-history.md +40 -40
- package/docs/extending.md +111 -83
- package/docs/web/exclusions.md +1 -1
- package/docs/web/footprint.md +1 -1
- package/docs/web/foreman-mode.md +2 -2
- package/docs/web/themes-author.md +1 -1
- package/docs/web-ui.md +41 -29
- package/harness.config.json +1 -1
- package/js/README.md +203 -0
- package/js/build/bundle.mjs +231 -0
- package/js/{catalog.mjs → build/catalog.mjs} +7 -7
- package/js/build/emit-claude.mjs +287 -0
- package/js/build/emit-common.mjs +255 -0
- package/js/build/emit-opencode.mjs +214 -0
- package/js/{generate.mjs → build/generate.mjs} +27 -28
- package/js/{render.mjs → build/render.mjs} +13 -9
- package/js/{checkout.mjs → build/source.mjs} +9 -9
- package/js/build/stubs.mjs +256 -0
- package/js/{themes.mjs → build/themes.mjs} +24 -24
- package/js/build/version.mjs +155 -0
- package/js/{hooks.mjs → hosts/hooks.mjs} +47 -44
- package/js/{hosts.mjs → hosts/hosts.mjs} +30 -28
- package/js/{installs.mjs → hosts/installs.mjs} +18 -16
- package/js/{link.mjs → hosts/link.mjs} +29 -28
- package/js/{mcp.mjs → hosts/mcp.mjs} +10 -9
- package/js/{native.mjs → hosts/native.mjs} +24 -22
- package/js/{opencode.mjs → hosts/opencode.mjs} +12 -7
- package/js/{settings.mjs → hosts/settings.mjs} +63 -55
- package/js/inspect/checks-authoring.mjs +761 -0
- package/js/inspect/checks-build.mjs +231 -0
- package/js/inspect/checks-repo.mjs +308 -0
- package/js/{diff.mjs → inspect/diff.mjs} +34 -31
- package/js/inspect/doctor.mjs +286 -0
- package/js/{excludes.mjs → inspect/excludes.mjs} +30 -33
- package/js/{inventory.mjs → inspect/inventory.mjs} +20 -17
- package/js/{registry.mjs → inspect/registry.mjs} +12 -10
- package/js/inspect/scan.mjs +125 -0
- package/js/{status.mjs → inspect/status.mjs} +27 -25
- package/js/inspect/validate.mjs +199 -0
- package/js/lib/fs.mjs +37 -759
- package/js/lib/json.mjs +344 -0
- package/js/lib/paths.mjs +242 -0
- package/js/lib/proc.mjs +3 -3
- package/js/lib/text.mjs +181 -0
- package/js/lib/udiff.mjs +1 -1
- package/js/{memory.mjs → maintain/memory.mjs} +10 -10
- package/js/{migrate.mjs → maintain/migrate.mjs} +28 -26
- package/js/{setup.mjs → maintain/setup.mjs} +34 -32
- package/js/{uninstall.mjs → maintain/uninstall.mjs} +55 -56
- package/js/{update.mjs → maintain/update.mjs} +51 -51
- package/js/{anim.mjs → ui/anim.mjs} +30 -28
- package/js/{cli.mjs → ui/cli.mjs} +10 -10
- package/js/{menu.mjs → ui/menu.mjs} +10 -9
- package/js/{tui.mjs → ui/tui.mjs} +36 -34
- package/js/web/actions.mjs +84 -85
- package/js/web/activity.mjs +22 -21
- package/js/web/api.mjs +154 -51
- package/js/web/daemon.mjs +319 -0
- package/js/web/docs.mjs +24 -21
- package/js/web/graph.mjs +0 -0
- package/js/web/handler.mjs +442 -0
- package/js/web/jobs.mjs +21 -19
- package/js/web/routes.mjs +245 -0
- package/js/web/server.mjs +56 -987
- package/package.json +11 -3
- package/src/doctrines/README.md +1 -1
- package/themes/opencode/README.md +1 -1
- package/web/dist/assets/{Activity-D_sYBlW-.js → Activity-B49fE_ug.js} +1 -1
- package/web/dist/assets/{ActivityDetail-BomKcMhy.js → ActivityDetail-abt7hzD2.js} +1 -1
- package/web/dist/assets/{Diff-BSvi1WUg.js → Diff-D3_bVkih.js} +1 -1
- package/web/dist/assets/{Doctor-Cz1s-y1n.js → Doctor-hXp7srsx.js} +1 -1
- package/web/dist/assets/{Graph-CuntzFJ0.js → Graph-DvYMtlvl.js} +1 -1
- package/web/dist/assets/Harness-BIWei7N5.js +1 -0
- package/web/dist/assets/Laws-CxmnRdwt.js +3 -0
- package/web/dist/assets/{Library-DXhTN9ju.js → Library-MjbOkHIW.js} +1 -1
- package/web/dist/assets/{Markdown-BoiZcdUl.js → Markdown-Dx_w0faR.js} +1 -1
- package/web/dist/assets/{Profile-DfCHlZ03.js → Profile-BRFvv2gr.js} +1 -1
- package/web/dist/assets/{Rules-eND7hJv9.js → Rules-CfsV1EVy.js} +1 -1
- package/web/dist/assets/{Skills-CDl9opZO.js → Skills-DArA6IHS.js} +1 -1
- package/web/dist/assets/{StatusBadge-DSDtcArX.js → StatusBadge-C9bZOEZk.js} +1 -1
- package/web/dist/assets/{activity-Dmb0FTAx.js → activity-Cc0n2jwx.js} +1 -1
- package/web/dist/assets/index-Bx3oxZ1o.css +1 -0
- package/web/dist/assets/index-DC3-GlCY.js +1 -0
- package/web/dist/assets/index-DP035wPa.js +12 -0
- package/web/dist/assets/{index-Dg58ScBe.js → index-dHi-7M-F.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/src/pages/Laws.jsx +5 -19
- package/docs/declined.md +0 -200
- package/docs/limits.md +0 -329
- package/js/doctor.mjs +0 -1750
- package/js/emit.mjs +0 -1411
- package/web/dist/assets/About-DyK7yRy7.js +0 -1
- package/web/dist/assets/Harnesses-DgRyOviS.js +0 -1
- package/web/dist/assets/Laws-CWk3Mpri.js +0 -3
- package/web/dist/assets/Themes-CoAWuIeM.js +0 -2
- package/web/dist/assets/index-B-mXquBZ.css +0 -1
- package/web/dist/assets/index-CH_Z725C.js +0 -12
- package/web/dist/assets/index-jrY4m9jP.js +0 -1
package/docs/extending.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# Extending — what it costs to change this tool
|
|
2
2
|
|
|
3
|
-
`docs/limits.md` is the standing answer to *"what does this tool not prove about itself?"*.
|
|
4
3
|
This file is its companion: **"what does it cost to add one thing?"** — one invariant, one
|
|
5
4
|
doctrine rule, one skill, one plugin, one hook verb, one doc page, one theme, one screen.
|
|
6
5
|
|
|
@@ -33,7 +32,7 @@ Three seams, and almost every question about "where do I edit?" resolves to one
|
|
|
33
32
|
Two rules follow from the table and they cause most of the surprises:
|
|
34
33
|
|
|
35
34
|
- **Nothing propagates by itself.** A bundle is a *render*. Editing `src/` changes nothing on any
|
|
36
|
-
machine until something re-emits (`js/emit.mjs:362` re-fingerprints, `js/status.mjs:80`
|
|
35
|
+
machine until something re-emits (`js/build/emit.mjs:362` re-fingerprints, `js/inspect/status.mjs:80`
|
|
37
36
|
reports the drift).
|
|
38
37
|
- **`docs/web/` is data, `web/src/` is code.** A new documentation page is one file and no
|
|
39
38
|
rebuild. A new screen is a new hashed chunk and a mandatory `web/dist` commit.
|
|
@@ -47,14 +46,14 @@ three-tier split added — `{N_ONTOLOGY}` (the ontology's sections), `{N_PACKS}`
|
|
|
47
46
|
packs). `{N_LAWS}` is the **invariant** count and nothing else; there is deliberately no token
|
|
48
47
|
meaning "the whole constitution", because `proseMirrorProblems` holds README and `SHIPPED.md`
|
|
49
48
|
prose to that same number and a token that quietly meant something wider would put the docs and
|
|
50
|
-
the gate at odds
|
|
49
|
+
the gate at odds.
|
|
51
50
|
|
|
52
51
|
⚠ **Only a `kind: "concept"` page is substituted.** A `markdown`-kind page renders `{N_PACKS}`
|
|
53
52
|
literally, on the screen, as those nine characters.
|
|
54
53
|
|
|
55
54
|
Typing a literal number into a `docs/web` page is caught by `proseMirrorProblems`. The README
|
|
56
55
|
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
|
|
56
|
+
exceptions — and each has its own arm in `js/inspect/doctor.mjs` (`countTableProblems` walks the five
|
|
58
57
|
badge keys `agents`/`skills`/`laws`/`themes`/`plugins`; `proseMirrorProblems` holds the prose).
|
|
59
58
|
|
|
60
59
|
---
|
|
@@ -94,9 +93,10 @@ cd web && npm ci && npm run lint && npm run format:check && npm test && npm run
|
|
|
94
93
|
|
|
95
94
|
Notes that matter:
|
|
96
95
|
|
|
97
|
-
- The root `package.json` has
|
|
98
|
-
exists
|
|
99
|
-
hand with the glob above.
|
|
96
|
+
- The root `package.json` has exactly one script, `lint`, and no pre-commit hook, Makefile or
|
|
97
|
+
justfile exists. `npm test` at the root exits 1 with *Missing script: "test"*; the suite is run
|
|
98
|
+
by hand with the glob above. `npm ci` once per clone installs the devDependencies `lint` needs
|
|
99
|
+
— it pulls no runtime dependency, because there are none.
|
|
100
100
|
- `--test-reporter=tap` is not decoration. Both workflows gate on the *count* (`# tests N ≥ 500`)
|
|
101
101
|
because `node --test` over a glob that matches nothing prints `# tests 0` and **exits 0**. Node's
|
|
102
102
|
default reporter changed between majors, which is how a green 1043-test suite once reported zero.
|
|
@@ -166,7 +166,7 @@ Five files, none of them a count. This is the slot most new material belongs in,
|
|
|
166
166
|
on purpose so that the invariant slot stays scarce.
|
|
167
167
|
|
|
168
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
|
|
169
|
+
the body. Two gates in `constitutionProblems` (`js/inspect/doctor.mjs`) sit on this: the pack named in
|
|
170
170
|
the *heading* is what addresses the rule (not the filename, so a rule filed in the wrong file is
|
|
171
171
|
reported rather than silently renamed), and ids must run **contiguously from 1**, so appending
|
|
172
172
|
at the end is the only place `n` is free. Packs are numbered independently — nothing outside
|
|
@@ -179,7 +179,7 @@ on purpose so that the invariant slot stays scarce.
|
|
|
179
179
|
3. Seed the fourteen themes:
|
|
180
180
|
|
|
181
181
|
```bash
|
|
182
|
-
node bin/
|
|
182
|
+
node bin/build-driver.mjs --sync-themes
|
|
183
183
|
```
|
|
184
184
|
|
|
185
185
|
⚠ Four spellings of this command are in circulation and two of them fail — `doctor`'s own tip
|
|
@@ -216,13 +216,10 @@ a defect.
|
|
|
216
216
|
Steps 1 and 9 of §2c, plus `LAW_META` if the Principle line moved. Two cautions:
|
|
217
217
|
|
|
218
218
|
- `--footprint` defaults to **lean**, and lean keeps only the heading plus the first sentence
|
|
219
|
-
(`terseBlocks` in `js/render.mjs`). A clause added to the second paragraph is invisible in the
|
|
219
|
+
(`terseBlocks` in `js/build/render.mjs`). A clause added to the second paragraph is invisible in the
|
|
220
220
|
default build. Amend the first sentence, or the amendment does not exist for most installs. The
|
|
221
221
|
same is true of a doctrine rule; it is **not** true of the ontology, which ships whole at both
|
|
222
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
223
|
|
|
227
224
|
### 2c — Adding an INVARIANT
|
|
228
225
|
|
|
@@ -231,7 +228,7 @@ the bar is in `DESIGN.md` Decision 7, and most candidates belong in a doctrine p
|
|
|
231
228
|
|
|
232
229
|
1. `src/laws/universal.md` — **append** `### {{LAW}} <roman> — {{LEX_<roman>}}` plus the body.
|
|
233
230
|
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
|
|
231
|
+
citations in `js/hosts/hooks.mjs` and two OpenCode plugins, and **nothing resolves a cross-reference
|
|
235
232
|
against the canon**. Renumbering silently rewires every one of them.
|
|
236
233
|
2. `themes/_TEMPLATE.json` — add `LEX_<roman>` in template order. Gated, as in §2a step 2:
|
|
237
234
|
`constitutionProblems` holds `LEX_I..LEX_IX` as an **equality** across all fifteen files (the
|
|
@@ -239,7 +236,7 @@ the bar is in `DESIGN.md` Decision 7, and most candidates belong in a doctrine p
|
|
|
239
236
|
arm exists because the renumber left `LEX_XXII`, `LEX_XXIII`, `LEX_XXIV` and `LEX_XXXVI` behind
|
|
240
237
|
in every file and parity was silent — they were absent from nowhere.
|
|
241
238
|
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.
|
|
239
|
+
4. `LAW_CLASS` in `js/inspect/inventory.mjs` — one of the six in `LAW_CLASSES` beside it.
|
|
243
240
|
5. `LAW_META` in `web/src/pages/Laws.jsx` — same class, plus the one-line Principle. Note the map
|
|
244
241
|
is keyed by **arabic** number where `universal.md` heads with a Roman numeral. **This copy
|
|
245
242
|
exists nowhere else**; a rule absent here renders with a blank description. It is why that one
|
|
@@ -271,13 +268,13 @@ Rare, and mechanical. On top of §2a for each rule the pack carries:
|
|
|
271
268
|
1. `src/doctrines/<pack>.md`, opening with its lead line — `**{{PACK_<NAME>}}** — how … .` A pack
|
|
272
269
|
file `PACK_ORDER` names but cannot read, or one carrying no `### {{DOCTRINE}} <pack> <n>`
|
|
273
270
|
heading at all, is a refusal.
|
|
274
|
-
2. `PACK_ORDER` in `js/
|
|
271
|
+
2. `PACK_ORDER` in `js/build/source.mjs` — the registration, and the render order. It is **narrative,
|
|
275
272
|
not alphabetical** (craft → rigor → ops → process); discovery sorts alphabetically and the
|
|
276
273
|
renderer refuses a `.md` under `src/doctrines/` that `PACK_ORDER` does not name, rather than
|
|
277
274
|
skipping it.
|
|
278
275
|
3. `PACK_<NAME>` in `themes/_TEMPLATE.json` and all fourteen voices. Unlike `DOC_*`, this family is
|
|
279
276
|
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,
|
|
277
|
+
4. `DOCTRINE_BLURBS` in `js/maintain/setup.mjs` — the one-line description an installer sees in the wizard,
|
|
281
278
|
and the only place a pack is ever explained to them. Missing entries fall back to the bare name.
|
|
282
279
|
5. `harness.config.json` if the pack should be on by default; `constitutionProblems` refuses a
|
|
283
280
|
`doctrines` array naming a pack this checkout does not ship.
|
|
@@ -286,7 +283,7 @@ Rare, and mechanical. On top of §2a for each rule the pack carries:
|
|
|
286
283
|
|
|
287
284
|
## 3 — Adding a SKILL or an AGENT
|
|
288
285
|
|
|
289
|
-
**Order matters at the first two steps.** `missingReferencedSpecs` (in `js/emit.mjs`) *aborts the
|
|
286
|
+
**Order matters at the first two steps.** `missingReferencedSpecs` (in `js/build/emit.mjs`) *aborts the
|
|
290
287
|
build* when `AGENT.md.tmpl` names a spec with no file — so the file comes before the table row, or
|
|
291
288
|
nothing builds at all. The refusal is clean: `tests/unit/emit_gates.test.mjs:265` proves a refused
|
|
292
289
|
emit leaves the prior install intact.
|
|
@@ -299,20 +296,20 @@ emit leaves the prior install intact.
|
|
|
299
296
|
Gated both ways: a row with no file, and a file with no row.
|
|
300
297
|
3. `DESC_<NAME>` in **all fourteen** themes. Theme parity catches a missing one; nothing catches
|
|
301
298
|
an unstyled placeholder.
|
|
302
|
-
4. **Skills only** — `SKILL_CLASS` in `js/inventory.mjs`. Gated both ways, inside
|
|
299
|
+
4. **Skills only** — `SKILL_CLASS` in `js/inspect/inventory.mjs`. Gated both ways, inside
|
|
303
300
|
`countTableProblems`, which matters because the runtime fallback is silent: an unclassified
|
|
304
301
|
skill renders as `build`.
|
|
305
302
|
5. `registry.json` — a row keyed `skills/<name>` or `agents/<name>` with `status` ∈
|
|
306
303
|
{`experimental`, `approved`, `deprecated`}, a semver `version`, a non-blank `owner`, an ISO
|
|
307
304
|
`added`, and `last_verified: ""`. **This is the most forgettable step**: four hand-typed fields,
|
|
308
|
-
and the gate fires in both directions (`js/doctor.mjs:388-436`).
|
|
305
|
+
and the gate fires in both directions (`js/inspect/doctor.mjs:388-436`).
|
|
309
306
|
6. `README.md` — the badge, the `**🤖 Agents** (N)` / `**🛠 Skills** (N)` count, and for a skill the
|
|
310
307
|
`·`-separated name in the 🛠 row (that enumeration is gated two ways, unlike the laws' one).
|
|
311
308
|
7. `SHIPPED.md` — the `N laws, N agents, N skills` triple.
|
|
312
309
|
8. `doctor --all`, then the suite.
|
|
313
310
|
|
|
314
311
|
**A vendored skill folder** (`src/skills/<name>/SKILL.md`) is a different shape: add it to
|
|
315
|
-
`VENDORED_SKILL_DIRS` (`js/native.mjs:37` — the literal is frozen in
|
|
312
|
+
`VENDORED_SKILL_DIRS` (`js/hosts/native.mjs:37` — the literal is frozen in
|
|
316
313
|
`tests/unit/authoring_gates.test.mjs:312`), write a `VENDOR.md` carrying `**Upstream:**`,
|
|
317
314
|
`**Commit:**` (a 40-hex commit, never a moving branch) and `**License:**`, add the registry row —
|
|
318
315
|
and **skip steps 2, 4, 6, 7**: the flat-spec globs only see `*.md` at the top level.
|
|
@@ -320,14 +317,13 @@ and **skip steps 2, 4, 6, 7**: the flat-spec globs only see `*.md` at the top le
|
|
|
320
317
|
Two traps worth knowing before you write the spec:
|
|
321
318
|
|
|
322
319
|
- **The word "Read-only" anywhere in an agent spec locks its emit.** It is a bare substring test
|
|
323
|
-
(`js/native.mjs:148`). The escape hatch is an explicit `<!-- bash: allow -->` marker, which
|
|
320
|
+
(`js/hosts/native.mjs:148`). The escape hatch is an explicit `<!-- bash: allow -->` marker, which
|
|
324
321
|
re-opens bash on all three hosts.
|
|
325
322
|
- **The console has a fourth, ungated copy of the skill taxonomy.** `SKILL_CATS` in
|
|
326
323
|
`web/src/pages/Skills.jsx:19` mirrors `SKILL_CLASS` with nothing comparing them (contrast
|
|
327
324
|
`LAW_META`, which *is* cross-checked). Inventing a new *category* means editing that file —
|
|
328
325
|
which drags in the `web/dist` rebuild — and an unknown category renders as Build with no warning.
|
|
329
326
|
|
|
330
|
-
Adding a spec reddens no recording: none of the surviving snapshots contains a skill or agent name.
|
|
331
327
|
|
|
332
328
|
---
|
|
333
329
|
|
|
@@ -358,8 +354,8 @@ No `registry.json` row, no `package.json` edit — `files[]` ships `adapters/` w
|
|
|
358
354
|
|
|
359
355
|
⚠ **Read §5 first. A fifth hook verb has no green path today.**
|
|
360
356
|
|
|
361
|
-
The mechanical part: implement `cmd<Verb>` in `js/hooks.mjs`; wire the command string in
|
|
362
|
-
`claudeHookGroups` (`js/settings.mjs:434`, spelled `` `${run} <verb> …` `` — the gate scrapes that
|
|
357
|
+
The mechanical part: implement `cmd<Verb>` in `js/hosts/hooks.mjs`; wire the command string in
|
|
358
|
+
`claudeHookGroups` (`js/hosts/settings.mjs:434`, spelled `` `${run} <verb> …` `` — the gate scrapes that
|
|
363
359
|
shape); add the entry to `bin/geneseed-hook.mjs:43` `VERBS` with **exactly two-space indent**; add
|
|
364
360
|
a row to `js/cli-table.json` with a help string over 20 characters.
|
|
365
361
|
|
|
@@ -381,6 +377,55 @@ the page renders, wrong, with nothing red.
|
|
|
381
377
|
|
|
382
378
|
---
|
|
383
379
|
|
|
380
|
+
## 4bis — Adding a RUNTIME DEPENDENCY
|
|
381
|
+
|
|
382
|
+
**Short answer: you vendor it, or you do not add it.** A dependency arrives as tracked source under
|
|
383
|
+
`js/vendor/<name>/`, imported by relative path, landing in the same `git merge --ff-only` as the
|
|
384
|
+
code that imports it. It never comes from a registry at install time. `tests/unit/dependency_policy.test.mjs`
|
|
385
|
+
gates this, and its header carries the long form.
|
|
386
|
+
|
|
387
|
+
This is not conservatism. The npm route is *unbuildable* here, for three reasons that were measured
|
|
388
|
+
rather than assumed:
|
|
389
|
+
|
|
390
|
+
1. **Every fixture would die at module load.** `copyCheckout` lists the tree with
|
|
391
|
+
`git ls-files --cached --others --exclude-standard`, which honours `.gitignore`, and
|
|
392
|
+
`.gitignore` ignores `/node_modules/`. The copy is written outside the repository, under the OS
|
|
393
|
+
temp root, and the product is run out of it as a real child process — so Node's upward resolver
|
|
394
|
+
walk starts in temp and finds nothing. Eleven test files build such a copy. And this is not a
|
|
395
|
+
fixture defect to work around: a user's fresh `git clone` has no `node_modules` either. **The
|
|
396
|
+
fixture is that clone.** Giving the fixture something the real install lacks makes it stop
|
|
397
|
+
reproducing the failure it exists to catch.
|
|
398
|
+
|
|
399
|
+
2. **The clone channel cannot install, and it is the only channel that would need to.** `geneseed
|
|
400
|
+
update` is `git pull` + rebuild. `npm ci` deletes `node_modules` before refetching, so a blocked
|
|
401
|
+
download leaves an install whose CLI cannot start — the one outcome the update must never
|
|
402
|
+
produce. `npm install` writes the tracked `package-lock.json`, so `preflight()`'s
|
|
403
|
+
`git status --porcelain` then reports a dirty tree and the *next* update refuses with "You have
|
|
404
|
+
local changes": a self-poisoning update. Meanwhile npm/npx installs already resolve
|
|
405
|
+
`dependencies` at install time and need nothing from us. There is no overlap between the channel
|
|
406
|
+
that would need a sync step and the channel where one could work.
|
|
407
|
+
|
|
408
|
+
3. **The hook path pays per tool call.** `bin/geneseed-hook.mjs` loads on every tool call of every
|
|
409
|
+
agent session (~14 ms). A relative import costs no resolver walk; a bare one does.
|
|
410
|
+
|
|
411
|
+
What vendoring costs, by contrast, is almost nothing: `files[]` already carries `js/` and the
|
|
412
|
+
manifest partition already carries the `js/` prefix row, so a vendored subtree costs **zero**
|
|
413
|
+
manifest lines and zero partition lines. The existing doctor gate already covers a bad drop, because
|
|
414
|
+
`runDoctor` validates the pulled tree in a fresh interpreter before the rebuild.
|
|
415
|
+
|
|
416
|
+
**The one exception, and it is not a loophole.** `adapters/` does not run in this tool's process — it
|
|
417
|
+
ships into OpenCode's. A module the *host* provides is resolvable there and nowhere else, so
|
|
418
|
+
reaching for one is legitimate. But it must be a **guarded dynamic import with a working degraded
|
|
419
|
+
path**, never a static one: an older host that lacks it would otherwise fail the whole plugin at
|
|
420
|
+
load instead of losing one capability. `geneseed-workflow.js`'s `@opencode-ai/plugin` is the shape
|
|
421
|
+
to copy. The allow-list in the gate is two-sided — an entry nothing imports fails as loudly as an
|
|
422
|
+
import nothing declares.
|
|
423
|
+
|
|
424
|
+
**Before proposing one at all, price it.** At the time this was written there were 463 import
|
|
425
|
+
specifiers under `js/`, `bin/` and `adapters/` and exactly one was bare — the host-provided
|
|
426
|
+
exception above. The invariant already held; it had simply never been written down. A library that
|
|
427
|
+
would delete fewer lines than its vendored source adds is not a saving.
|
|
428
|
+
|
|
384
429
|
## 5 — The four things that are genuinely hard
|
|
385
430
|
|
|
386
431
|
These are not checklist items. They are structural, and each one should be settled *before* the
|
|
@@ -398,34 +443,24 @@ already exempted a named `NATIVE` list; the hook half exempted nothing — so a
|
|
|
398
443
|
failed with **no green path**, since the recorder died on 2026-08-17 and the failure message
|
|
399
444
|
correctly forbids deleting cells to pass.
|
|
400
445
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
against*) is the same reason a verb postdating the reference has none, so it is one exemption,
|
|
405
|
-
not two. Nothing measurable was lost: the hook verb set is pinned twice more, from editable
|
|
406
|
-
sources, twelve lines above.
|
|
407
|
-
|
|
408
|
-
**To add a hook verb today:** implement `cmd<Verb>` in `js/hooks.mjs`, wire the string in
|
|
409
|
-
`claudeHookGroups`, add the `VERBS` entry, add the `js/cli-table.json` row, then name the verb in
|
|
410
|
-
`NATIVE` in **both** `tests/unit/hook_cli.test.mjs` and `tests/snapshot/cli_help.test.mjs` (they
|
|
411
|
-
read each other's source, so one alone fails) and give it an absolute unit gate.
|
|
412
|
-
|
|
413
|
-
### 5.2 ⚠ Seven theme keys are frozen byte-for-byte
|
|
446
|
+
`NATIVE` in `tests/unit/hook_cli.test.mjs` names the verbs held to the structural rules rather
|
|
447
|
+
than to a per-verb gate, and the reverse check refuses an exemption for a verb neither entry point
|
|
448
|
+
carries — so a name that stops meaning anything fails rather than sitting there.
|
|
414
449
|
|
|
415
|
-
`
|
|
416
|
-
|
|
417
|
-
|
|
450
|
+
**To add a hook verb today:** implement `cmd<Verb>` in `js/hosts/hooks.mjs`, wire the string in
|
|
451
|
+
`claudeHookGroups`, add the `VERBS` entry, add the `js/cli-table.json` row, then give it an
|
|
452
|
+
absolute unit gate. `NATIVE` used to be duplicated across two files that read each other's source
|
|
453
|
+
to stay in step; there is one copy now, so there is nothing to keep in step.
|
|
418
454
|
|
|
419
|
-
|
|
420
|
-
|---|---|
|
|
421
|
-
| `theme_preview` | `TAGLINE`, `LOADED_SIGIL`, `VOICE`, **`LEX_I`**, `BENEDICTION` |
|
|
422
|
-
| `theme_flair` | `ACCENT`, `BANNER` |
|
|
455
|
+
### 5.2 ✅ Theme text is free to edit — resolved 2026-08-23
|
|
423
456
|
|
|
424
|
-
|
|
425
|
-
|
|
457
|
+
Seven keys (`TAGLINE`, `LOADED_SIGIL`, `VOICE`, `LEX_I`, `BENEDICTION`, `ACCENT`, `BANNER`) used to
|
|
458
|
+
be pinned byte-for-byte by a recording with no re-bless path, so restyling a theme's tagline was a
|
|
459
|
+
red gate. **That recording is gone.** Theme text is now held only by `doctor --all` — key presence,
|
|
460
|
+
cross-theme parity, and no unresolved tokens — which is a claim about structure, not wording.
|
|
426
461
|
|
|
427
|
-
|
|
428
|
-
|
|
462
|
+
Restyle freely. What still bites is `LOADED_SIGIL` uniqueness (§6): it is load-bearing for theme
|
|
463
|
+
detection and nothing checks it.
|
|
429
464
|
|
|
430
465
|
### 5.3 ⚠ The hook shim is machine-wide and last-writer-wins
|
|
431
466
|
|
|
@@ -434,7 +469,7 @@ anything owns **every** install's hooks. Emitting from a git worktree or a temp
|
|
|
434
469
|
repoint the shim at that disposable path; removing the worktree then killed hooks for every
|
|
435
470
|
install on the machine.
|
|
436
471
|
|
|
437
|
-
**`hookPrefix` now refuses the claim** (`js/settings.mjs`, `ephemeralCheckout`): an emit whose
|
|
472
|
+
**`hookPrefix` now refuses the claim** (`js/hosts/settings.mjs`, `ephemeralCheckout`): an emit whose
|
|
438
473
|
checkout is under the OS temp root, or is a linked git worktree (`.git` is a *file*), leaves a
|
|
439
474
|
shim that still resolves exactly as it is and wires the emitted hooks to it anyway. The bundle it
|
|
440
475
|
writes is byte-identical to a normal emit — the install simply keeps running the durable
|
|
@@ -463,37 +498,30 @@ remains the cheapest thing to run.
|
|
|
463
498
|
Related: a stray `console.log` on a hook path silently disables a gate. Hooks signal through
|
|
464
499
|
stdout JSON and return 0 on every path — a printed byte is not a warning, it is a disabled gate.
|
|
465
500
|
|
|
466
|
-
### 5.4
|
|
501
|
+
### 5.4 ✅ Expected values are written out — resolved 2026-08-23
|
|
467
502
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
writing
|
|
472
|
-
|
|
503
|
+
This section used to say that a snapshot red was a finding rather than a step: the recordings under
|
|
504
|
+
`tests/__snapshots__/` came from an implementation that no longer existed, there was no `--record`,
|
|
505
|
+
and a change that moved a recorded byte left only two honest options — revert it, or argue in
|
|
506
|
+
writing that the recording was wrong. The rule that fell out of it was *when a frozen recording
|
|
507
|
+
blocks a change, move the logic to the nearest caller that is not frozen*.
|
|
473
508
|
|
|
474
|
-
**
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
passes an empty `shipped` string and expects `[]`, and the new absence arm answered otherwise.
|
|
478
|
-
Both obvious moves are wrong. Re-blessing is impossible. Abandoning the fix leaves a gate failing
|
|
479
|
-
open. The third move is the one to reach for: **the presence check moved up one level, into
|
|
480
|
-
`countTableProblems`, which is not recorded** — where it reads better anyway, beside the code that
|
|
481
|
-
already knows whether the file could be opened. The rule that falls out: *when a frozen recording
|
|
482
|
-
blocks a change, look for the nearest caller that is not frozen.* Check with
|
|
509
|
+
**None of that applies any more.** The recordings are retired (tag `corpus-reference-v3.1.2` is the
|
|
510
|
+
only way back to them), and every expected value now lives in the test file that asserts it, above
|
|
511
|
+
a sentence saying which decision it pins. The rule is correspondingly ordinary:
|
|
483
512
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
```
|
|
513
|
+
> **Change the expectation and the sentence that explains it in the same commit, and say why in
|
|
514
|
+
> the message.**
|
|
487
515
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
do not. `docs/limits.md:311-316` names this cluster as the one open standing decision.
|
|
516
|
+
A red test is a step again. What it costs is an argument, in prose, that the new answer is better —
|
|
517
|
+
the cost that keeps the tables honest, and far cheaper than the one it replaced.
|
|
491
518
|
|
|
492
|
-
The CLI surface is the most expensive thing in the repo to change
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
`tests/unit/
|
|
519
|
+
The CLI surface is no longer the most expensive thing in the repo to change. Editing a verb's help
|
|
520
|
+
text or flags is now an ordinary edit: `js/cli-table.json` is the owned document, and
|
|
521
|
+
`tests/unit/text_layout.test.mjs` renders every verb through `formatHelp` to check the layout
|
|
522
|
+
holds. Three hand-written equalities still converge on it and are cheap to keep in step — the
|
|
523
|
+
29-command count and the hidden-argument rule (`tests/unit/cli_table.test.mjs`), and
|
|
524
|
+
`tests/unit/docs.test.mjs`, which validates every fenced `geneseed <verb> <flags>` line in
|
|
497
525
|
`README.md`, `SETUP.md`, `QUICKSTART.md` and `SHIPPED.md` against the table.
|
|
498
526
|
|
|
499
527
|
---
|
|
@@ -511,12 +539,12 @@ only, at best, on its presence.
|
|
|
511
539
|
| The README keyword enumerations | `README.md`, `docs/web/rules.md` | nothing |
|
|
512
540
|
| The plugin capability enumeration | `docs/web/model.md` | nothing (the number substitutes; the list does not) |
|
|
513
541
|
| Section labels and page subtitles | `web/src/lib/sections.js`, `web/src/pages/Docs/index.jsx` | nothing |
|
|
514
|
-
| `THEME_BLURBS` (8 of 14), `ART` (8 of 14) | `js/setup.mjs`, `js/anim.mjs` | nothing — missing entries fall back silently |
|
|
542
|
+
| `THEME_BLURBS` (8 of 14), `ART` (8 of 14) | `js/maintain/setup.mjs`, `js/ui/anim.mjs` | nothing — missing entries fall back silently |
|
|
515
543
|
| `LOADED_SIGIL` uniqueness | `themes/*.json` | nothing, and it is load-bearing for theme detection |
|
|
516
544
|
| `adapters/claude-code/settings.json` | — | nothing compares it to what the emitter writes; **it is already divergent** |
|
|
517
545
|
| `NATIVE`, twice | two test files | they check each other, which is the gate that makes the duplication safe |
|
|
518
546
|
| The emit-size `CEILING` | `tests/unit/emit_smoke.test.mjs:54` | a hand-transcribed measurement |
|
|
519
|
-
| Stale docblock counts | `js/themes.mjs:62` ("137 values" → 140), `js/render.mjs:116` ("145 tokens" → 148) | nothing |
|
|
547
|
+
| Stale docblock counts | `js/build/themes.mjs:62` ("137 values" → 140), `js/build/render.mjs:116` ("145 tokens" → 148) | nothing |
|
|
520
548
|
|
|
521
549
|
---
|
|
522
550
|
|
|
@@ -555,7 +583,7 @@ answer.
|
|
|
555
583
|
5. **Numbers in docblocks that had drifted** — `_TEMPLATE.json`'s "137 values" (140), "the 145
|
|
556
584
|
tokens" `src/` uses (148), and "sixteen `_*_problems` checks" (fifteen — the sixteenth was
|
|
557
585
|
named as `cli`, which was removed).
|
|
558
|
-
6. **A claim that was simply false.** `js/render.mjs` said `--theme` is validated against
|
|
586
|
+
6. **A claim that was simply false.** `js/build/render.mjs` said `--theme` is validated against
|
|
559
587
|
`choices` before anything renders, which is why its own refusal read as an unreachable nicety.
|
|
560
588
|
It is not in `choicesFor`; that refusal is the only one there is, on every path.
|
|
561
589
|
7. **Five dead cross-references** to `tests/snapshot/no_python_in_corpus.test.mjs`, retired with
|
|
@@ -563,7 +591,7 @@ answer.
|
|
|
563
591
|
Plus a skip message naming a `record-corpus` CI job that no longer exists.
|
|
564
592
|
8. **`CHANGELOG.md` said 2.0.0 was "not yet published"**, with "no tag, no release and no install
|
|
565
593
|
URL"; `geneseed@2.0.0` has been on npm since 2026-08-17. And **`SETUP.md`** documented
|
|
566
|
-
`geneseed upgrade v1.0.0 # pin to a tag`, which `js/update.mjs` prints is IGNORED.
|
|
594
|
+
`geneseed upgrade v1.0.0 # pin to a tag`, which `js/maintain/update.mjs` prints is IGNORED.
|
|
567
595
|
9. **`docs/web/agents.md`** enumerated 16 agents and omitted `developer`, where README says 17 —
|
|
568
596
|
the doctor arm cannot fire, because the page spells `{N_AGENTS}` and that arm only ever catches
|
|
569
597
|
a literal. **`docs/web/rules.md`** called each law "a short markdown file under `src/laws/`";
|
|
@@ -662,9 +690,9 @@ stop a change today, and all of them cost a contributor a wrong turn until they
|
|
|
662
690
|
and today nothing warns you before the snapshot goes red.
|
|
663
691
|
4. **Gate `AGENT_LESSON_PROMPT` like its sibling (§7.7), and either gate or delete
|
|
664
692
|
`adapters/claude-code/settings.json` (§7.10).** Two silent failure modes, both cheap to close.
|
|
665
|
-
5. **
|
|
666
|
-
|
|
667
|
-
|
|
693
|
+
5. **Finish the `scripts` block in the root `package.json`.** `lint` landed with the ESLint
|
|
694
|
+
config; the rest of what CI runs is still reassembled by hand from this file. A `verify` script
|
|
695
|
+
chaining lint + suite + doctor is three lines and makes §1 executable rather than descriptive.
|
|
668
696
|
|
|
669
697
|
The list of hand-maintained satellites in §6 is the *design*, not a defect — this project
|
|
670
698
|
deliberately keeps tables and prose hand-written and gates them from the source tree. The
|
package/docs/web/exclusions.md
CHANGED
|
@@ -3,7 +3,7 @@ group: concepts
|
|
|
3
3
|
order: 9
|
|
4
4
|
title: "Sovereign repos (exclusions)"
|
|
5
5
|
kind: "concept"
|
|
6
|
-
link: {"hash": "#/
|
|
6
|
+
link: {"hash": "#/harness", "label": "Manage in Harness →"}
|
|
7
7
|
---
|
|
8
8
|
A **sovereign repo** is a folder where every GLOBAL harness install goes fully dormant: hooks stay silent and the global preamble (`AGENT.md`/`CLAUDE.md`) is never loaded there. A repo that ships its own agent config you don't want a global harness layered onto is the usual case.
|
|
9
9
|
|
package/docs/web/footprint.md
CHANGED
|
@@ -29,7 +29,7 @@ Neither setting hides text from the agent; they hide it from *every turn*.
|
|
|
29
29
|
It's set-and-forget — stored in the `.geneseed-footprint` marker, preserved across every rebuild, identical on every host (OpenCode, Claude Code, Bob, Copilot). Changing it re-emits the install.
|
|
30
30
|
|
|
31
31
|
- **Settings** — the **Footprint** toggle flips the current install (full ⇄ lean) and rebuilds it in place.
|
|
32
|
-
- **
|
|
32
|
+
- **Harness tab** — on an active install, a per-harness dropdown sets it for that install independently, then **Apply**. On a host that is not installed yet, it is step 2 of **Install…**.
|
|
33
33
|
- **Setup / re-theme wizard** — asks for footprint alongside voice, posture, mode, and doctrine packs.
|
|
34
34
|
- **CLI** — `geneseed-build --footprint lean` (with any `--emit`).
|
|
35
35
|
|
package/docs/web/foreman-mode.md
CHANGED
|
@@ -3,7 +3,7 @@ group: concepts
|
|
|
3
3
|
order: 12
|
|
4
4
|
title: "Foreman mode"
|
|
5
5
|
kind: "concept"
|
|
6
|
-
link: {"hash": "#/
|
|
6
|
+
link: {"hash": "#/harness", "label": "Set the mode in Harness →"}
|
|
7
7
|
---
|
|
8
8
|
**Mode** is the session's *operating* register — how work gets executed — one of four build-time dials, beside posture (the *relationship* register), [doctrine packs](#/docs/setup-choices) (which practice rules bind) and footprint (how much of the constitution loads inline). Two modes ship, set at build time so the choice holds steady instead of drifting mid-session, and preserved across every rebuild and re-theme.
|
|
9
9
|
|
|
@@ -24,7 +24,7 @@ Because the crew runs apart from the conversation, the session stays responsive
|
|
|
24
24
|
|
|
25
25
|
Set-and-forget, stored beside the install and preserved across rebuilds and re-themes:
|
|
26
26
|
|
|
27
|
-
- **
|
|
27
|
+
- **Harness tab** — on an active install, the per-install **Mode** dropdown (beside Voice, Footprint, and Posture), then **Apply**. On a host that is not installed yet, the same four choices are steps inside **Install…**.
|
|
28
28
|
- **Setup / re-theme wizard** — asks for mode alongside voice, posture, doctrine packs, and footprint.
|
|
29
29
|
- **CLI** — `geneseed-build --mode foreman`; switch back with `--mode direct`.
|
|
30
30
|
|
|
@@ -3,7 +3,7 @@ group: extend
|
|
|
3
3
|
order: 1
|
|
4
4
|
title: "Write a new theme"
|
|
5
5
|
kind: "concept"
|
|
6
|
-
link: {"hash": "#/
|
|
6
|
+
link: {"hash": "#/harness", "label": "Open the voice gallery →"}
|
|
7
7
|
---
|
|
8
8
|
A theme is one JSON file under `themes/` declaring voice tokens only: `BANNER`, `TAGLINE`, `LOADED_SIGIL`, `VOICE`, the core nouns (`ONTOLOGY`/`LAW(S)`/`DOCTRINE(S)`/`AGENT(S)`/`SKILL(S)`/`MEMORY`/`NOTEBOOK`/`WIKI`), the section intros `INTRO_*`, the epigraphs `EPI_*`, the `BENEDICTION`, the `ROAST_PERSONA`, and `DESC_*` blurbs. Copy `themes/neutral.json` and edit. `geneseed build --theme yours` renders it; `doctor` checks for missing tokens.
|
|
9
9
|
|
package/docs/web-ui.md
CHANGED
|
@@ -50,12 +50,17 @@ The left rail mirrors the harness's own shape:
|
|
|
50
50
|
|
|
51
51
|
| Group | Views |
|
|
52
52
|
| --- | --- |
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
53
|
+
| *(ungrouped)* | Dashboard |
|
|
54
|
+
| **📖 Codex** | Constitution · Rules · Profile · Skills · Agents · Library · Docs · Graph |
|
|
55
|
+
| **🩺 Care** | Activity · Changes · Doctor |
|
|
56
|
+
| **🎨 Setup** | Harness · Settings |
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
Above the index sits a **vitals** card — the germination ring, drift, the doctor's verdict
|
|
59
|
+
and how long ago the harness was built — so those four facts are on screen from every page.
|
|
60
|
+
It is hidden where the rail collapses to icons (≤960px) and returns with the phone drawer.
|
|
61
|
+
|
|
62
|
+
A global **Spotlight** search lives in the topbar — press <kbd>/</kbd> to focus it (or
|
|
63
|
+
<kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>K</kbd>, which also works from inside a text field), type
|
|
59
64
|
to jump to any agent, skill, doc, spec, MCP server, or constitutional rule — the `laws`
|
|
60
65
|
catalog it indexes is the whole constitution, so an ontology section, an invariant and a
|
|
61
66
|
doctrine rule are all reachable by name; <kbd>↑</kbd>/<kbd>↓</kbd> to
|
|
@@ -129,32 +134,39 @@ streams the output of background jobs and keeps their history across reloads.
|
|
|
129
134
|
theme, parity, links, authoring gates, drift), grouping any problems per check. Re-run
|
|
130
135
|
on demand.
|
|
131
136
|
|
|
132
|
-
### 🎨
|
|
133
|
-
|
|
134
|
-
- **
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
### 🎨 Setup
|
|
138
|
+
|
|
139
|
+
- **Harness** (`#/harness`) — this machine's own install first (path, voice, footprint,
|
|
140
|
+
and when it was built), then the **Voice** reference, then every detected install.
|
|
141
|
+
`#/harnesses` and `#/themes` both still resolve here, so old links and bookmarks keep
|
|
142
|
+
working.
|
|
143
|
+
- The **Voice** card is the former Themes tab, kept as a REFERENCE rather than a second
|
|
144
|
+
control: a collapsed row that opens to every shipped voice with its tagline, the
|
|
145
|
+
deployed one pinned first and marked. It is the one place you can read what a voice
|
|
146
|
+
sounds like — the per-install picker below is a list of bare names. Applying happens
|
|
147
|
+
there, per install, beside that install's own footprint, posture and mode; a gallery at
|
|
148
|
+
the top of the page could only ever act on one of them.
|
|
149
|
+
- **Every install** lists each detected install (host × scope), one row each. An
|
|
150
|
+
ACTIVE row carries per-row **Voice**, **Footprint**, **Posture** and **Mode**
|
|
151
|
+
pickers plus **Apply**, an on/off switch, and remove; a host that is NOT installed
|
|
152
|
+
yet shows a single **Install…** that discloses the same four choices as steps.
|
|
153
|
+
**Posture** is the relationship register (peer/mentor/expert/assistant/artisan —
|
|
154
|
+
default **peer**); **Mode** is the operating register (direct/foreman — default
|
|
155
|
+
**direct**; foreman triages tasks into isolated pipelines instead of working every
|
|
156
|
+
one itself). Both are orthogonal to voice and to each other, picked here or via the
|
|
157
|
+
setup wizard, and preserved across every rebuild.
|
|
158
|
+
- "Deploy to folder…" and "Rebuild all" apply to the whole set. An **Excluded folders**
|
|
159
|
+
card (shown once a global install exists) manages sovereign repos — folders where every
|
|
160
|
+
global harness goes dormant (hooks stay silent, the global preamble never loads);
|
|
161
|
+
add or remove one here, the web mirror of `harness exclude add|remove|list`.
|
|
148
162
|
- **Settings** (`#/settings`) — a **Console direction** picker (the visual flavour of the
|
|
149
163
|
console) with a **Dashboard layout** control (Auto follows each theme's designed Status
|
|
150
|
-
lens; Cultivar / Greenhouse / Operator force one regardless of skin), the
|
|
151
|
-
(deployed/target/theme/version/memory store), a Maintenance card (PATH
|
|
152
|
-
uninstall), an offline package download,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
- **About** (`#/about`) — project + creator credits and the source link.
|
|
164
|
+
lens; Cultivar / Greenhouse / Operator / Journal force one regardless of skin), the
|
|
165
|
+
install snapshot (deployed/target/theme/version/memory store), a Maintenance card (PATH
|
|
166
|
+
link/unlink, uninstall), an offline package download, server controls (Stop), and the
|
|
167
|
+
**About** colophon — project + creator credits and the source link, folded in from the
|
|
168
|
+
retired `#/about` tab (which still resolves here). Build, update, and MCP wiring live in
|
|
169
|
+
the Harness tab.
|
|
158
170
|
|
|
159
171
|
## Rebuilding the UI
|
|
160
172
|
|
package/harness.config.json
CHANGED
|
@@ -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.1.
|
|
5
|
+
"version": "3.1.3"
|
|
6
6
|
}
|