geneseed 3.4.0 → 3.5.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.
- package/CHANGELOG.md +283 -0
- package/DESIGN.md +11 -14
- package/QUICKSTART.md +3 -1
- package/README.md +51 -42
- package/SETUP.md +25 -27
- package/SHIPPED.md +26 -32
- package/adapters/bob/README.md +12 -9
- package/adapters/copilot/README.md +26 -25
- package/adapters/opencode/GLOBAL-HARNESS-SPEC.md +1 -1
- package/adapters/opencode/README.md +2 -2
- package/adapters/opencode/plugins/geneseed-context.js +5 -3
- package/adapters/opencode/plugins/geneseed-workflow.js +30 -3
- package/adapters/opencode/workflows/_runtime.js +97 -7
- package/bin/build-driver.mjs +5 -1248
- package/bin/geneseed-cli.mjs +11 -6
- package/bin/geneseed-hook.mjs +4 -2
- package/docs/candidate-hosts.md +1 -1
- package/docs/design-history.md +37 -37
- package/docs/extending.md +45 -24
- package/docs/token-footprint.md +52 -26
- package/docs/web/claude-hooks.md +3 -2
- package/docs/web/collaboration.md +2 -2
- package/docs/web/first-session.md +1 -1
- package/docs/web/footprint.md +2 -2
- package/docs/web/harnesses.md +1 -1
- package/docs/web/model.md +1 -1
- package/docs/web/plugin-workflow.md +1 -0
- package/docs/web/rules.md +3 -3
- package/docs/web/setup-choices.md +2 -2
- package/docs/web/themes-author.md +1 -1
- package/docs/web-ui.md +18 -10
- package/harness.config.json +1 -1
- package/js/README.md +6 -5
- package/js/build/driver.mjs +1191 -0
- package/js/build/emit-claude.mjs +17 -10
- package/js/build/emit-common.mjs +4 -4
- package/js/build/generate.mjs +1 -1
- package/js/build/render.mjs +48 -44
- package/js/build/stubs.mjs +1 -1
- package/js/build/themes.mjs +4 -4
- package/js/build/version.mjs +12 -0
- package/js/cli-table.json +21 -6
- package/js/hosts/hooks.mjs +89 -74
- package/js/hosts/hosts.mjs +19 -2
- package/js/hosts/mcp.mjs +14 -20
- package/js/hosts/native.mjs +23 -17
- package/js/hosts/settings.mjs +52 -104
- package/js/inspect/checks-authoring.mjs +117 -3
- package/js/inspect/diff.mjs +1 -1
- package/js/inspect/doctor.mjs +1 -1
- package/js/inspect/excludes.mjs +1 -1
- package/js/inspect/scan.mjs +3 -14
- package/js/inspect/status.mjs +2 -2
- package/js/inspect/validate.mjs +1 -1
- package/js/lib/fs.mjs +23 -8
- package/js/lib/json.mjs +7 -1
- package/js/lib/proc.mjs +6 -4
- package/js/maintain/migrate.mjs +1 -1
- package/js/maintain/setup.mjs +1 -1
- package/js/maintain/uninstall.mjs +17 -15
- package/js/ui/cli.mjs +19 -0
- package/js/ui/menu.mjs +1 -1
- package/js/web/actions.mjs +4 -4
- package/js/web/activity.mjs +4 -4
- package/js/web/api.mjs +14 -11
- package/js/web/daemon.mjs +1 -1
- package/js/web/docs.mjs +12 -12
- package/js/web/handler.mjs +12 -5
- package/js/web/jobs.mjs +1 -1
- package/package.json +1 -1
- package/src/AGENT.md.tmpl +69 -36
- package/src/agents/developer.md +2 -2
- package/src/agents/reviewer.md +5 -2
- package/src/doctrines/README.md +26 -3
- package/src/doctrines/craft.md +78 -5
- package/src/doctrines/ops.md +62 -0
- package/src/doctrines/process.md +91 -4
- package/src/doctrines/rigor.md +40 -0
- package/src/laws/universal.md +27 -21
- package/src/memory/README.md +4 -2
- package/src/modes/foreman.md +5 -5
- package/src/notebook/README.md +2 -1
- package/src/ontology/universal.md +71 -67
- package/src/postures/peer.md +3 -0
- package/src/skills/bruno-collection-generator.md +1 -1
- package/src/skills/bruno-test-writer.md +2 -2
- package/src/skills/daydream/LICENSE +21 -0
- package/src/skills/daydream/VENDOR.md +3 -1
- package/src/skills/geneseed-code-review.md +6 -2
- package/src/skills/parallel-agents.md +3 -3
- package/src/skills/pipeline.md +4 -4
- package/src/skills/react-view-transitions/VENDOR.md +7 -1
- package/src/skills/workflow.md +12 -4
- package/themes/_TEMPLATE.json +6 -4
- package/themes/biker.json +5 -3
- package/themes/commentator.json +5 -3
- package/themes/cyberpunk.json +5 -3
- package/themes/gamer.json +5 -3
- package/themes/imperial.json +5 -3
- package/themes/joker.json +5 -3
- package/themes/marvin.json +5 -3
- package/themes/mean.json +5 -3
- package/themes/military.json +5 -3
- package/themes/neutral.json +6 -4
- package/themes/opencode/README.md +2 -2
- package/themes/pirate.json +5 -3
- package/themes/sports.json +5 -3
- package/themes/verstappen.json +5 -3
- package/themes/wizard.json +5 -3
- package/web/dist/assets/Activity-Flgf4kHy.js +1 -0
- package/web/dist/assets/ActivityDetail-DIpr85nh.js +1 -0
- package/web/dist/assets/CatalogRow-CjyLjaLb.js +1 -0
- package/web/dist/assets/Diff-Deisdha8.js +2 -0
- package/web/dist/assets/Docs-DGUdY4l4.js +2 -0
- package/web/dist/assets/Doctor-F74lJ69P.js +1 -0
- package/web/dist/assets/FilterInput-BakJLvaV.js +1 -0
- package/web/dist/assets/GreenhouseView-D072lBiD.js +2 -0
- package/web/dist/assets/Harness-DNRP2Va7.js +1 -0
- package/web/dist/assets/JournalView-DWFqZ0Iw.js +1 -0
- package/web/dist/assets/Laws-BN6g3HU7.js +3 -0
- package/web/dist/assets/Library-CDOv4emM.js +1 -0
- package/web/dist/assets/Markdown-BQAvN8xx.js +38 -0
- package/web/dist/assets/OperatorHudView-CwNIUVX8.js +2 -0
- package/web/dist/assets/Profile-CpP9BTne.js +1 -0
- package/web/dist/assets/Rules-DFXsUCB7.js +1 -0
- package/web/dist/assets/Settings-BqZVUtJ5.js +1 -0
- package/web/dist/assets/Skills-Cu6kBgfF.js +1 -0
- package/web/dist/assets/StatusBadge-Cg6j-NJL.js +1 -0
- package/web/dist/assets/activity-iWaWVtsR.js +1 -0
- package/web/dist/assets/drawable-BgfrEBer.js +1 -0
- package/web/dist/assets/index-CAqrWhKl.js +2 -0
- package/web/dist/assets/index-Ruh93A3A.css +1 -0
- package/web/dist/assets/jobBuckets-Dbq8ZlWW.js +1 -0
- package/web/dist/assets/lawCats-BLgxl151.js +1 -0
- package/web/dist/assets/react-DbkvxWG5.js +8 -0
- package/web/dist/assets/rolldown-runtime-CbXtAM7H.js +1 -0
- package/web/dist/index.html +4 -3
- package/web/src/pages/Laws.jsx +22 -8
- package/src/skills/react-view-transitions/README.md +0 -42
- package/web/dist/assets/Activity-DMGhhjQc.js +0 -1
- package/web/dist/assets/ActivityDetail-CeQcDA7e.js +0 -1
- package/web/dist/assets/CatalogRow-CnSbDPyg.js +0 -1
- package/web/dist/assets/Diff-D_ksMBD1.js +0 -5
- package/web/dist/assets/Doctor-Brb3_oCT.js +0 -1
- package/web/dist/assets/FilterInput-DbRWPhtT.js +0 -1
- package/web/dist/assets/GreenhouseView-F3VNFr73.js +0 -2
- package/web/dist/assets/Harness-DJSwfYeF.js +0 -1
- package/web/dist/assets/JournalView-BLRYRnB3.js +0 -1
- package/web/dist/assets/Laws-CKYns9qe.js +0 -3
- package/web/dist/assets/Library-BK0YloSv.js +0 -1
- package/web/dist/assets/Markdown-BzTRzc7a.js +0 -46
- package/web/dist/assets/OperatorHudView-D3OQSGp5.js +0 -2
- package/web/dist/assets/Profile-CPA2u_0g.js +0 -1
- package/web/dist/assets/Rules-Lukel-Ru.js +0 -1
- package/web/dist/assets/Skills-BKRLHH7m.js +0 -1
- package/web/dist/assets/StatusBadge-DI3weRKd.js +0 -1
- package/web/dist/assets/activity-C_H2Bx1P.js +0 -1
- package/web/dist/assets/index-B7_Wga23.js +0 -2
- package/web/dist/assets/index-DzdQjvM1.css +0 -1
- package/web/dist/assets/index-N8dTrVro.js +0 -1
- package/web/dist/assets/index-Py_IQjgK.js +0 -10
- package/web/dist/assets/jobBuckets-DfTUJUxR.js +0 -1
- package/web/dist/assets/lawCats-DUS9UTH4.js +0 -1
- package/web/dist/assets/react-DOpiTn5v.js +0 -32
package/bin/geneseed-cli.mjs
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
// table below now holds one LOADER per row (`() => import(…).then(m => m.cmdX)`), so a verb
|
|
55
55
|
// loads its slice of the tree and no other verb's. The spawn allow-list gate still sees the
|
|
56
56
|
// whole graph: `tests/unit/hook_cli.test.mjs`'s `importClosure` follows both import spellings.
|
|
57
|
-
import { cliSpec, printHelp } from '../js/ui/cli.mjs';
|
|
57
|
+
import { cliSpec, printHelp, printVerbList } from '../js/ui/cli.mjs';
|
|
58
58
|
import { parseIntStrict } from '../js/lib/text.mjs';
|
|
59
59
|
import { printErr } from '../js/lib/fs.mjs';
|
|
60
60
|
|
|
@@ -279,10 +279,15 @@ async function main(argv) {
|
|
|
279
279
|
// 'error' event and a stack trace.
|
|
280
280
|
process.stdout.on('error', (e) => { if (e.code === 'EPIPE') process.exit(0); });
|
|
281
281
|
|
|
282
|
+
// Bare = `home`, as both launchers (`geneseed`, `geneseed.cmd`) already did — but the npm
|
|
283
|
+
// `bin` names THIS file, so a bare `geneseed` off PATH used to die asking for a verb.
|
|
284
|
+
if (!argv.length) argv = ['home'];
|
|
282
285
|
const verb = argv[0];
|
|
283
|
-
if (
|
|
284
|
-
return
|
|
285
|
-
|
|
286
|
+
if (verb === '-h' || verb === '--help') {
|
|
287
|
+
return printVerbList('geneseed', [...Object.keys(VERBS), 'validate'], {
|
|
288
|
+
update: 'an alias of upgrade',
|
|
289
|
+
validate: "check the source tree without emitting (the generator's --validate-only)",
|
|
290
|
+
});
|
|
286
291
|
}
|
|
287
292
|
// `validate` — the generator's `--validate-only`, and the one verb this entry answers that is NOT
|
|
288
293
|
// in the table above. It is dispatched here, before the lookup, for a reason that is a
|
|
@@ -302,7 +307,7 @@ async function main(argv) {
|
|
|
302
307
|
// Lazy for the same reason the table rows are — `parseDriverArgs` alone drags the whole
|
|
303
308
|
// generator driver in, and every OTHER verb was paying for it.
|
|
304
309
|
const [{ cmdValidate }, { parseDriverArgs }] = await Promise.all([
|
|
305
|
-
import('../js/inspect/validate.mjs'), import('
|
|
310
|
+
import('../js/inspect/validate.mjs'), import('../js/build/driver.mjs'),
|
|
306
311
|
]);
|
|
307
312
|
try {
|
|
308
313
|
return cmdValidate(parseDriverArgs(argv.slice(1)));
|
|
@@ -319,7 +324,7 @@ async function main(argv) {
|
|
|
319
324
|
// just about to become false — it was already claiming a third place for verbs that do
|
|
320
325
|
// not exist.
|
|
321
326
|
return die(2, `invalid choice: '${verb}'. This entry point carries only `
|
|
322
|
-
+ `${Object.keys(VERBS).join(', ')}; the
|
|
327
|
+
+ `${Object.keys(VERBS).join(', ')}; the five hook verbs live in `
|
|
323
328
|
+ 'bin/geneseed-hook.mjs.');
|
|
324
329
|
}
|
|
325
330
|
// `js/cli-table.json`, the CLI as data. A verb this table dispatches and the file does not
|
package/bin/geneseed-hook.mjs
CHANGED
|
@@ -48,7 +48,7 @@ import { printErr } from '../js/lib/fs.mjs';
|
|
|
48
48
|
const HOSTED = { '--root': 'root', '--host': 'host' };
|
|
49
49
|
const VERBS = {
|
|
50
50
|
context: { fn: cmdContext, flags: HOSTED },
|
|
51
|
-
'git-gate': { fn: cmdGitGate, flags: HOSTED },
|
|
51
|
+
'git-gate': { fn: cmdGitGate, flags: HOSTED, switches: { '--no-consent': 'noConsent' } },
|
|
52
52
|
'rule-gate': { fn: cmdRuleGate, flags: HOSTED },
|
|
53
53
|
'tool-gate': { fn: cmdToolGate, flags: HOSTED },
|
|
54
54
|
learn: {
|
|
@@ -72,7 +72,9 @@ function die(code, msg) {
|
|
|
72
72
|
* positional.
|
|
73
73
|
*/
|
|
74
74
|
function parse(spec, argv) {
|
|
75
|
-
const args = {
|
|
75
|
+
const args = {
|
|
76
|
+
root: null, host: null, memory: null, consolidate: false, noConsent: false, file: null,
|
|
77
|
+
};
|
|
76
78
|
for (let i = 0; i < argv.length; i += 1) {
|
|
77
79
|
const tok = argv[i];
|
|
78
80
|
const eq = tok.indexOf('=');
|
package/docs/candidate-hosts.md
CHANGED
|
@@ -36,7 +36,7 @@ current host gets. Skills are the largest surface by file count and they cost no
|
|
|
36
36
|
- **Codex CLI — cheapest, and the only one with an ask tier.** Its hook schema is
|
|
37
37
|
Claude's with `hooks.json`/`config.toml` as the carrier, its payload field names are
|
|
38
38
|
Claude's, and `permissionDecision: "ask"` exists. The Claude engine with `host: "codex"`
|
|
39
|
-
covers instructions (`AGENTS.md`), skills (`.agents/skills`) and the
|
|
39
|
+
covers instructions (`AGENTS.md`), skills (`.agents/skills`) and the hook verbs
|
|
40
40
|
unchanged; the only new code is the carrier writer (a `hooks.json` merge or a TOML
|
|
41
41
|
table) and the config-dir resolver (`~/.codex`, `$CODEX_HOME`). Roughly the Copilot
|
|
42
42
|
port minus the dialect work.
|
package/docs/design-history.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**The Node port, P2 → P10 (2026-06 → 2026-08). A record, not a spec.**
|
|
6
6
|
|
|
7
|
-
[← Back to DESIGN.md](../DESIGN.md) · [What
|
|
7
|
+
[← Back to DESIGN.md](../DESIGN.md) · [What it costs to add one thing](extending.md)
|
|
8
8
|
|
|
9
9
|
</div>
|
|
10
10
|
|
|
@@ -31,7 +31,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
31
31
|
---
|
|
32
32
|
|
|
33
33
|
## ⚙️ Generator contract — the port narrative
|
|
34
|
-
- **The render core has a Node twin: [`js/build/render.mjs`](js/build/render.mjs).** It is a
|
|
34
|
+
- **The render core has a Node twin: [`js/build/render.mjs`](../js/build/render.mjs).** It is a
|
|
35
35
|
translation of `_build_render.py`'s pure pipeline, byte-identical by test rather than by
|
|
36
36
|
intent — `tests/test_render_parity.py` renders both over every theme × footprint ×
|
|
37
37
|
catalog × laws-prefix × posture × mode and compares the written trees byte for byte,
|
|
@@ -39,7 +39,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
39
39
|
sort. `js/lib/fs.mjs` holds the primitives where the two runtimes disagree in silence;
|
|
40
40
|
`Path.write_text`'s `\n` → `os.linesep` translation is the load-bearing one. Zero
|
|
41
41
|
dependencies on that side too.
|
|
42
|
-
- **The host-native layer has one too: [`js/hosts/native.mjs`](js/hosts/native.mjs)** — the point where
|
|
42
|
+
- **The host-native layer has one too: [`js/hosts/native.mjs`](../js/hosts/native.mjs)** — the point where
|
|
43
43
|
RENDER stops being a pure function of `src/`. Its output depends on your
|
|
44
44
|
`agent-overrides.json`, on which files already exist in the target (claim-on-create never
|
|
45
45
|
overwrites a file Geneseed did not write), and on which of the three host dialects is
|
|
@@ -49,7 +49,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
49
49
|
items and compares the written tree byte for byte, the returned ownership list *in order*,
|
|
50
50
|
and the warning stream — and asserts the Node side printed nothing at all on stdout,
|
|
51
51
|
because the emitted hook gates signal their verdict there and return 0 on every path.
|
|
52
|
-
- **The OpenCode extras are ported too: [`js/hosts/opencode.mjs`](js/hosts/opencode.mjs)** — the
|
|
52
|
+
- **The OpenCode extras are ported too: [`js/hosts/opencode.mjs`](../js/hosts/opencode.mjs)** — the
|
|
53
53
|
branded and curated colour themes, the opt-in primary agent and slash-command layer, the
|
|
54
54
|
always-on `/ponytail` switch, the verbatim plugin/workflow copies, and the
|
|
55
55
|
`agent-overrides.json` stub with its staleness notice. Half of it sits behind
|
|
@@ -61,7 +61,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
61
61
|
streams** — the staleness notice goes to stdout where every native-layer warning goes to
|
|
62
62
|
stderr, an asymmetry inherited from the Python rather than corrected.
|
|
63
63
|
- **The seam is now a real process boundary: `build.py` spawns Node once per emit.**
|
|
64
|
-
|
|
64
|
+
`js/build/emit.mjs` writes the bundle (`build`) and the OpenCode layer's RENDER
|
|
65
65
|
stage; Python keeps WIRE, PRUNE, MANIFEST and VERIFY, and drives — so doctor, web deploy,
|
|
66
66
|
setup and rebuild-all keep calling `build.emit_*` in-process with no call site changed.
|
|
67
67
|
Node falls back to Python when `node` is missing, silently and by design: the whole claim
|
|
@@ -121,7 +121,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
121
121
|
(with the bundle in a subfolder, so `--out` and the target are not the same directory), a
|
|
122
122
|
`.claude/` the user got to first, and every write-once file edited *between* two emits —
|
|
123
123
|
the only way a byte comparison can tell "kept your store" from "re-seeded it".
|
|
124
|
-
- **The wiring layer has a Node twin as well: [`js/hosts/settings.mjs`](js/hosts/settings.mjs)** — the
|
|
124
|
+
- **The wiring layer has a Node twin as well: [`js/hosts/settings.mjs`](../js/hosts/settings.mjs)** — the
|
|
125
125
|
JSONC reader, the `opencode.json` and `settings.json` merges, the hook shim, the
|
|
126
126
|
managed-block machinery and the settings integrity check. It was proven before it was
|
|
127
127
|
wired, the way every piece before it was; the emit now drives it. It was the last unit to
|
|
@@ -203,7 +203,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
203
203
|
writes — a write through the facade cannot be restored reliably, and a redirect that
|
|
204
204
|
silently outlives its test poisons every later read. A bare `SRC` left in a submodule
|
|
205
205
|
raises `NameError` rather than quietly reading a stale copy; that noise is the point.
|
|
206
|
-
- **There is a second driver:
|
|
206
|
+
- **There is a second driver: `bin/geneseed.mjs`, and it is ADDED beside
|
|
207
207
|
`main()` rather than replacing it.** `build.py` is also the `import build` facade that 19
|
|
208
208
|
`rituals/` modules read 55 distinct names from, and roughly eleven sites spawn it as a
|
|
209
209
|
subprocess; none of them flip, because flipping them would make Node mandatory for the
|
|
@@ -336,7 +336,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
336
336
|
- **The runtime port starts with the four HOOK verbs, and with a gate rather than with
|
|
337
337
|
code.** `context`, `git-gate`, `rule-gate` and `learn` are the four commands an emitted
|
|
338
338
|
`settings.json` actually invokes, and they now have a Node twin:
|
|
339
|
-
[`js/hosts/hooks.mjs`](js/hosts/hooks.mjs) behind [`bin/geneseed-hook.mjs`](bin/geneseed-hook.mjs), a
|
|
339
|
+
[`js/hosts/hooks.mjs`](../js/hosts/hooks.mjs) behind [`bin/geneseed-hook.mjs`](../bin/geneseed-hook.mjs), a
|
|
340
340
|
**second binary** rather than a subcommand of the generator driver — `learn` must spawn
|
|
341
341
|
whatever `$GENESEED_LLM` names, and `bin/geneseed.mjs` is under a hard `child_process`
|
|
342
342
|
ban that is half the proof it is not a passthrough. The hook entry loads none of the
|
|
@@ -345,7 +345,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
345
345
|
to import `build` for the marker filenames they need.
|
|
346
346
|
**A cell is not an emit here, and that is the phase's first design question.** A verb's
|
|
347
347
|
observable surface is stdout, stderr, the **exit code** and whatever it wrote, so
|
|
348
|
-
[`tests/helpers/cli_golden.mjs`](tests/helpers/cli_golden.mjs) compares all four across 103 seeded
|
|
348
|
+
[`tests/helpers/cli_golden.mjs`](../tests/helpers/cli_golden.mjs) compares all four across 103 seeded
|
|
349
349
|
worlds — reusing `golden.py`'s `cell_env`, `_normalise`, `_snapshot` and `_split` rather
|
|
350
350
|
than growing a second set of rules about sandboxes and CRLF. `cwd` is an **input** to
|
|
351
351
|
three of the four (discovery, the sovereign bypass, and learn's memory search all read
|
|
@@ -417,7 +417,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
417
417
|
transitive relative imports instead — the same assertion, one level out, added because of
|
|
418
418
|
this phase.
|
|
419
419
|
- **`status` and `version` cross, and the phase's method is a CORPUS beside the matrix.**
|
|
420
|
-
[`js/inspect/status.mjs`](js/inspect/status.mjs) answers both from `bin/geneseed-cli.mjs`. Two structural
|
|
420
|
+
[`js/inspect/status.mjs`](../js/inspect/status.mjs) answers both from `bin/geneseed-cli.mjs`. Two structural
|
|
421
421
|
things came with them.
|
|
422
422
|
**A closure walk counts what the callee COMPUTES; the caller consumes a fraction of it.**
|
|
423
423
|
An `ast` walk puts `status` at 527 LOC across 7 modules — `_tui_inventory` returns every
|
|
@@ -441,7 +441,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
441
441
|
the handoff offered.** `_color_enabled()` is `sys.stdout.isatty()`, every harness captures
|
|
442
442
|
through a pipe, so deleting every escape code would be byte-identical across the whole
|
|
443
443
|
matrix — ship it ungated or drop it and regress a real terminal. `_status_lines` is
|
|
444
|
-
documented pure, so
|
|
444
|
+
documented pure, so `tests/snapshot/pure_snapshot.test.mjs`
|
|
445
445
|
calls it directly on both sides over a corpus of dashboards × colour × ASCII, and the tty
|
|
446
446
|
question never arises. Three more cell-unreachable functions ride along for the same
|
|
447
447
|
reason — `_version_verdict`'s up-to-date branch, `_manifest_is_claude` (only consulted for
|
|
@@ -459,8 +459,8 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
459
459
|
now has one owner for the primitive instead of two.
|
|
460
460
|
- **The first NON-hook verb crosses, and it needed a third binary rather than a row in an
|
|
461
461
|
existing table.** `harness exclude add|remove|list` — the sovereign-repo exclusions —
|
|
462
|
-
is now [`js/inspect/excludes.mjs`](js/inspect/excludes.mjs) behind
|
|
463
|
-
[`bin/geneseed-cli.mjs`](bin/geneseed-cli.mjs). The *reader* had been ported since P5a
|
|
462
|
+
is now [`js/inspect/excludes.mjs`](../js/inspect/excludes.mjs) behind
|
|
463
|
+
[`bin/geneseed-cli.mjs`](../bin/geneseed-cli.mjs). The *reader* had been ported since P5a
|
|
464
464
|
(`sovereignBypass` runs on every hook call); this is the writer, which maintains
|
|
465
465
|
`excludes.json` across every global install and wires each host's native per-repo
|
|
466
466
|
suppression into the excluded repo.
|
|
@@ -492,7 +492,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
492
492
|
atomic (temp + rename) on both sides, so a concurrent pair loses an edit rather than
|
|
493
493
|
tearing the file.
|
|
494
494
|
- **`build`, `prompt` and `theme` cross — the generator's own CLI face —** as
|
|
495
|
-
[`js/build/generate.mjs`](js/build/generate.mjs). **Ten of the 24 subcommands are now Node.** Three
|
|
495
|
+
[`js/build/generate.mjs`](../js/build/generate.mjs). **Ten of the 24 subcommands are now Node.** Three
|
|
496
496
|
things are worth recording, and the first is about measurement rather than code.
|
|
497
497
|
**A closure walk cannot see through a `subprocess`, and `build` is nothing but one.**
|
|
498
498
|
`cmd_build` measures three lines because the work is `run([sys.executable, BUILD])`. Its
|
|
@@ -524,7 +524,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
524
524
|
`test_the_fence_corpus_still_describes_the_real_tree` re-derives it and fails the day a
|
|
525
525
|
source file grows a four-backtick run — at which point a cell becomes writable and should
|
|
526
526
|
be written. The corpus file is renamed to
|
|
527
|
-
|
|
527
|
+
`tests/snapshot/pure_snapshot.test.mjs` with it: it was
|
|
528
528
|
named for the status panel when the panel was all it held.
|
|
529
529
|
**Three of the six verbs the phase was scoped around did not cross, each for a measured
|
|
530
530
|
reason.** `sync-self` is a five-line alias whose body is `_update.sync_self` → `upgrade()`,
|
|
@@ -536,16 +536,16 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
536
536
|
interpreter using the discovery the shim flip deleted; and a shim naming
|
|
537
537
|
`bin/geneseed-cli.mjs` instead would answer three of the 24 verbs. The npm `bin` map is
|
|
538
538
|
what replaces them, so they belong to the publish phase and not to this one.
|
|
539
|
-
- **`diff` and `rebuild-all` cross** as [`js/inspect/diff.mjs`](js/inspect/diff.mjs) and `cmdRebuildAll` in
|
|
540
|
-
[`js/build/generate.mjs`](js/build/generate.mjs), with the install detectors extracted to
|
|
541
|
-
[`js/hosts/installs.mjs`](js/hosts/installs.mjs) and the registry to [`js/inspect/registry.mjs`](js/inspect/registry.mjs).
|
|
539
|
+
- **`diff` and `rebuild-all` cross** as [`js/inspect/diff.mjs`](../js/inspect/diff.mjs) and `cmdRebuildAll` in
|
|
540
|
+
[`js/build/generate.mjs`](../js/build/generate.mjs), with the install detectors extracted to
|
|
541
|
+
[`js/hosts/installs.mjs`](../js/hosts/installs.mjs) and the registry to [`js/inspect/registry.mjs`](../js/inspect/registry.mjs).
|
|
542
542
|
**Twelve of the 24 subcommands are now Node.**
|
|
543
543
|
**A closure walk cannot see through the standard library either.** An `ast` walk puts
|
|
544
544
|
`diff` at 252 LOC of marker and manifest reading, and the verb's *entire* user-visible
|
|
545
545
|
payload is `difflib.unified_diff`, which the walk counts as zero because `difflib` is not in
|
|
546
546
|
`rituals/`. There is no lazier answer than reproducing it: a generic JS diff produces a
|
|
547
547
|
correct diff and a *different* one, and what the acceptance matrix compares is bytes. So
|
|
548
|
-
[`js/lib/udiff.mjs`](js/lib/udiff.mjs) is `SequenceMatcher` and `unified_diff`, including
|
|
548
|
+
[`js/lib/udiff.mjs`](../js/lib/udiff.mjs) is `SequenceMatcher` and `unified_diff`, including
|
|
549
549
|
`autojunk` — the popular-line purge that engages at 200 elements and rewrites the hunks of
|
|
550
550
|
every real harness file, and which no fixture is large enough to reach.
|
|
551
551
|
**The rest of the diff is gated by cells and the algorithm by a corpus**, with the
|
|
@@ -582,10 +582,10 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
582
582
|
does not exist yet: copy the checkout into the sandbox, plant one fault, and run *both*
|
|
583
583
|
binaries from the copy so their two `ROOT`s move together. That is the next phase's first
|
|
584
584
|
design decision rather than a corner of this one.
|
|
585
|
-
- **`doctor` crosses** as [`js/inspect/doctor.mjs`](js/inspect/doctor.mjs), and the fixture came first.
|
|
585
|
+
- **`doctor` crosses** as [`js/inspect/doctor.mjs`](../js/inspect/doctor.mjs), and the fixture came first.
|
|
586
586
|
**Thirteen of the 24 subcommands are now Node.**
|
|
587
587
|
**The copy-the-checkout fixture, measured rather than feared.** `_copy_checkout` in
|
|
588
|
-
[`tests/helpers/cli_golden.mjs`](tests/helpers/cli_golden.mjs) gives each cell its own copy of the
|
|
588
|
+
[`tests/helpers/cli_golden.mjs`](../tests/helpers/cli_golden.mjs) gives each cell its own copy of the
|
|
589
589
|
working tree (`git ls-files --cached --others --exclude-standard`), plants one fault in it,
|
|
590
590
|
and runs *both* binaries from the copy so the two `ROOT`s move together. 511 files, 5.2 MB,
|
|
591
591
|
**0.31 s** against a `doctor` run of ~2.1 s — a seventh of the cell, so it is one copy per
|
|
@@ -621,7 +621,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
621
621
|
indistinguishable), and one of the mutations is aimed at the FIXTURE: with `_copy_checkout`
|
|
622
622
|
planting nothing, a planted-fault cell does not go red, it goes *vacuous* — which is the
|
|
623
623
|
absolute half of a cell doing its job one layer further out than it ever has.
|
|
624
|
-
- **`uninstall` crosses** as [`js/maintain/uninstall.mjs`](js/maintain/uninstall.mjs), and it is the first verb
|
|
624
|
+
- **`uninstall` crosses** as [`js/maintain/uninstall.mjs`](../js/maintain/uninstall.mjs), and it is the first verb
|
|
625
625
|
in the port that **deletes**. **Fourteen of the 24 subcommands are now Node**; of the ten
|
|
626
626
|
left, `setup`/`menu`/`home` are P5's and the rest belong to P6–P10.
|
|
627
627
|
**A deletion needs the gate to prove what SURVIVED.** Every cell before this one asserts
|
|
@@ -660,7 +660,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
660
660
|
question about the corpus of inputs before it is a question about the code. The control had to
|
|
661
661
|
be re-chosen too; the first one shared mechanism with half the mutations and reported four
|
|
662
662
|
over-reaches that were nothing of the kind.
|
|
663
|
-
- **`setup` crosses** as [`js/maintain/setup.mjs`](js/maintain/setup.mjs), and it is the first **interactive**
|
|
663
|
+
- **`setup` crosses** as [`js/maintain/setup.mjs`](../js/maintain/setup.mjs), and it is the first **interactive**
|
|
664
664
|
verb in the port. **Fifteen of the 24 subcommands are now Node**, and P5 is done: the nine
|
|
665
665
|
left belong to P6–P10.
|
|
666
666
|
**`cmd_setup` is a dispatcher, and that is the phase's first finding.** It is 21 lines: a
|
|
@@ -717,7 +717,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
717
717
|
- **The web console's HTTP shell crosses, and it needed a third acceptance harness.**
|
|
718
718
|
`tests/golden.py` compares the tree a generator writes; `tests/harness_golden.py` compares
|
|
719
719
|
one verb invocation on stdout/stderr/exit/files. An endpoint writes nothing and never
|
|
720
|
-
exits, so [`tests/helpers/web_fixture.mjs`](tests/helpers/web_fixture.mjs) asks a different question: one
|
|
720
|
+
exits, so [`tests/helpers/web_fixture.mjs`](../tests/helpers/web_fixture.mjs) asks a different question: one
|
|
721
721
|
seeded world, one **sequence of requests** against a freshly started server, compared on
|
|
722
722
|
the status line, the response body as bytes, the five headers the handler *chooses*, the
|
|
723
723
|
daemon record, and the server's own streams. **Both sides are real processes**, started on
|
|
@@ -757,7 +757,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
757
757
|
- **Eight web read endpoints cross, and six of them were a JSON face over work already
|
|
758
758
|
done.** `/api/overview`, `/api/themes`, `/api/doctor`, `/api/diff`, `/api/installs`,
|
|
759
759
|
`/api/excludes`, `/api/setup` and `/api/profile` now answer from
|
|
760
|
-
[`js/web/api.mjs`](js/web/api.mjs) as well as from `rituals/_web_*.py`. **No subcommand
|
|
760
|
+
[`js/web/api.mjs`](../js/web/api.mjs) as well as from `rituals/_web_*.py`. **No subcommand
|
|
761
761
|
crosses here and the count stays fifteen** — `web` the verb is P6h's, and until then the
|
|
762
762
|
only caller of this code is the acceptance harness. `api_setup` is `statusData()` plus
|
|
763
763
|
four fields; `api_diff` is `diffCollect()` reshaped; `api_excludes` is
|
|
@@ -788,7 +788,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
788
788
|
seconds apart — so the harness normalises them, which means a twin formatting in UTC, or
|
|
789
789
|
padding wrong, would compare equal in every web cell. The stamp is a pure function of an
|
|
790
790
|
epoch second and the local zone, so it is gated in
|
|
791
|
-
|
|
791
|
+
`tests/snapshot/pure_snapshot.test.mjs` over five
|
|
792
792
|
instants chosen for what they break: single-digit month/day/hour/minute, both sides of a
|
|
793
793
|
UTC date rollover, a DST transition, and a fractional second.
|
|
794
794
|
**Two gates, two findings, and neither came from review.** The harness's own self-check
|
|
@@ -821,8 +821,8 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
821
821
|
the control — without which every 501 in the list is vacuous.
|
|
822
822
|
- **The catalog crosses, and the taxonomy it needed was already ported.**
|
|
823
823
|
`/api/catalog/<section>`, `/api/item/<type>/<name>` and the wiki reader answer from
|
|
824
|
-
[`js/web/api.mjs`](js/web/api.mjs), over a new
|
|
825
|
-
[`js/inspect/inventory.mjs`](js/inspect/inventory.mjs) — `_harness_tui.py`'s catalog half. **Still
|
|
824
|
+
[`js/web/api.mjs`](../js/web/api.mjs), over a new
|
|
825
|
+
[`js/inspect/inventory.mjs`](../js/inspect/inventory.mjs) — `_harness_tui.py`'s catalog half. **Still
|
|
826
826
|
fifteen subcommands; still no call site until P6h.**
|
|
827
827
|
**The measurement is the finding.** `js/inspect/status.mjs` had said since P5d that "the ~111
|
|
828
828
|
lines of TUI taxonomy" were P7's and not ported. `LAW_CLASS`, `SKILL_CLASS`,
|
|
@@ -858,7 +858,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
858
858
|
cell whose target does not exist proves nothing.** Closed by seeding files that ARE there
|
|
859
859
|
and naming, in each cell, the content that must not come back.
|
|
860
860
|
- **The Docs pages cross for 57 of 59, and the two that do not are declared rather than
|
|
861
|
-
discovered.** [`js/web/docs.mjs`](js/web/docs.mjs) answers `/api/docs` and
|
|
861
|
+
discovered.** [`js/web/docs.mjs`](../js/web/docs.mjs) answers `/api/docs` and
|
|
862
862
|
`/api/docs/page/<id>` for the `markdown`, `concept` and `glossary` kinds — the harness-
|
|
863
863
|
block stripping, the slug rules, the section slicing, the live-count substitution and the
|
|
864
864
|
theme-aware glossary. **Still fifteen subcommands.** The measurement that scoped it is
|
|
@@ -878,7 +878,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
878
878
|
Python's `\s` on a `str` pattern is not JavaScript's, and the standing note read "differs
|
|
879
879
|
only at U+FEFF". Measured: `python \s = js \s − U+FEFF + U+001C..U+001F + U+0085`. A
|
|
880
880
|
heading carrying an information separator slugged differently on each side. `PY_SPACE`
|
|
881
|
-
in [`js/lib/fs.mjs`](js/lib/fs.mjs) is the measured set, and no cell could have found
|
|
881
|
+
in [`js/lib/fs.mjs`](../js/lib/fs.mjs) is the measured set, and no cell could have found
|
|
882
882
|
it — nobody has written that heading.
|
|
883
883
|
**Twelve mutations, eleven fire, six of them on the corpus rather than a cell** — which
|
|
884
884
|
is what "a corpus phase" means in practice. The survivor is honest: dropping a blank
|
|
@@ -886,8 +886,8 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
886
886
|
the empty string and a missing value reach the same fallback. Indistinguishable through
|
|
887
887
|
its one consumer, not unreachable, and kept for the reason the POST body drain was.
|
|
888
888
|
- **The capability graph and the live-activity surface cross, and the spawn the plan warned
|
|
889
|
-
about was never there.**
|
|
890
|
-
[`js/web/activity.mjs`](js/web/activity.mjs) answer `/api/graph`, `/api/activity` (GET)
|
|
889
|
+
about was never there.** `js/web/graph.mjs` and
|
|
890
|
+
[`js/web/activity.mjs`](../js/web/activity.mjs) answer `/api/graph`, `/api/activity` (GET)
|
|
891
891
|
and `/api/activity/<sid>` — **22 of 29 web paths, still fifteen subcommands.** The plan
|
|
892
892
|
scored the pid probe as a `tasklist` spawn needing a sixth allow-list row; the source is
|
|
893
893
|
`ctypes` over `OpenProcess`, so **neither implementation spawns anything** and
|
|
@@ -912,7 +912,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
912
912
|
**Eighteen mutations, eighteen fire, no survivors** — two of them visible only in the file
|
|
913
913
|
snapshot, because the response body is byte-identical with the unlink removed.
|
|
914
914
|
- **The web console starts WRITING, and the argv-in-a-response-body trap had a false
|
|
915
|
-
premise.** [`js/web/actions.mjs`](js/web/actions.mjs) and [`js/hosts/mcp.mjs`](js/hosts/mcp.mjs) answer
|
|
915
|
+
premise.** [`js/web/actions.mjs`](../js/web/actions.mjs) and [`js/hosts/mcp.mjs`](../js/hosts/mcp.mjs) answer
|
|
916
916
|
the eight mutating POSTs that own their own path plus the `/api/rules` and `/api/mcp` GETs
|
|
917
917
|
that had to cross with them — **27 of 29 web paths, still fifteen subcommands.** The plan
|
|
918
918
|
called `api_install_cmd`'s `{"cmd": [sys.executable, "build.py", …]}` "a Python argv, as
|
|
@@ -941,7 +941,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
941
941
|
not be closed by any cell, so the 409 column itself became the thing under test, read out of
|
|
942
942
|
the reference by `ast` and compared against the table `doPost` dispatches on.
|
|
943
943
|
- **The console runs BACKGROUND JOBS, and the port's one sanctioned spawn is argued rather
|
|
944
|
-
than assumed.** [`js/web/jobs.mjs`](js/web/jobs.mjs) answers the last two web paths —
|
|
944
|
+
than assumed.** [`js/web/jobs.mjs`](../js/web/jobs.mjs) answers the last two web paths —
|
|
945
945
|
**29 of 29, still fifteen subcommands** — and it is the only module in the port allowed to
|
|
946
946
|
start a copy of *this program*. **Isolation is the discriminator and it is a property of the
|
|
947
947
|
RUNTIME, not of the verb**: the reference's daemon is threaded, Node's server is
|
|
@@ -975,7 +975,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
975
975
|
get one writes a file whose NAME carries a clock. **A destamp that only reaches inside files
|
|
976
976
|
leaves the filename as an ungateable axis.**
|
|
977
977
|
- **The `web` VERB crosses — sixteen of 24 subcommands — and the TEARDOWN decided which cells
|
|
978
|
-
exist.** [`js/web/server.mjs`](js/web/server.mjs) grows the daemon lifecycle in one file, as
|
|
978
|
+
exist.** [`js/web/server.mjs`](../js/web/server.mjs) grows the daemon lifecycle in one file, as
|
|
979
979
|
the reference keeps it: `_probe`, `_live_daemon`, `_spawn_detached`, `start|stop|status|
|
|
980
980
|
restart`, `_build_plan`'s consumer, `_npm_build`, the browser open, `cmd_web`, and the last
|
|
981
981
|
POST — `/api/restart`. `bin/geneseed-cli.mjs` finally imports the web tree, which is what
|
|
@@ -1009,7 +1009,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
1009
1009
|
every non-interactive run; the reference's `input()` raises there. The byte reader now
|
|
1010
1010
|
reports that case as null, gated by a stdin-seeded corpus.
|
|
1011
1011
|
- **The install TOGGLE crosses and the web console is whole — 29 of 29 paths ANSWER, still
|
|
1012
|
-
sixteen subcommands.** [`js/maintain/uninstall.mjs`](js/maintain/uninstall.mjs) gains the reversible half it was
|
|
1012
|
+
sixteen subcommands.** [`js/maintain/uninstall.mjs`](../js/maintain/uninstall.mjs) gains the reversible half it was
|
|
1013
1013
|
written expecting: deactivate MOVES every owned artifact into a sibling stash and drops the
|
|
1014
1014
|
wiring, reactivate moves the same bytes back, and the stash directory's PRESENCE is the
|
|
1015
1015
|
disabled flag — no recorded state that could drift from the filesystem. `/api/pick-folder` is
|
|
@@ -1038,7 +1038,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
1038
1038
|
default the hook shim's runner and entry, so the Claude reactivate answered with that refusal
|
|
1039
1039
|
on its first Node run instead of quietly wiring hooks at an invented path.
|
|
1040
1040
|
- **`upgrade` crosses — seventeen of 24 — and the phase's real work was a fixture that could
|
|
1041
|
-
WRITE.** [`js/maintain/update.mjs`](js/maintain/update.mjs) is `rituals/_update.py`'s upgrade half: the
|
|
1041
|
+
WRITE.** [`js/maintain/update.mjs`](../js/maintain/update.mjs) is `rituals/_update.py`'s upgrade half: the
|
|
1042
1042
|
preflight, the streamed fetch, the four upstream classifications, the fast-forward, the
|
|
1043
1043
|
doctor gate with its exact rollback, and the two rebuilds. Every other verb in this port
|
|
1044
1044
|
READS the checkout; this one rewrites it, so its cells needed a git repository they could
|
package/docs/extending.md
CHANGED
|
@@ -32,7 +32,7 @@ Three seams, and almost every question about "where do I edit?" resolves to one
|
|
|
32
32
|
Two rules follow from the table and they cause most of the surprises:
|
|
33
33
|
|
|
34
34
|
- **Nothing propagates by itself.** A bundle is a *render*. Editing `src/` changes nothing on any
|
|
35
|
-
machine until something re-emits (`js/build/
|
|
35
|
+
machine until something re-emits (`sourceFingerprint` in `js/build/version.mjs` re-fingerprints, `js/inspect/status.mjs`
|
|
36
36
|
reports the drift).
|
|
37
37
|
- **`docs/web/` is data, `web/src/` is code.** A new documentation page is one file and no
|
|
38
38
|
rebuild. A new screen is a new hashed chunk and a mandatory `web/dist` commit.
|
|
@@ -58,10 +58,10 @@ badge keys `agents`/`skills`/`laws`/`themes`/`plugins`; `proseMirrorProblems` ho
|
|
|
58
58
|
|
|
59
59
|
---
|
|
60
60
|
|
|
61
|
-
## 1 — The baseline, and the
|
|
61
|
+
## 1 — The baseline, and the seven commands that reproduce CI
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
The baseline is whatever these commands report on your checkout — run them; no count is
|
|
64
|
+
written here, because a typed count is stale by the next commit.
|
|
65
65
|
|
|
66
66
|
CI is **four jobs expanding to six runs** (`.github/workflows/ci.yml`): `validate` and
|
|
67
67
|
`node-cells` on Linux *and* Windows, plus `package-no-python` and `web` on Linux. From the repo
|
|
@@ -83,6 +83,10 @@ node tests/golden.mjs --idempotent
|
|
|
83
83
|
node tests/golden.mjs --deletion
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
```bash
|
|
87
|
+
node tests/golden.mjs --cli
|
|
88
|
+
```
|
|
89
|
+
|
|
86
90
|
```bash
|
|
87
91
|
node tests/mutate.mjs --verify
|
|
88
92
|
```
|
|
@@ -100,6 +104,11 @@ Notes that matter:
|
|
|
100
104
|
- `--test-reporter=tap` is not decoration. Both workflows gate on the *count* (`# tests N ≥ 500`)
|
|
101
105
|
because `node --test` over a glob that matches nothing prints `# tests 0` and **exits 0**. Node's
|
|
102
106
|
default reporter changed between majors, which is how a green 1043-test suite once reported zero.
|
|
107
|
+
- `--cli` runs the ~320-cell CLI/hook matrix against the expectations written in each cell —
|
|
108
|
+
the only end-to-end gate on what a verb prints. CI runs it in `node-cells` on both platforms.
|
|
109
|
+
A cell that needs a broken checkout plants its fault as an **edit of the real file**
|
|
110
|
+
(`{ "edit": [[find, replace]] }` in `tests/helpers/matrix/cli.*.json`), never as a copy of it;
|
|
111
|
+
a `find` that stops matching exactly once fails the cell loudly.
|
|
103
112
|
- `--deletion` is the only thing that exercises the write-before-delete prune. Anyone renaming or
|
|
104
113
|
removing a plugin, a skill or an emitted file needs it.
|
|
105
114
|
- **A local pass is weaker than CI.** ~18 tests in `tests/unit/package_manifest.test.mjs` carry
|
|
@@ -107,12 +116,12 @@ Notes that matter:
|
|
|
107
116
|
on PATH they report *skipped* and the run is green.
|
|
108
117
|
- CI additionally re-runs the packaging suite under `npm@latest` (Linux, last in the job) and
|
|
109
118
|
packs the tarball into a `node:22-slim` container with no interpreter. Neither is reproducible
|
|
110
|
-
from the
|
|
119
|
+
from the seven commands; the container one is reachable locally through
|
|
111
120
|
`tests/helpers/no-python-container.sh` with a docker daemon.
|
|
112
121
|
- **Two assertions in `tests/unit/claude.test.mjs` silently stand down** on any machine where an
|
|
113
122
|
ancestor of the temp sandbox is a `.claude`/`.bob` install — which is every developer machine
|
|
114
123
|
with Claude Code installed under `~`. They are replaced by a `t.diagnostic`, so the TAP
|
|
115
|
-
`# skipped` count does **not** move
|
|
124
|
+
`# skipped` count does **not** move, and a local run shows no sign of it.
|
|
116
125
|
|
|
117
126
|
### The gates CI does not run
|
|
118
127
|
|
|
@@ -162,11 +171,15 @@ why the commands are here and the answers are not load-bearing.
|
|
|
162
171
|
|
|
163
172
|
### 2a — Adding a DOCTRINE RULE (the common case)
|
|
164
173
|
|
|
165
|
-
Five files,
|
|
166
|
-
on purpose so that the invariant slot stays scarce.
|
|
174
|
+
Five files, plus the written-out rule totals in four test files. This is the slot most new
|
|
175
|
+
material belongs in, and it is cheap on purpose so that the invariant slot stays scarce.
|
|
167
176
|
|
|
168
177
|
1. `src/doctrines/<pack>.md` — **append** `### {{DOCTRINE}} <pack> <n> — {{DOC_<PACK>_<n>}}` plus
|
|
169
|
-
the body
|
|
178
|
+
the body, wrapped in a `LEAN` block with **both halves authored** — the heading stays outside the
|
|
179
|
+
block, the full text goes in `LEAN:begin`, and the lean half is the rule *and its mechanism*, no
|
|
180
|
+
maxims (the style rule is in `src/doctrines/README.md`, and `leanBlockProblems` refuses a rule
|
|
181
|
+
with no block or an empty half). Two gates in `constitutionProblems`
|
|
182
|
+
(`js/inspect/checks-authoring.mjs`) sit on this too: the pack named in
|
|
170
183
|
the *heading* is what addresses the rule (not the filename, so a rule filed in the wrong file is
|
|
171
184
|
reported rather than silently renamed), and ids must run **contiguously from 1**, so appending
|
|
172
185
|
at the end is the only place `n` is free. Packs are numbered independently — nothing outside
|
|
@@ -198,9 +211,18 @@ on purpose so that the invariant slot stays scarce.
|
|
|
198
211
|
|
|
199
212
|
- **No class entry.** There is no `LAW_CLASS` analogue: a doctrine rule's class *is* its pack, and
|
|
200
213
|
there is no second taxonomy over it.
|
|
201
|
-
- **No count
|
|
214
|
+
- **No count in the product.** No badge, no `SHIPPED.md` triple, no prose mirror. The console spends
|
|
202
215
|
`{N_PACKS}` / `{N_PACKS_ACTIVE}` / `{N_DOCTRINE_RULES}`, all computed at request time.
|
|
203
|
-
- **No
|
|
216
|
+
- **No renumber risk.** But there *are* written-out rule totals to bump — expected values are
|
|
217
|
+
written out, never recorded — in `tests/unit/emit_smoke.test.mjs`, `harness.test.mjs`,
|
|
218
|
+
`setup.test.mjs` and `web_api.test.mjs`. Each fails loudly with the new count; change the
|
|
219
|
+
number *and* the comment that explains it. A rule in **craft** moves more: `setup` and
|
|
220
|
+
`web_api` also pin the craft-only narrowed counts, `generate.test.mjs` excludes every craft
|
|
221
|
+
rule by address to empty the pack, and `harness.test.mjs`'s contiguity fixture plants a rule at
|
|
222
|
+
the first free craft id — the doctrine twin of §2c step 8. Grep `tests/` for the previous
|
|
223
|
+
rule's address before trusting this list. A rule long enough to matter can also breach the
|
|
224
|
+
`full` carrier ceiling in `emit_smoke` — raise it with a dated note there, as every earlier
|
|
225
|
+
move did.
|
|
204
226
|
|
|
205
227
|
⚠ **Cite only downward.** An always-on tier may never reference a toggleable one:
|
|
206
228
|
`constitutionProblems` refuses a `{{DOCTRINE}}` token anywhere under `src/ontology/` or
|
|
@@ -217,10 +239,9 @@ Steps 1 and 9 of §2c, plus `LAW_META` if the Principle line moved. Two cautions
|
|
|
217
239
|
|
|
218
240
|
- `--footprint` defaults to **lean**, and a law's lean text is the authored `LEAN:else` half of
|
|
219
241
|
its block in `src/laws/universal.md` (`resolveLean` in `js/build/render.mjs`). Amend **both**
|
|
220
|
-
halves, or the amendment does not exist for most installs
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
render at full, whatever the footprint.
|
|
242
|
+
halves, or the amendment does not exist for most installs — and a doctrine rule is no different,
|
|
243
|
+
since its rules got the same block. The on-disk `laws/`, `ontology/` and `doctrines/` copies
|
|
244
|
+
always render at full, whatever the footprint.
|
|
224
245
|
|
|
225
246
|
### 2c — Adding an INVARIANT
|
|
226
247
|
|
|
@@ -232,7 +253,7 @@ the bar is in `DESIGN.md` Decision 7, and most candidates belong in a doctrine p
|
|
|
232
253
|
citations in `js/hosts/hooks.mjs` and two OpenCode plugins, and **nothing resolves a cross-reference
|
|
233
254
|
against the canon**. Renumbering silently rewires every one of them.
|
|
234
255
|
2. `themes/_TEMPLATE.json` — add `LEX_<roman>` in template order. Gated, as in §2a step 2:
|
|
235
|
-
`constitutionProblems` holds `LEX_I..
|
|
256
|
+
`constitutionProblems` holds `LEX_I..LEX_XI` as an **equality** across all fifteen files (the
|
|
236
257
|
fourteen voices and the template), so both a missing key *and* a leftover one are reported. That
|
|
237
258
|
arm exists because the renumber left `LEX_XXII`, `LEX_XXIII`, `LEX_XXIV` and `LEX_XXXVI` behind
|
|
238
259
|
in every file and parity was silent — they were absent from nowhere.
|
|
@@ -245,7 +266,7 @@ the bar is in `DESIGN.md` Decision 7, and most candidates belong in a doctrine p
|
|
|
245
266
|
6. `README.md` — badge `badge/laws-N` and the `N universal laws` sentence.
|
|
246
267
|
7. `SHIPPED.md` — the `N laws, N agents, N skills` triple.
|
|
247
268
|
8. `tests/unit/harness.test.mjs` — the negative fixture for the `LAW_CLASS` gate plants a rule at
|
|
248
|
-
the *first free numeral*, which is `
|
|
269
|
+
the *first free numeral*, which is `XII` now that the invariants are I..XI. Bump it, or it stops
|
|
249
270
|
being a fixture and starts being a duplicate.
|
|
250
271
|
9. `CHANGELOG.md`, then rebuild and commit `web/dist` (step 5 touched `web/src`).
|
|
251
272
|
|
|
@@ -256,9 +277,9 @@ error — the heading simply stops matching and the whole tier parses to nothing
|
|
|
256
277
|
|
|
257
278
|
The carrier itself is gated: `tests/unit/emit_smoke.test.mjs` checks every rule reaches every one
|
|
258
279
|
of nine emit modes in order and themed, under both footprints, with a `CEILING` **and** a floor
|
|
259
|
-
(half the ceiling) on the rendered size. Its docblock carries the current measurement
|
|
260
|
-
|
|
261
|
-
|
|
280
|
+
(half the ceiling) on the rendered size. Its docblock carries the current measurement against
|
|
281
|
+
the largest carrier — the host-agnostic `files` bundle; no figure is repeated here, because a
|
|
282
|
+
typed headroom is stale by the next rule. Read that docblock before adding text to any tier; it is
|
|
262
283
|
re-measured, never nudged, and the ceiling is measured **with all four packs active**, which is
|
|
263
284
|
the only configuration it can speak for.
|
|
264
285
|
|
|
@@ -284,7 +305,7 @@ Rare, and mechanical. On top of §2a for each rule the pack carries:
|
|
|
284
305
|
|
|
285
306
|
## 3 — Adding a SKILL or an AGENT
|
|
286
307
|
|
|
287
|
-
**Order matters at the first two steps.** `missingReferencedSpecs` (in `js/build/
|
|
308
|
+
**Order matters at the first two steps.** `missingReferencedSpecs` (in `js/build/bundle.mjs`) *aborts the
|
|
288
309
|
build* when `AGENT.md.tmpl` names a spec with no file — so the file comes before the table row, or
|
|
289
310
|
nothing builds at all. The refusal is clean: `tests/unit/emit_gates.test.mjs:265` proves a refused
|
|
290
311
|
emit leaves the prior install intact.
|
|
@@ -537,7 +558,7 @@ only, at best, on its presence.
|
|
|
537
558
|
|---|---|---|
|
|
538
559
|
| `§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` |
|
|
539
560
|
| Per-law Principle lines | `web/src/pages/Laws.jsx` | presence and class only, never accuracy |
|
|
540
|
-
|
|
|
561
|
+
| 588 themed constitution titles (42 keys × 14 voices) — `LEX_I..LEX_XI` (11), `DOC_<PACK>_<n>` (27), `PACK_<NAME>` (4) | `themes/*.json` | key presence only — a shipped placeholder is green. `LEX_*` and `DOC_*` are held across the template too, and in both directions; `PACK_*` only across the voices |
|
|
541
562
|
| The README keyword enumerations | `README.md`, `docs/web/rules.md` | nothing |
|
|
542
563
|
| The plugin capability enumeration | `docs/web/model.md` | nothing (the number substitutes; the list does not) |
|
|
543
564
|
| Section labels and page subtitles | `web/src/lib/sections.js`, `web/src/pages/Docs/index.jsx` | nothing |
|
|
@@ -545,7 +566,7 @@ only, at best, on its presence.
|
|
|
545
566
|
| `LOADED_SIGIL` uniqueness | `themes/*.json` | nothing, and it is load-bearing for theme detection |
|
|
546
567
|
| `adapters/claude-code/settings.json` | — | nothing compares it to what the emitter writes; **it is already divergent** |
|
|
547
568
|
| `NATIVE`, twice | two test files | they check each other, which is the gate that makes the duplication safe |
|
|
548
|
-
| The emit-size `CEILING` | `tests/unit/emit_smoke.test.mjs
|
|
569
|
+
| The emit-size `CEILING` | `tests/unit/emit_smoke.test.mjs` (`const CEILING`) | a hand-transcribed measurement |
|
|
549
570
|
| Stale docblock counts | `js/build/themes.mjs:62` ("137 values" → 140), `js/build/render.mjs:116` ("145 tokens" → 148) | nothing |
|
|
550
571
|
|
|
551
572
|
---
|
|
@@ -624,7 +645,7 @@ gated. Every other appearance is derived at runtime (`.geneseed-version` markers
|
|
|
624
645
|
why no test pins a version string and none should.
|
|
625
646
|
|
|
626
647
|
Do not run `npm version`. Bump the two files by hand, add the `CHANGELOG` section, update
|
|
627
|
-
`SHIPPED.md`, rebuild `web/dist` if `web/src` moved, run the
|
|
648
|
+
`SHIPPED.md`, rebuild `web/dist` if `web/src` moved, run the seven commands from §1, push, tag,
|
|
628
649
|
then rehearse and fire the workflow:
|
|
629
650
|
|
|
630
651
|
```bash
|