geneseed 3.1.0 → 3.1.2
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 +110 -0
- package/DESIGN.md +31 -20
- package/README.md +2 -2
- package/SETUP.md +30 -3
- package/SHIPPED.md +1 -1
- package/bin/{geneseed.mjs → build-driver.mjs} +88 -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 +113 -85
- package/docs/web/rules.md +2 -0
- package/docs/web/setup-choices.md +13 -1
- 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} +45 -31
- package/js/{render.mjs → build/render.mjs} +72 -11
- package/js/{checkout.mjs → build/source.mjs} +52 -11
- 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} +68 -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} +94 -69
- 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} +34 -20
- 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} +49 -37
- package/js/{uninstall.mjs → maintain/uninstall.mjs} +62 -58
- 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 +124 -89
- package/js/web/activity.mjs +22 -21
- package/js/web/api.mjs +88 -58
- 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 +29 -21
- package/js/web/routes.mjs +223 -0
- package/js/web/server.mjs +56 -987
- package/package.json +11 -3
- package/src/AGENT.md.tmpl +1 -1
- package/src/doctrines/README.md +1 -1
- package/themes/opencode/README.md +1 -1
- package/web/dist/assets/{About-_MnJcwb_.js → About-CPvIFZGh.js} +1 -1
- package/web/dist/assets/{Activity-DpjqO-FR.js → Activity-z0uXdS7J.js} +1 -1
- package/web/dist/assets/{ActivityDetail-Bvrjv8oW.js → ActivityDetail-CEj5IJpF.js} +1 -1
- package/web/dist/assets/{Diff-DZ0IehBV.js → Diff-BMR8fPuG.js} +1 -1
- package/web/dist/assets/{Doctor-cu4krooO.js → Doctor-Bz5qE4r9.js} +1 -1
- package/web/dist/assets/{Graph-DUOPY65j.js → Graph-BV6a6t2l.js} +1 -1
- package/web/dist/assets/{Harnesses-B5Osg-BF.js → Harnesses-C5Ag5PaQ.js} +1 -1
- package/web/dist/assets/Laws-BK07NKw9.js +3 -0
- package/web/dist/assets/{Library-8y69I3_z.js → Library-CiXN9sC3.js} +1 -1
- package/web/dist/assets/{Markdown-Ds5GS1OD.js → Markdown-DG6FjUz-.js} +1 -1
- package/web/dist/assets/{Profile-7Jxa7ZUX.js → Profile-B2oSvdxH.js} +1 -1
- package/web/dist/assets/{Rules-UZ0jWkjg.js → Rules-DobVPd80.js} +1 -1
- package/web/dist/assets/{Skills-Bgkl0xLd.js → Skills-DQPsvsn9.js} +1 -1
- package/web/dist/assets/{StatusBadge-Cx4caF_2.js → StatusBadge-BhJ3HhdI.js} +1 -1
- package/web/dist/assets/{Themes-De8az7Sv.js → Themes-BP38FBU9.js} +1 -1
- package/web/dist/assets/{activity-VDAdVQ3q.js → activity-D9mfv7pY.js} +1 -1
- package/web/dist/assets/index-BE226Gv6.css +1 -0
- package/web/dist/assets/{index-BKrjBRV4.js → index-BZ7cPLMI.js} +1 -1
- package/web/dist/assets/index-BgIs5eIa.js +12 -0
- package/web/dist/assets/{index-wNiNzFFQ.js → index-DGM5j2hB.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/src/pages/Laws.jsx +135 -69
- package/docs/declined.md +0 -200
- package/docs/limits.md +0 -329
- package/js/doctor.mjs +0 -1712
- package/js/emit.mjs +0 -1410
- package/web/dist/assets/Laws-D8DJgTBp.js +0 -3
- package/web/dist/assets/index-1pPSz7Kl.css +0 -1
- package/web/dist/assets/index-C-gb-q-r.js +0 -12
package/docs/design-history.md
CHANGED
|
@@ -19,7 +19,7 @@ something you can open. The same goes for the `tests/test_*.py` gates named thro
|
|
|
19
19
|
were real, they were green, and they are gone — their Node successors are named in
|
|
20
20
|
`tests/ported.json`. Where a passage says "now" it means *now, at that phase*.
|
|
21
21
|
|
|
22
|
-
This annex is deliberately **not** de-Pythoned. Rewriting `_build_render.py` to `js/render.mjs`
|
|
22
|
+
This annex is deliberately **not** de-Pythoned. Rewriting `_build_render.py` to `js/build/render.mjs`
|
|
23
23
|
throughout would destroy the one thing it documents — that there were two implementations, and
|
|
24
24
|
what it cost to keep them byte-identical. What was wrong was filing it under a header that
|
|
25
25
|
promised a spec someone should read before changing structure. That spec is now
|
|
@@ -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/render.mjs`](js/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/native.mjs`](js/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/opencode.mjs`](js/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,13 +61,13 @@ 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
|
-
[`js/emit.mjs`](js/emit.mjs) writes the bundle (`build`) and the OpenCode layer's RENDER
|
|
64
|
+
[`js/build/emit.mjs`](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
|
|
68
68
|
is that the two are indistinguishable. `GENESEED_NO_JS=1` forces the Python path.
|
|
69
69
|
**The child's stdout carries the protocol document and nothing else — structurally.**
|
|
70
|
-
`js/emit.mjs` replaces `process.stdout.write` and `process.stderr.write` with buffers for
|
|
70
|
+
`js/build/emit.mjs` replaces `process.stdout.write` and `process.stderr.write` with buffers for
|
|
71
71
|
the whole run and restores them only to emit that document, so a stray `console.log`
|
|
72
72
|
cannot corrupt the handoff; it lands in the payload, Python re-prints it, and the byte
|
|
73
73
|
comparison fails. Python re-emits both buffers through its own `print`, which is what
|
|
@@ -93,7 +93,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
93
93
|
pre-switch vocabulary by design — and a cell whose carve-out excuses nothing fails too.
|
|
94
94
|
- **All nine emits cross that seam** — and this bullet said so for two phases while eight
|
|
95
95
|
did. `emit_opencode_global` spawned Node **zero** times: its render half was inline
|
|
96
|
-
Python, `js/emit.mjs` offered no job kind for it, and `tests/test_emit_boundary.py` had no
|
|
96
|
+
Python, `js/build/emit.mjs` offered no job kind for it, and `tests/test_emit_boundary.py` had no
|
|
97
97
|
cell for it either, which is why nothing contradicted the sentence.
|
|
98
98
|
`tests/test_seam_coverage.py` measures the spawn count of every mode, fails if the table
|
|
99
99
|
drifts, and **refuses a mode that crosses without a boundary cell** — so the count above
|
|
@@ -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/settings.mjs`](js/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
|
|
@@ -228,7 +228,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
228
228
|
six at once. Four of them write `settings.json` hooks, so they reach `hookPrefix`, which
|
|
229
229
|
needs the two machine-absolute values `_hook_runner_entry()` returns — `sys.executable`
|
|
230
230
|
and the harness entry point. **A Node driver has no `sys.executable`**: the hooks it would
|
|
231
|
-
be wiring are Python and `process.execPath` is node. `js/emit.mjs` gates the whole
|
|
231
|
+
be wiring are Python and `process.execPath` is node. `js/build/emit.mjs` gates the whole
|
|
232
232
|
settings path behind `!isCopilot` and skips VERIFY for the same host, so the Copilot pair
|
|
233
233
|
rides the shared engine without touching either — which buys the entire driver body
|
|
234
234
|
(manifest-with-`managed`, prune, atomic write) while leaving the interpreter question
|
|
@@ -259,7 +259,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
259
259
|
`statSync` on the reparse point raises `EACCES`, so the plain spelling already skips the
|
|
260
260
|
thing the launcher needed a subprocess to detect. (End of the superseded passage.)
|
|
261
261
|
**VERIFY did not need porting; it needed calling.** `settingsIntegrityCheck` has been
|
|
262
|
-
complete in `js/settings.mjs` since P3a with no production caller, so the "largest unported
|
|
262
|
+
complete in `js/hosts/settings.mjs` since P3a with no production caller, so the "largest unported
|
|
263
263
|
unit left" was one call site. What changed with it is what it proves: on the Python driver
|
|
264
264
|
the stage runs in Python *after* a Node child did the wiring, making it a live
|
|
265
265
|
cross-implementation check on every build; with both halves in Node that property is gone
|
|
@@ -299,7 +299,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
299
299
|
location. Two `cfg` keys are deliberately **absent**, and their absence is load-bearing:
|
|
300
300
|
`js_cfg()` always sends `structure` and `capabilityLinkRe` because the Python originals are
|
|
301
301
|
module names tests mutate, but this driver has no Python module to mutate — so
|
|
302
|
-
`js/render.mjs`'s `cfg.structure ?? STRUCTURE` and `js/emit.mjs`'s `capabilityLinkRe`
|
|
302
|
+
`js/build/render.mjs`'s `cfg.structure ?? STRUCTURE` and `js/build/emit.mjs`'s `capabilityLinkRe`
|
|
303
303
|
fallback take their right-hand branches for the first time since they were written. They
|
|
304
304
|
were unreachable, not untested. A second driver is what reached them.
|
|
305
305
|
**A byte gate cannot tell two implementations from one**, so the driver owes a process
|
|
@@ -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/hooks.mjs`](js/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
|
|
@@ -404,20 +404,20 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
404
404
|
`harness_golden.py` reads stdout through a universal-newline decoder, so the difference
|
|
405
405
|
was invisible to the entire matrix — the same shape as the shim's exclusion from
|
|
406
406
|
`golden.py`. Harmless while nothing baked the Node entry; the bytes a real host reads once
|
|
407
|
-
something does. `js/hooks.mjs`'s two output funnels translate, and one gate reads raw
|
|
407
|
+
something does. `js/hosts/hooks.mjs`'s two output funnels translate, and one gate reads raw
|
|
408
408
|
bytes.
|
|
409
409
|
The acceptance harness is what kept the port from being dead code before any of this — it
|
|
410
410
|
executes `bin/geneseed-hook.mjs` as a real process against the Python one, which is the
|
|
411
|
-
lesson from a stage that sat complete and uncalled in `js/settings.mjs` for two phases and
|
|
411
|
+
lesson from a stage that sat complete and uncalled in `js/hosts/settings.mjs` for two phases and
|
|
412
412
|
was then briefed as unported.
|
|
413
413
|
**Adding a module that legitimately spawns opened a door the driver's own gate cannot see
|
|
414
414
|
through**: `test_the_driver_imports_no_child_process_module` greps `bin/geneseed.mjs`'s
|
|
415
|
-
source, and one `import` of `js/hooks.mjs` would put `child_process` in the driver's
|
|
415
|
+
source, and one `import` of `js/hosts/hooks.mjs` would put `child_process` in the driver's
|
|
416
416
|
process with that source still clean. `tests/test_hook_cli_parity.py` walks the driver's
|
|
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/status.mjs`](js/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
|
|
@@ -448,7 +448,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
448
448
|
a candidate ordered *after* the unfenceable `ROOT/"Harness"`), and `_accent_for`'s
|
|
449
449
|
fallback — plus `pyLen`/`pyLjust`, because a reproduction of a language primitive needs a
|
|
450
450
|
corpus rather than a cell.
|
|
451
|
-
**And the corpus found a live defect in shipped code.** `js/hooks.mjs` carried its own
|
|
451
|
+
**And the corpus found a live defect in shipped code.** `js/hosts/hooks.mjs` carried its own
|
|
452
452
|
`str(Path(p))` as `pyStrPath`, built on `path.normalize`; P5c had already found that
|
|
453
453
|
`normalize` collapses `a/../b` where `PurePath` keeps it, fixed `js/lib/fs.mjs`'s
|
|
454
454
|
`pyPathStr`, and gated it with a 25-path corpus — which never reached the twin, because a
|
|
@@ -459,7 +459,7 @@ 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/excludes.mjs`](js/excludes.mjs) behind
|
|
462
|
+
is now [`js/inspect/excludes.mjs`](js/inspect/excludes.mjs) behind
|
|
463
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
|
|
@@ -467,8 +467,8 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
467
467
|
**Where to put it was the whole question, and two of the three answers cost something
|
|
468
468
|
specific.** Growing `bin/geneseed-hook.mjs` would have meant relaxing
|
|
469
469
|
`test_the_entry_carries_exactly_the_verbs_the_emitter_wires` from equality to a subset
|
|
470
|
-
relation — the gate the machine-wide shim now rests on — *and* loading `js/settings.mjs`
|
|
471
|
-
plus `js/emit.mjs`, ~2,100 lines, on every `PreToolUse` call, against a spec that says the
|
|
470
|
+
relation — the gate the machine-wide shim now rests on — *and* loading `js/hosts/settings.mjs`
|
|
471
|
+
plus `js/build/emit.mjs`, ~2,100 lines, on every `PreToolUse` call, against a spec that says the
|
|
472
472
|
shim must exec a minimal entry. Putting it in `bin/geneseed.mjs` would have crossed two
|
|
473
473
|
different Python programs: that file is `build.py`'s `main()`, parses generator flags with
|
|
474
474
|
a partition asserted over them, and carries a `child_process` ban that `web` and `upgrade`
|
|
@@ -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/generate.mjs`](js/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
|
|
@@ -536,9 +536,9 @@ 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/diff.mjs`](js/diff.mjs) and `cmdRebuildAll` in
|
|
540
|
-
[`js/generate.mjs`](js/generate.mjs), with the install detectors extracted to
|
|
541
|
-
[`js/installs.mjs`](js/installs.mjs) and the registry to [`js/registry.mjs`](js/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
|
|
@@ -582,7 +582,7 @@ 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/doctor.mjs`](js/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
588
|
[`tests/helpers/cli_golden.mjs`](tests/helpers/cli_golden.mjs) gives each cell its own copy of the
|
|
@@ -599,7 +599,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
599
599
|
against what it wrote) is the only honest source of a bundle a fresh render *agrees* with.
|
|
600
600
|
**A `git ls-files` fixture cannot see the file you are writing.** The first draft copied the
|
|
601
601
|
*tracked* set, so every cell reported the candidate dying on `ERR_MODULE_NOT_FOUND`:
|
|
602
|
-
`js/doctor.mjs` was untracked until it was committed. The fixture mirrors the working tree
|
|
602
|
+
`js/inspect/doctor.mjs` was untracked until it was committed. The fixture mirrors the working tree
|
|
603
603
|
now, ignored paths excluded, which is also what keeps `node_modules/` out of 27 copies.
|
|
604
604
|
**The `child_process` ban became an allow-list, on the condition its own docstring named.**
|
|
605
605
|
`_authoring_problems` runs `node --check` over the OpenCode plugins and there is no
|
|
@@ -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/uninstall.mjs`](js/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/setup.mjs`](js/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
|
|
@@ -694,7 +694,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
694
694
|
The menu's three fallback arms — an in-range index, a *parsed* out-of-range one, and an
|
|
695
695
|
unparseable answer matched against the option keys — are told apart by nothing else.
|
|
696
696
|
**The `child_process` allow-list became a table.** `_lsp_prereqs` runs `java -version` to
|
|
697
|
-
see whether OpenCode's `jdtls` has a JVM, so `js/setup.mjs` is the second module on this
|
|
697
|
+
see whether OpenCode's `jdtls` has a JVM, so `js/maintain/setup.mjs` is the second module on this
|
|
698
698
|
entry allowed to spawn. The gate now carries the module, the binding, the call count and
|
|
699
699
|
the literal argv for each one, cross-checked against the source — the port's rule that the
|
|
700
700
|
second instance of anything stops being a special case.
|
|
@@ -764,7 +764,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
764
764
|
`excludesSnapshot()` unmodified; `api_installs` is a row per `installTargets()` entry
|
|
765
765
|
through five detectors P5d and P5f had already ported. That is the same discovery every
|
|
766
766
|
P5 sub-phase made, one layer up.
|
|
767
|
-
**One parameter had to grow: `doctorCollect({groups})`.** `js/doctor.mjs` already carried
|
|
767
|
+
**One parameter had to grow: `doctorCollect({groups})`.** `js/inspect/doctor.mjs` already carried
|
|
768
768
|
a `ran(check, label, probs)` whose label went nowhere, with a docblock saying it stayed
|
|
769
769
|
because it is the one place each check is named. `/api/doctor` renders one card per
|
|
770
770
|
check, so this is the phase that fills it. `on_progress=` is still absent and still P7's.
|
|
@@ -797,7 +797,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
797
797
|
path inside a JSON string, where the backslashes made `\U` an invalid escape — the
|
|
798
798
|
reference then swallowed the parse error and returned the empty stub, which reads exactly
|
|
799
799
|
like a working exclusion list. And the first parity run found a real port bug in
|
|
800
|
-
`js/installs.mjs`: `readMaybe` was a bare `readFileSync` where `Path.read_text` opens in
|
|
800
|
+
`js/hosts/installs.mjs`: `readMaybe` was a bare `readFileSync` where `Path.read_text` opens in
|
|
801
801
|
TEXT mode and folds `\r\n` to `\n`. Every caller until now read a single-line marker and
|
|
802
802
|
trimmed it, so five phases never noticed; `/api/profile` is the first consumer that hands
|
|
803
803
|
the whole decoded text back out, and it failed on both the text and the sha256 of it.
|
|
@@ -822,11 +822,11 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
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
824
|
[`js/web/api.mjs`](js/web/api.mjs), over a new
|
|
825
|
-
[`js/inventory.mjs`](js/inventory.mjs) — `_harness_tui.py`'s catalog half. **Still
|
|
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
|
-
**The measurement is the finding.** `js/status.mjs` had said since P5d that "the ~111
|
|
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`,
|
|
829
|
-
`LAW_CLASSES` and `ENTITY_STATUSES` had in fact crossed in P5g, inside `js/doctor.mjs`,
|
|
829
|
+
`LAW_CLASSES` and `ENTITY_STATUSES` had in fact crossed in P5g, inside `js/inspect/doctor.mjs`,
|
|
830
830
|
because doctor's authoring gates are what validate them. So P6c moved the four to the
|
|
831
831
|
module that mirrors where Python keeps them and had doctor import them back, rather than
|
|
832
832
|
writing a second copy — a copy of a value under test stops being the value under test the
|
|
@@ -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/mcp.mjs`](js/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
|
|
@@ -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/uninstall.mjs`](js/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/update.mjs`](js/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
|
|
@@ -1049,7 +1049,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
1049
1049
|
the verb**. `pullAndValidate` fast-forwards and then has to ask the PULLED source whether it
|
|
1050
1050
|
is sound, and `rebuildBundle` has to render with the PULLED generator — an in-process
|
|
1051
1051
|
`cmdDoctor` would validate the modules this process loaded before the merge, pass, and then
|
|
1052
|
-
render the old source while reporting an upgrade. `js/generate.mjs`'s `cmdBuild` calls the
|
|
1052
|
+
render the old source while reporting an upgrade. `js/build/generate.mjs`'s `cmdBuild` calls the
|
|
1053
1053
|
driver in-process and is right to; the discriminator is whether anything changed underneath.
|
|
1054
1054
|
**"No cell reaches the network" is enforced by git, not by the fixture.** A fixture that
|
|
1055
1055
|
merely points `origin` somewhere safe is a claim about the fixture. `GIT_ALLOW_PROTOCOL=file`
|
|
@@ -1249,7 +1249,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
1249
1249
|
**The panel itself is declared, and the declaration is asserted from both directions**: the
|
|
1250
1250
|
reference is shown drawing one, and the port is shown emitting no alt-screen escape, no
|
|
1251
1251
|
cursor hide and no section header — plus a structural check that there is nothing in
|
|
1252
|
-
`js/tui.mjs` that *could* draw one. A declaration that only says "we did not" is a claim
|
|
1252
|
+
`js/ui/tui.mjs` that *could* draw one. A declaration that only says "we did not" is a claim
|
|
1253
1253
|
about one run; this says there is no screen to reach.
|
|
1254
1254
|
The phase's own worst defect was a fixture, not a port: a scripted key list feeding a
|
|
1255
1255
|
BLOCKING reader deadlocked the suite the moment it ran dry — and it did not hang when the
|
|
@@ -1275,7 +1275,7 @@ pointer whatever the prose around it says. Dead **prose paths** are left as writ
|
|
|
1275
1275
|
`dict.get` against the JavaScript prototype chain, `str.center`'s `marg & width & 1` term,
|
|
1276
1276
|
Python's non-negative modulo, floor division into a repeat count that *throws* where Python
|
|
1277
1277
|
empties, strict `int()` for `COLUMNS`, and `max(default=0)` against `Math.max()`. It went
|
|
1278
|
-
into a new module rather than `js/tui.mjs`, so that module's "contains no escape sequence"
|
|
1278
|
+
into a new module rather than `js/ui/tui.mjs`, so that module's "contains no escape sequence"
|
|
1279
1279
|
assertion — the thing that makes the panel's declaration a measurement — survived untouched.
|
|
1280
1280
|
**The empty-inventory crash P7b recorded is fixed**: an inventory with no agents, skills or
|
|
1281
1281
|
laws crashed the panel on its first frame, and the guard went at the call site rather than in
|