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/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,129 @@ labels in `harness.config.json`. The canonical identity of an *installed* harnes
|
|
|
6
6
|
is the source fingerprint in `.geneseed-version` (see `geneseed version`), not this
|
|
7
7
|
label. For the capability ↔ spec map, see [SHIPPED.md](SHIPPED.md).
|
|
8
8
|
|
|
9
|
-
## [
|
|
9
|
+
## [3.1.3] — 2026-08-24
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **The Journal Atlas console.** A fourth dashboard lens, `journal`: growth rings per section,
|
|
14
|
+
the newest hand-authored entries, and the constitution drawn as a map — a hub of rules in
|
|
15
|
+
force, its three tiers, and dashed edges to the skills, agents, memory and wiki they govern.
|
|
16
|
+
Every node navigates. The map is data-driven where the design was hand-placed: circles size
|
|
17
|
+
to the text they hold and the pack nodes ride a fitted arc, so a harness with seven
|
|
18
|
+
invariants or six packs draws the same picture rather than overflowing it.
|
|
19
|
+
- **`atlas`, a twelfth console skin** — loam dark, sage ink, Space Grotesk, pills, in both
|
|
20
|
+
light and dark. Atlas and Greenhouse default to the journal lens; every other skin is
|
|
21
|
+
unchanged, and the lens is pickable from any of them in Settings.
|
|
22
|
+
- **`GET /api/recent`** — the newest file-backed entries across the harness. Nothing on the API
|
|
23
|
+
carried a per-entry date: every catalog row is `{name, title, desc, source}` and a browser
|
|
24
|
+
cannot stat a path. It answers only the four sections a person writes by hand, and drops any
|
|
25
|
+
section too large to stat exhaustively rather than reporting the newest of a truncated
|
|
26
|
+
sample. Declared in `GET_BEYOND_REF` — the GET twin of `POST_BEYOND_REF` — so the route
|
|
27
|
+
partition keeps its set equality against a reference surface that must not be rewritten.
|
|
28
|
+
- **`overview.install` now reports `build_epoch`** beside `build_time`, so a relative label is
|
|
29
|
+
computed rather than parsed back out of a formatted stamp.
|
|
30
|
+
- **`Ctrl`/`⌘`+`K` opens Spotlight**, beside `/`. The chord is the one that still works from
|
|
31
|
+
inside a text field, which is where `/` deliberately does not fire.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **The rail regroups into Codex · Care · Setup**, with the Dashboard on its own and a vitals
|
|
36
|
+
card above the index — germination, drift, the doctor's verdict, and how long ago the harness
|
|
37
|
+
was built, on every page. Doctor badges `clean` in words; it used to badge nothing, which
|
|
38
|
+
reads the same as never having run.
|
|
39
|
+
- **Harnesses and Themes merge into one Harness page**, and About folds into Settings. No route
|
|
40
|
+
dies: `#/harnesses`, `#/themes` and `#/about` all still resolve. An uninstalled host now
|
|
41
|
+
offers one `Install…` that discloses its four choices instead of rendering them inline.
|
|
42
|
+
- **The voice gallery is a reference, not a second control.** Every install picks its own voice
|
|
43
|
+
beside its own footprint, posture and mode; a gallery at the top of the page could only ever
|
|
44
|
+
act on one of them. It keeps the taglines — which a `<select>` of bare names cannot show —
|
|
45
|
+
and gives up the buttons.
|
|
46
|
+
- **The rail width is responsive**, `clamp(232px, 18vw, 256px)`, between the two widths the
|
|
47
|
+
console has been designed at. The breakpoint overrides still win, so it still collapses.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- **The Spotlight input was never wired to its own listbox.** No `role="combobox"`,
|
|
52
|
+
`aria-expanded`, `aria-controls`, or `aria-activedescendant`, and the rows had no ids —
|
|
53
|
+
so the arrow keys moved a highlight that was visual and nothing else, and a screen reader
|
|
54
|
+
was never told results had appeared.
|
|
55
|
+
- **The rail overflowed the document** on short windows once the vitals card was added: a fixed
|
|
56
|
+
`100vh` box with no scroller of its own produced a second scrollbar beside the page's.
|
|
57
|
+
- **`web/dist` is now gated on the publish path.** It ships in `files[]`, and until now nothing
|
|
58
|
+
on that path built it or looked at it — the freshness check ran only on pull requests, while
|
|
59
|
+
`workflow_dispatch` can publish any ref.
|
|
60
|
+
- **Two tests in `laws.test.jsx` failed roughly one run in three.** They gated on rows existing,
|
|
61
|
+
but the pack selection is seeded in an effect, so there is a rendered frame in which every
|
|
62
|
+
pack reads as off. The same race, twice, and neither was a product defect.
|
|
63
|
+
|
|
64
|
+
## [3.1.2] — 2026-08-24
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
- **`js/` is now domain folders, not a flat drawer.** The tree splits into `build/`, `hosts/`,
|
|
70
|
+
`inspect/`, and `main/`, and the four largest modules split with it: `server.mjs` into
|
|
71
|
+
routes / handler / daemon / server, `emit.mjs` into common / stubs / version / bundle,
|
|
72
|
+
`doctor.mjs` into scan plus three check groups, and `fs.mjs` into fs / json / paths / text.
|
|
73
|
+
The Node generator driver is renamed to `bin/build-driver.mjs`; the `geneseed-build` bin name
|
|
74
|
+
is unchanged, so nothing on an installed harness has to move.
|
|
75
|
+
|
|
76
|
+
**No emitted byte moves.** The 261-cell corpus replays identical across all 14 themes, which
|
|
77
|
+
is the whole warrant for a reorganization this size — 151 files, 8502 insertions, 6481
|
|
78
|
+
deletions, and zero observable difference for anyone who installed 3.1.1.
|
|
79
|
+
|
|
80
|
+
- **Docs point at the code that exists.** A module map and front door in `js/README.md`, with a
|
|
81
|
+
doctor arm that keeps them honest; 201 stale paths repointed in prose, the 35 split-module
|
|
82
|
+
pointers resolved, and 69 docblock claims corrected where the code had stopped supporting
|
|
83
|
+
what the comment promised. The vestigial `emit.mjs` is deleted.
|
|
84
|
+
|
|
85
|
+
- **The Python-era `py` identifier prefix is retired**, and `js/ bin/ tests/ adapters/` are now
|
|
86
|
+
linted — the half of the tree that never was.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
- **Two of "the four things that are genuinely hard" are resolved rather than rewritten.**
|
|
90
|
+
`docs/extending.md` §5.2 — seven theme keys pinned byte-for-byte, so restyling a tagline was a
|
|
91
|
+
red gate — and §5.4 — *a snapshot red is a finding, not a step*. Theme text is free to edit,
|
|
92
|
+
and a red test is a step again. Editing a CLI verb's help text or flags is an ordinary edit.
|
|
93
|
+
|
|
94
|
+
- **The standing rule is now "expected values are written out, not recorded"**: change the
|
|
95
|
+
expectation and the sentence that explains it in the same commit, and say why in the message.
|
|
96
|
+
|
|
97
|
+
### Added
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
- **A gate on how a runtime dependency may enter**: vendored under `js/vendor/<name>/` as tracked
|
|
101
|
+
source behind a relative import, never installed. Fixtures run from the OS temp root and cannot
|
|
102
|
+
resolve a bare specifier, and neither can a fresh `git clone`; devDependencies are unaffected.
|
|
103
|
+
|
|
104
|
+
### Removed
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
- **The recorded corpora, and the notion of a corpus with them.** `tests/__snapshots__/` held
|
|
108
|
+
2.6 MB of transcripts taken from the CPython reference before it was deleted, replayed by
|
|
109
|
+
`tests/snapshot/`. With no recorder left they could never be re-blessed, so a red run could
|
|
110
|
+
only ever mean *you changed something*, never *you broke something* — and every deliberate
|
|
111
|
+
change to emitted text arrived as a red gate. The 1381-cell retirement of 2026-08-17 had
|
|
112
|
+
already made the argument; this finishes it.
|
|
113
|
+
|
|
114
|
+
**What they owned is now written out.** `wrapText`, `dwidth` and `formatHelp` had zero unit
|
|
115
|
+
coverage — the recordings were their only gate — so `tests/unit/text_layout.test.mjs` states
|
|
116
|
+
their rules explicitly and renders all 29 verbs. The JSONC, Windows-PATH and token-report
|
|
117
|
+
tables moved inline; `sync_themes` became an owned fixture with plain-text expectations. The
|
|
118
|
+
"corpus contains what it is named for" gates went with them: when the apostrophe is visible in
|
|
119
|
+
the file, nothing has to assert it is there.
|
|
120
|
+
|
|
121
|
+
Recoverable at tag `corpus-reference-v3.1.2`. Nothing can regenerate them.
|
|
122
|
+
|
|
123
|
+
- **Three migration statistics.** "N of the M subcommands run from Node", "0 commands have no
|
|
124
|
+
Node twin", and a native-verb list duplicated across two files purely so each could police the
|
|
125
|
+
other. A migration percentage is not a fact about a finished tool. The durable property under
|
|
126
|
+
the first two — every declared verb is dispatched by an entry point — is now asserted directly.
|
|
127
|
+
|
|
128
|
+
- **`golden.mjs --against`**, which could not have worked since the emit corpus was retired, and
|
|
129
|
+
`orphanCheck`, `narrowingReason` and `tests/helpers/snapshot_io.mjs` with it. The three
|
|
130
|
+
surviving modes are all self-comparisons.
|
|
131
|
+
|
|
10
132
|
|
|
11
133
|
## [3.1.1] — 2026-08-21
|
|
12
134
|
|
package/DESIGN.md
CHANGED
|
@@ -35,7 +35,7 @@ vault or a specific tool's hooks.
|
|
|
35
35
|
names (`ONT_TELOS`, `ONT_EVIDENCE`, `ONT_DECISIONS`, `ONT_CONDUCT`), a few rare
|
|
36
36
|
technical nouns (`Context`, `Scripts`, `Charter`), and the folder names (`laws/`,
|
|
37
37
|
`ontology/`, `doctrines/`, `agents/`, `skills/`, `memory/`, `notebook/` via `DIR_*`).
|
|
38
|
-
These live in the `STRUCTURE` map in `js/render.mjs` and are laid on top of every
|
|
38
|
+
These live in the `STRUCTURE` map in `js/build/render.mjs` and are laid on top of every
|
|
39
39
|
render, so a theme can never move a path, a link, or a heading number. No theme file
|
|
40
40
|
defines a `DIR_*` or an `ONT_*` key, and none may: an ontology citation spells the
|
|
41
41
|
section on both sides (`{{ONTOLOGY}}: {{ONT_TELOS}}`), so heading and reference move
|
|
@@ -102,7 +102,7 @@ vault or a specific tool's hooks.
|
|
|
102
102
|
Rules, `I`..`IX`, headed `### {{LAW}} <roman> — {{LEX_<roman>}}`.
|
|
103
103
|
- **Doctrines** (`src/doctrines/{craft,rigor,ops,process}.md`) — *how work is done
|
|
104
104
|
here*. Practice rules addressed by pack and number, cited as `Doctrine process 5`.
|
|
105
|
-
`PACK_ORDER` (`js/
|
|
105
|
+
`PACK_ORDER` (`js/build/source.mjs`) fixes the order craft → rigor → ops → process, which
|
|
106
106
|
is narrative and deliberately not alphabetical.
|
|
107
107
|
|
|
108
108
|
**The Ontology and the Invariants are never toggleable** — every build carries both,
|
|
@@ -128,7 +128,7 @@ vault or a specific tool's hooks.
|
|
|
128
128
|
repeal one; a user rule outranks a doctrine rule outright; nothing outranks an
|
|
129
129
|
invariant. And an always-on tier may never cite a toggleable one — a `--doctrines craft`
|
|
130
130
|
build would otherwise ship an invariant pointing at text its `AGENT.md` does not
|
|
131
|
-
contain — so `constitutionProblems` (`js/
|
|
131
|
+
contain — so `constitutionProblems` (`js/inspect/checks-authoring.mjs`) refuses a `{{DOCTRINE}}` token
|
|
132
132
|
anywhere under `src/ontology/` or `src/laws/`.
|
|
133
133
|
|
|
134
134
|
The bar for a **new invariant** is the highest in the tree: universal (it binds *every*
|
|
@@ -146,9 +146,9 @@ vault or a specific tool's hooks.
|
|
|
146
146
|
|---|---|---|---|
|
|
147
147
|
| body | append to `src/laws/universal.md` — never insert; nothing resolves a cross-reference against the canon | append to `src/doctrines/<pack>.md`, `### {{DOCTRINE}} <pack> <n> — {{DOC_<PACK>_<n>}}`; ids must run contiguously from 1 | a new `src/doctrines/<pack>.md` with its `**Name** — lead line` |
|
|
148
148
|
| themed title | `LEX_<roman>` in all 15 theme files | `DOC_<PACK>_<n>` in all 15 theme files | `PACK_<NAME>` in all 15, plus each rule's `DOC_*` |
|
|
149
|
-
| class | `LAW_CLASS` in `js/inventory.mjs`, one of the six in `LAW_CLASSES` | **none** — a doctrine rule's class *is* its pack, and there is no second taxonomy over it | — |
|
|
149
|
+
| class | `LAW_CLASS` in `js/inspect/inventory.mjs`, one of the six in `LAW_CLASSES` | **none** — a doctrine rule's class *is* its pack, and there is no second taxonomy over it | — |
|
|
150
150
|
| console copy | a `LAW_META` row in `web/src/pages/Laws.jsx` (keyed by arabic number) | a `DOCTRINE_META` row in the same file (keyed `<pack>.<n>`) | one `DOCTRINE_META` row per rule |
|
|
151
|
-
| registration | the numeral is the registration | the number is the registration | the pack name in `PACK_ORDER`, `js/
|
|
151
|
+
| registration | the numeral is the registration | the number is the registration | the pack name in `PACK_ORDER`, `js/build/source.mjs` |
|
|
152
152
|
| renumber risk | high — appending is the only safe move | none — packs are numbered independently | none |
|
|
153
153
|
| counts | README badge + the `N universal laws` prose, `SHIPPED.md`'s triple, the web onboarding copy | **none** — the console spends the `N_PACKS` / `N_PACKS_ACTIVE` / `N_DOCTRINE_RULES` count tokens, all computed at request time | none, same reason |
|
|
154
154
|
| changelog | `CHANGELOG.md` | `CHANGELOG.md` | `CHANGELOG.md` |
|
|
@@ -160,7 +160,7 @@ vault or a specific tool's hooks.
|
|
|
160
160
|
synonym. `--sync-themes` writes the template's placeholder and only *prints* which keys
|
|
161
161
|
to restyle; a shipped placeholder passes every gate.
|
|
162
162
|
|
|
163
|
-
Almost all of it is gated, across six named checks in `js/
|
|
163
|
+
Almost all of it is gated, across six named checks in `js/inspect/checks-build.mjs` —
|
|
164
164
|
`themeParityProblems` (key parity across the voices), `lawMetaProblems` and
|
|
165
165
|
`doctrineMetaProblems` (the console's Principle column, in both directions),
|
|
166
166
|
`constitutionProblems` (pack numbering and filing, `LEX_I..LEX_IX` as an *equality*, the
|
|
@@ -195,11 +195,11 @@ theme file defines a `DIR_*` key, so `laws/` is `laws/` in all fourteen voices.
|
|
|
195
195
|
| Notebook | `src/notebook/` | `notebook/` | the agent's sovereign space — any medium, seed-once charter the agent may rewrite; only `.gitignore` re-asserted |
|
|
196
196
|
| Posture | `src/postures/` | inlined into `AGENT.md` | how much ceremony the agent brings to a task |
|
|
197
197
|
| Mode | `src/modes/` | inlined into `AGENT.md` | the working axis (solo, foreman, …) — orthogonal to posture |
|
|
198
|
-
| Context | `js/
|
|
198
|
+
| Context | `js/build/stubs.mjs` | `context.json` | empty per-repo manifest, written once and never overwritten; git-ignore it |
|
|
199
199
|
| Themes | `themes/*.json` | — | token → label maps; `themes/opencode/` is a separate colour system |
|
|
200
200
|
| Release label | `harness.config.json` | `.geneseed-version` | the theme and the human-readable version the driver reads; the canonical identity is the source fingerprint, not this string |
|
|
201
201
|
| Lifecycle | `registry.json` | — | maintainer-side status/version/owner per agent and skill; never rendered into a bundle |
|
|
202
|
-
| Generator | `bin/
|
|
202
|
+
| Generator | `bin/build-driver.mjs` (`geneseed-build`) | — | substitution + `<!-- INCLUDE: -->` inlining, and the nine `--emit` targets |
|
|
203
203
|
| CLI, as data | `js/cli-table.json` | — | the owned document the entry point parses its own argv from |
|
|
204
204
|
| Automation | `bin/geneseed-cli.mjs` (`geneseed`) | — | 25 verbs — `build` / `doctor` / `status` / `prompt` / `diff` / `setup` / `web` / `upgrade` / … |
|
|
205
205
|
| Hooks | `bin/geneseed-hook.mjs` (`geneseed-hook`) | — | the four verbs an emitted `settings.json` invokes: `context` / `git-gate` / `rule-gate` / `learn` |
|
|
@@ -212,28 +212,28 @@ theme file defines a `DIR_*` key, so `laws/` is `laws/` in all fourteen voices.
|
|
|
212
212
|
- Unknown tokens are left visible (debugging aid); `doctor` flags them.
|
|
213
213
|
- Node built-ins only; no third-party dependencies, ever. (`package.json` carries neither a
|
|
214
214
|
`dependencies` nor a `devDependencies` key, and a test asserts it.)
|
|
215
|
-
- **`js/settings.mjs` is the half of the emit that edits files you co-own** — the
|
|
215
|
+
- **`js/hosts/settings.mjs` is the half of the emit that edits files you co-own** — the
|
|
216
216
|
`settings.json` / `opencode.json` merges, JSONC parsing, the hook shim and the managed
|
|
217
217
|
CLAUDE.md block. It is a separate module because every function in it reconciles
|
|
218
218
|
Geneseed's claim with content Geneseed did not write, and because eleven of its names are
|
|
219
|
-
driven by the *runtime* as well as by an emit (`js/mcp.mjs` uses ten of them
|
|
219
|
+
driven by the *runtime* as well as by an emit (`js/hosts/mcp.mjs` uses ten of them
|
|
220
220
|
for deactivate, remerge, reactivate and uninstall; `exclude` and `doctor` use the rest).
|
|
221
|
-
Its dependency closure points one way only: nothing in it calls into `js/render.mjs` or
|
|
222
|
-
`js/
|
|
221
|
+
Its dependency closure points one way only: nothing in it calls into `js/build/render.mjs` or
|
|
222
|
+
`js/build/bundle.mjs`. Keep it that way — that closure is what makes it a unit.
|
|
223
223
|
- **Every emit runs five stages in one order: `RENDER* → WIRE* → PRUNE → MANIFEST →
|
|
224
|
-
VERIFY`.** RENDER writes files Geneseed owns wholesale; WIRE is `js/settings.mjs`
|
|
224
|
+
VERIFY`.** RENDER writes files Geneseed owns wholesale; WIRE is `js/hosts/settings.mjs`
|
|
225
225
|
reconciling files you co-own; PRUNE removes what the previous manifest owned and this
|
|
226
226
|
emit no longer produces; MANIFEST records both; VERIFY re-reads the merge to confirm it
|
|
227
227
|
stuck. WIRE must precede MANIFEST because wiring is what fills the `managed` record the
|
|
228
228
|
manifest stores, and no RENDER may follow a WIRE because a render writes wholesale a file
|
|
229
229
|
a wire has just reconciled. `tests/unit/emit_phase_order.test.mjs` fails the build if any of
|
|
230
230
|
the nine emits drifts out of that order, or if a new file-mutating routine in
|
|
231
|
-
`js/settings.mjs` is called from an emit without being classified. RENDER and WIRE now run
|
|
231
|
+
`js/hosts/settings.mjs` is called from an emit without being classified. RENDER and WIRE now run
|
|
232
232
|
in the *same process* — the seam that once spawned a second runtime per emit is gone — so
|
|
233
233
|
the render and wire dispatchers stay two separate functions and two separate statements on
|
|
234
234
|
purpose: that shape is the only thing left for the walker to check, and a gate refuses an
|
|
235
235
|
emit whose sequence has lost its WIRE.
|
|
236
|
-
`bin/
|
|
236
|
+
`bin/build-driver.mjs`'s `main()` is classified too, as the one stage no per-emit gate can see: it
|
|
237
237
|
writes the `.geneseed-emit` / `-footprint` / `-theme` markers and the install-registry
|
|
238
238
|
record *after* every emit returns, and nothing that reconciles a file you co-own may run
|
|
239
239
|
there, because there is no manifest to record the claim and no teardown able to undo it.
|
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ Both launchers are thin shims over the Node CLI: they need `node` (22.3+) on `PA
|
|
|
93
93
|
|
|
94
94
|
Node ≥ 22.3 is the entire dependency list. Every subcommand, all four hooks, every web-console endpoint and both generators run from it, and every commit runs the whole unit suite plus `doctor --all` across every theme × host × footprint, on Linux and Windows both. So an install needs no second interpreter — not for the harness, and not for anything it ships. (Until 2026-08-17 the generator's output was also replayed byte for byte against recordings taken from the Python implementation. Those were retired — see [`docs/limits.md`](docs/limits.md) for the measurement that licensed it and the coverage it cost.)
|
|
95
95
|
|
|
96
|
-
- **
|
|
96
|
+
- **One runtime, no second one to install**: every command and all four hook verbs answer from the Node entry points. `catalog`, `mcp` and `memory` are console-facing verbs the CLI table declares alongside them. There is no full-screen browse panel — `geneseed tui` and `geneseed menu` are verbs that refuse it by name and print the command list instead, off a terminal as well as on one.
|
|
97
97
|
- **Nothing you install needs an interpreter.** The `token-report` skill is a script rather than prose, and it ships as `scripts/token_report.mjs`, run with `node`; `daydream` and `herdr` hand their inline code to `node -e`. So **every bundle carries** nothing that needs one: no Python file, and no skill that shells out to an interpreter, on any host, theme or footprint. Three tests freeze that — one scans every tracked file for inline code handed to an interpreter, one globs the bundle source, and one replays the report script over seeded transcripts for all four hosts and compares the bytes.
|
|
98
98
|
- **`upgrade` / `update` / `sync-self` / `bootstrap` are for a git checkout.** They `git pull` the install's own origin. From an npm install they stop before touching anything and name `npm install -g geneseed@latest` as the update instead.
|
|
99
99
|
|
package/SETUP.md
CHANGED
|
@@ -100,7 +100,7 @@ hand? Pick a path below.
|
|
|
100
100
|
> **Every `geneseed-build …` below is the generator.** `npm install -g geneseed` puts it
|
|
101
101
|
> on your `PATH`; without installing anything, `npx -p geneseed geneseed-build --emit
|
|
102
102
|
> opencode-global` is the same command. From a checkout it is
|
|
103
|
-
> `node bin/
|
|
103
|
+
> `node bin/build-driver.mjs` with the same flags. Every commit re-emits with it across every
|
|
104
104
|
> theme × host × footprint combination and checks the result with `doctor --all`, on Linux
|
|
105
105
|
> and Windows both. Until 2026-08-17 that output was also compared byte for byte against
|
|
106
106
|
> recordings taken from the Python implementation; those were retired, and `docs/limits.md`
|
package/SHIPPED.md
CHANGED
|
@@ -20,7 +20,7 @@ historical pointers, not files distributed with the repo.
|
|
|
20
20
|
> **On version numbers.** [`harness.config.json`](harness.config.json)'s `version` is a
|
|
21
21
|
> human-readable label only. The canonical identity of an installed harness is the
|
|
22
22
|
> **source fingerprint** stamped in `.geneseed-version` (see `geneseed version`), not the
|
|
23
|
-
> string. Current label: **3.1.
|
|
23
|
+
> string. Current label: **3.1.3**.
|
|
24
24
|
|
|
25
25
|
> **Spec status audit (2026-06-15).** Every dated spec authored before today was
|
|
26
26
|
> verified against the source tree and confirmed implemented (code + tests + docs);
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* `js_cfg()` (_build_core.py:199) always sends `structure` and `capabilityLinkRe`, because
|
|
21
21
|
* the Python originals are module-level names that TESTS MUTATE — `_OWNED` membership
|
|
22
22
|
* asked one level out. This driver has no Python module to mutate, so it sends neither and
|
|
23
|
-
* `js/render.mjs:215`'s `cfg.structure ?? STRUCTURE` and `js/
|
|
23
|
+
* `js/build/render.mjs:215`'s `cfg.structure ?? STRUCTURE` and `js/build/bundle.mjs`'s
|
|
24
24
|
* `cfg.capabilityLinkRe ? ... : <default>` take their right-hand branches for the first
|
|
25
25
|
* time. Those two fallbacks have been dead code since they were written — always
|
|
26
26
|
* overridden by the driver that always supplies them. P4 is what makes them live.
|
|
@@ -36,35 +36,36 @@ import {
|
|
|
36
36
|
} from 'node:fs';
|
|
37
37
|
import path from 'node:path';
|
|
38
38
|
import { fileURLToPath } from 'node:url';
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
} from '../js/emit.mjs';
|
|
42
|
-
import { settingsIntegrityCheck } from '../js/settings.mjs';
|
|
43
|
-
import { writeText,
|
|
39
|
+
import { build, phaseLog } from '../js/build/bundle.mjs';
|
|
40
|
+
import { emitClaudeRender } from '../js/build/emit-claude.mjs';
|
|
41
|
+
import { emitOpencodeRender, emitOpencodeGlobalRender } from '../js/build/emit-opencode.mjs';
|
|
42
|
+
import { settingsIntegrityCheck } from '../js/hosts/settings.mjs';
|
|
43
|
+
import { writeText, withPlatformNewlines } from '../js/lib/fs.mjs';
|
|
44
|
+
import { parseJson, jsonDumpsIndent } from '../js/lib/json.mjs';
|
|
44
45
|
// P5c moved these out of this file: `bin/geneseed-cli.mjs` needs the same four resolvers to
|
|
45
46
|
// find a global install, and a resolver that decides WHERE a driver writes is the last thing
|
|
46
47
|
// that should exist twice. golden.py's 259 cells are what made the move safe to attempt.
|
|
47
48
|
import {
|
|
48
|
-
GLOBAL_MANIFEST,
|
|
49
|
+
GLOBAL_MANIFEST, resolvePath, opencodeConfigDir, claudeConfigDir, bobConfigDir,
|
|
49
50
|
copilotConfigDir,
|
|
50
|
-
} from '../js/hosts.mjs';
|
|
51
|
+
} from '../js/hosts/hosts.mjs';
|
|
51
52
|
|
|
52
53
|
// P5d moved these out of this file for the reason P5c moved the host resolvers: `harness
|
|
53
54
|
// status` renders to count, so `bin/geneseed-cli.mjs` needs the same checkout paths and the
|
|
54
55
|
// same cfg. golden.py's 259 cells all build one, which is what made the move safe.
|
|
55
56
|
import {
|
|
56
|
-
ROOT, CONFIG,
|
|
57
|
-
} from '../js/
|
|
57
|
+
ROOT, CONFIG, discoverNames, makeCfg, PACK_ORDER, knownRuleIds,
|
|
58
|
+
} from '../js/build/source.mjs';
|
|
58
59
|
|
|
59
60
|
// P5f moved these, for the same arithmetic a third time: `harness rebuild-all` reads the
|
|
60
61
|
// registry to find every install it must re-emit, and a CLI verb may not reach into a driver
|
|
61
|
-
// for a reader. See js/registry.mjs.
|
|
62
|
-
import { registryRecord, registryRoots } from '../js/registry.mjs';
|
|
62
|
+
// for a reader. See js/inspect/registry.mjs.
|
|
63
|
+
import { registryRecord, registryRoots } from '../js/inspect/registry.mjs';
|
|
63
64
|
|
|
64
65
|
// P2. `--sync-themes` crossed into its own module rather than into this file: it is 90 lines
|
|
65
66
|
// of textual surgery over committed files with a corpus of its own, and it is the half of the
|
|
66
67
|
// maintainer pair that needs nothing this driver is banned from having.
|
|
67
|
-
import { syncThemes } from '../js/themes.mjs';
|
|
68
|
+
import { syncThemes } from '../js/build/themes.mjs';
|
|
68
69
|
|
|
69
70
|
/** The nine `--emit` choices, in build.py:337-338's order. */
|
|
70
71
|
const EMITS = ['files', 'opencode', 'opencode-global', 'claude', 'claude-global',
|
|
@@ -95,24 +96,24 @@ const NATIVE_CATALOG = { opencode: true, claude: true, bob: false, copilot: fals
|
|
|
95
96
|
/** `_build_render.resolve_out` — absolute, or relative to the CURRENT WORKING DIRECTORY
|
|
96
97
|
* (not to ROOT), so the harness renders straight into any repository.
|
|
97
98
|
*
|
|
98
|
-
* EXPORTED IN P5i because `js/setup.mjs` became its second caller: `_setup_summary_lines`
|
|
99
|
+
* EXPORTED IN P5i because `js/maintain/setup.mjs` became its second caller: `_setup_summary_lines`
|
|
99
100
|
* names the AGENT.md a bundle emit just wrote, and it names it by the same resolution the
|
|
100
101
|
* emit used. Copying the one line would be a second owner of the rule that a bare `Harness`
|
|
101
102
|
* follows the invocation and not the checkout — which is what
|
|
102
103
|
* `build/the-bundle-follows-cwd-not-the-checkout` exists to gate.
|
|
103
104
|
*
|
|
104
|
-
* `
|
|
105
|
+
* `resolvePath` AND NOT `path.resolve`, and the difference is the whole reason `resolvePath`
|
|
105
106
|
* exists: the reference ends in `.resolve()`, which canonicalises — 8.3 short names expanded
|
|
106
107
|
* on Windows, symlinks followed on POSIX, the filesystem's own casing — where `path.resolve`
|
|
107
|
-
* only normalises `.`/`..`. Nine `
|
|
108
|
+
* only normalises `.`/`..`. Nine `resolvePath` call sites had the rule and this one did not.
|
|
108
109
|
* It is invisible on a machine whose paths are already canonical, which is every machine
|
|
109
110
|
* this ran on until GitHub's Windows runner handed it a `C:\\Users\\RUNNER~1\\...` and the
|
|
110
111
|
* two CLIs printed the same directory under two different names — a 70-byte difference in a
|
|
111
112
|
* line compared BYTE for byte by `test_the_two_entry_points_agree_on_stdout_BYTES`. The
|
|
112
|
-
* argument to `
|
|
113
|
+
* argument to `resolvePath` is made absolute first so its `expanduser` cannot fire: the
|
|
113
114
|
* reference does not expand `~` here, and a bare `~` is a legal directory name. */
|
|
114
115
|
export function resolveOut(raw) {
|
|
115
|
-
return
|
|
116
|
+
return resolvePath(path.resolve(process.cwd(), raw));
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
/**
|
|
@@ -218,7 +219,7 @@ function choicesFor() {
|
|
|
218
219
|
* carrier, and a marker whose contents depend on argument order is one that compares unequal
|
|
219
220
|
* to itself. Validation is against DISCOVERY, not `PACK_ORDER`, so the refusal names the
|
|
220
221
|
* packs the checkout actually has; a discovered pack missing from `PACK_ORDER` is a
|
|
221
|
-
* different fault and `js/render.mjs` refuses the whole build for it.
|
|
222
|
+
* different fault and `js/build/render.mjs` refuses the whole build for it.
|
|
222
223
|
*/
|
|
223
224
|
/**
|
|
224
225
|
* `--exclude-rules "process 7,craft 3"` (or `process.7`) -> the cfg's exclusion array.
|
|
@@ -357,11 +358,11 @@ function parseArgs(argv, defaults) {
|
|
|
357
358
|
* hand-rolled copy would have its own `--target` alias, its own `choices` lists and its own
|
|
358
359
|
* `-h`, and `test_the_help_text_names_every_flag_the_reference_takes` gates only this one.
|
|
359
360
|
*
|
|
360
|
-
* `
|
|
361
|
+
* `withPlatformNewlines` because the refusal and `-h` paths WRITE: called from `bin/build-driver.mjs`
|
|
361
362
|
* they sit inside `main`'s funnel, and called from the CLI binary they would not.
|
|
362
363
|
*/
|
|
363
364
|
export function parseDriverArgs(argv) {
|
|
364
|
-
return
|
|
365
|
+
return withPlatformNewlines(() => parseArgs(argv, configDefaults()));
|
|
365
366
|
}
|
|
366
367
|
|
|
367
368
|
function choice(flag, value, allowed) {
|
|
@@ -441,7 +442,7 @@ function die(code, msg) {
|
|
|
441
442
|
* re-emit, which is P10's migration arriving five phases early.
|
|
442
443
|
*
|
|
443
444
|
* EXPORTED SINCE P6i, because the emitter is no longer its only caller.
|
|
444
|
-
* `js/uninstall.mjs`'s `remergeClaudeHooks` re-merges the canonical hooks when a disabled
|
|
445
|
+
* `js/maintain/uninstall.mjs`'s `remergeClaudeHooks` re-merges the canonical hooks when a disabled
|
|
445
446
|
* Claude install is turned back on, and `mergeClaudeSettings` refuses to guess the pair —
|
|
446
447
|
* correctly. There is exactly one right answer on this side and it is this function, so the
|
|
447
448
|
* second caller imports it rather than restating two lines that must never drift: a
|
|
@@ -464,14 +465,14 @@ export function hookRunnerEntry() {
|
|
|
464
465
|
* through `_build_core._claude_config_dir`, an `_OWNED` name precisely because the suite
|
|
465
466
|
* redirects it at a sandbox, and a redirect that stops at a subprocess half-works in
|
|
466
467
|
* silence. The child must therefore never derive it — but this file is the PARENT, so
|
|
467
|
-
* deriving it is exactly this file's job, and `
|
|
468
|
+
* deriving it is exactly this file's job, and `the host emit` receives the answer.
|
|
468
469
|
*
|
|
469
470
|
* POSIX spelling, per the original: `claudeMdExcludes` entries are glob patterns, where a
|
|
470
471
|
* backslash is an escape, so the Windows-native form risks never matching.
|
|
471
472
|
*/
|
|
472
473
|
function preambleExclude(claudeMd) {
|
|
473
474
|
if (path.basename(claudeMd) !== 'CLAUDE.md') return null;
|
|
474
|
-
return
|
|
475
|
+
return resolvePath(path.join(claudeConfigDir(), 'CLAUDE.md')).split(path.sep).join('/');
|
|
475
476
|
}
|
|
476
477
|
|
|
477
478
|
/**
|
|
@@ -566,10 +567,6 @@ function pruneOwned(oc, oldOwned, owned) {
|
|
|
566
567
|
}
|
|
567
568
|
}
|
|
568
569
|
|
|
569
|
-
function isDir(p) {
|
|
570
|
-
try { return statSync(p).isDirectory(); } catch { return false; }
|
|
571
|
-
}
|
|
572
|
-
|
|
573
570
|
/**
|
|
574
571
|
* `_build_global._project_survivors` — registered per-repo PROJECT installs of `emitName`.
|
|
575
572
|
*
|
|
@@ -736,7 +733,7 @@ function emitOpencodeGlobal(cfg, args, out) {
|
|
|
736
733
|
* `_build_global._emit_claude_core` — the shared engine behind six emits.
|
|
737
734
|
*
|
|
738
735
|
* Only the two Copilot ones run through it so far, and that is a deliberate split rather
|
|
739
|
-
* than an arbitrary stopping point: `js/emit.mjs
|
|
736
|
+
* than an arbitrary stopping point: `js/build/emit-claude.mjs's wire` gates the entire settings/hook path
|
|
740
737
|
* behind `if (!isCopilot)`, so a Copilot emit never reaches `hookPrefix` and therefore never
|
|
741
738
|
* needs `hookOpts` — the pair of machine-absolute values (`sys.executable` and the harness
|
|
742
739
|
* entry point) that a Node driver has no way to produce. Copilot crossing first isolates
|
|
@@ -1007,7 +1004,7 @@ export function emitProjectInto(host, { theme, out, root, footprint = 'full', do
|
|
|
1007
1004
|
// same reason: a drift reader that renders `expected` at the DEFAULT pack set reports the
|
|
1008
1005
|
// whole doctrines section as edited on every narrowed install. `null` means "no opinion" and
|
|
1009
1006
|
// `makeCfg` renders all four, which is what every validation caller wants.
|
|
1010
|
-
return
|
|
1007
|
+
return withPlatformNewlines(() => emit(makeCfg(doctrines ? { doctrines } : {}),
|
|
1011
1008
|
{ theme, footprint, root }, out));
|
|
1012
1009
|
}
|
|
1013
1010
|
|
|
@@ -1020,7 +1017,7 @@ export function emitProjectInto(host, { theme, out, root, footprint = 'full', do
|
|
|
1020
1017
|
* exactly as `run` inherits it below.
|
|
1021
1018
|
*/
|
|
1022
1019
|
export function buildInto({ theme, out, footprint = 'lean' }) {
|
|
1023
|
-
return
|
|
1020
|
+
return withPlatformNewlines(
|
|
1024
1021
|
() => build(makeCfg(), theme, out, { footprint, nativeCatalog: false }),
|
|
1025
1022
|
);
|
|
1026
1023
|
}
|
|
@@ -1042,7 +1039,7 @@ export function emitGlobalInto(host, {
|
|
|
1042
1039
|
// rendering at `peer`/`direct`; the drift readers pass what the deployment actually says,
|
|
1043
1040
|
// because rendering `expected` at the defaults reports AGENT.md as edited on every install
|
|
1044
1041
|
// that chose a register — the same scar the footprint left in `diffCollect`, one axis over.
|
|
1045
|
-
return
|
|
1042
|
+
return withPlatformNewlines(
|
|
1046
1043
|
() => emit(
|
|
1047
1044
|
// THE PACK SELECTION RIDES THE SAME RAIL, with one difference: `doctrinesOfDir` answers
|
|
1048
1045
|
// `null` for "no marker / undetectable", and that must render at ALL packs rather than
|
|
@@ -1062,16 +1059,16 @@ export function emitGlobalInto(host, {
|
|
|
1062
1059
|
* `_harness_build.cmd_build` is `run([sys.executable, BUILD, *extra]).returncode` — Python
|
|
1063
1060
|
* needs a second process because `build.py` is a different PROGRAM. Here it is a module in
|
|
1064
1061
|
* the same one, and `bin/geneseed-cli.mjs` is under a transitive `child_process` ban, so
|
|
1065
|
-
* `js/generate.mjs` calls this directly. The export is what makes the auto-run below need a
|
|
1062
|
+
* `js/build/generate.mjs` calls this directly. The export is what makes the auto-run below need a
|
|
1066
1063
|
* guard: without one, importing this file to reach `main` would RUN the generator with the
|
|
1067
1064
|
* CLI's argv.
|
|
1068
1065
|
*/
|
|
1069
1066
|
export function main(argv) {
|
|
1070
|
-
// `
|
|
1067
|
+
// `withPlatformNewlines` is what makes this driver's bytes Python's bytes on Windows, for every
|
|
1071
1068
|
// print site in the whole render tree at once — see its docblock for why a funnel and not
|
|
1072
|
-
// 25 calls to `
|
|
1069
|
+
// 25 calls to `printOut`. It wraps the `catch` as well as `run`, because `die` writes its
|
|
1073
1070
|
// line on the way out.
|
|
1074
|
-
return
|
|
1071
|
+
return withPlatformNewlines(() => {
|
|
1075
1072
|
try {
|
|
1076
1073
|
return run(argv);
|
|
1077
1074
|
} catch (e) {
|
|
@@ -1092,12 +1089,12 @@ function run(argv) {
|
|
|
1092
1089
|
// different places because the transitive `child_process` ban splits them.
|
|
1093
1090
|
//
|
|
1094
1091
|
// `--sync-themes` is HERE, because it needs nothing this driver may not have: it reads
|
|
1095
|
-
// `themes/`, rewrites the files textually, and prints. `js/themes.mjs` carries it.
|
|
1092
|
+
// `themes/`, rewrites the files textually, and prints. `js/build/themes.mjs` carries it.
|
|
1096
1093
|
// Non-zero when files were CHANGED (0 == already in sync), so CI can run it as a drift
|
|
1097
1094
|
// check — build.py:393-397's mapping, and a refusal (exit 2) if the template is unreadable.
|
|
1098
1095
|
if (args.syncThemes) return syncThemes() ? 1 : 0;
|
|
1099
1096
|
// `--validate-only` is NOT, and cannot be: its source-tree half is the doctor, and
|
|
1100
|
-
// `js/
|
|
1097
|
+
// `js/inspect/checks-authoring.mjs` starts a process (`node --check` over the OpenCode plugins). This driver
|
|
1101
1098
|
// is under a transitive ban on reaching any such module, gated twice — by a source grep in
|
|
1102
1099
|
// `tests/test_node_cli_parity.py` and by an import walk in `tests/test_hook_cli_parity.py`.
|
|
1103
1100
|
// So the tool crossed onto the CLI binary, which already carries the doctor, and this flag
|
|
@@ -1172,10 +1169,10 @@ function run(argv) {
|
|
|
1172
1169
|
}
|
|
1173
1170
|
|
|
1174
1171
|
/**
|
|
1175
|
-
* Run only when this file IS the entry point, not when `js/generate.mjs` imports it.
|
|
1172
|
+
* Run only when this file IS the entry point, not when `js/build/generate.mjs` imports it.
|
|
1176
1173
|
*
|
|
1177
1174
|
* `import.meta.main` would say this in one word and is Node 24; the spec's `engines` floor
|
|
1178
|
-
* is still open and this machine runs v22, the
|
|
1175
|
+
* is still open and this machine runs v22, which is why the comparison is spelled out rather than using it. So the
|
|
1179
1176
|
* check is the manual one, through `realpathSync` because `process.argv[1]` is whatever the
|
|
1180
1177
|
* caller typed and `import.meta.url` is always the resolved file — an `npx`-generated shim
|
|
1181
1178
|
* or a `geneseed link` symlink makes those differ as strings while naming the same file.
|
|
@@ -1183,9 +1180,9 @@ function run(argv) {
|
|
|
1183
1180
|
* BOTH ways of getting this wrong are gated, which is why it is a guard and not a comment,
|
|
1184
1181
|
* and both were measured rather than argued:
|
|
1185
1182
|
*
|
|
1186
|
-
* * stuck FALSE — `node bin/
|
|
1183
|
+
* * stuck FALSE — `node bin/build-driver.mjs` becomes a silent no-op: 259 golden cells.
|
|
1187
1184
|
* * stuck TRUE — importing this file runs the generator on the IMPORTER's argv, so it is
|
|
1188
|
-
* not only `build` that breaks. `bin/geneseed-cli.mjs` reaches `js/generate.mjs` at
|
|
1185
|
+
* not only `build` that breaks. `bin/geneseed-cli.mjs` reaches `js/build/generate.mjs` at
|
|
1189
1186
|
* module load, so every CLI verb dies in the driver's flag parser: **62 of the 166
|
|
1190
1187
|
* acceptance cells**, `exclude` and `status` and `version` among them.
|
|
1191
1188
|
*/
|