geneseed 3.16.0 → 3.18.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 +45 -0
- package/README.md +17 -3
- package/SHIPPED.md +3 -3
- package/adapters/claude-code/skills/geneseed/SKILL.md +1 -1
- package/adapters/opencode/README.md +2 -2
- package/adapters/opencode/plugins/geneseed-context.js +31 -21
- package/adapters/opencode/plugins/geneseed-guard.js +35 -7
- package/bin/geneseed-cli.mjs +5 -0
- package/bin/geneseed.mjs +17 -0
- package/docs/concepts/exclusions.md +1 -1
- package/docs/concepts/hooks.md +1 -0
- package/docs/concepts/loop-notes.md +3 -0
- package/docs/concepts/loops.md +11 -0
- package/docs/concepts/skills.md +4 -0
- package/docs/extending.md +33 -0
- package/docs/guides/headless.md +1 -1
- package/docs/guides/project-context.md +2 -2
- package/docs/guides/read-non-markdown.md +2 -2
- package/docs/guides/uninstall.md +1 -1
- package/docs/guides/upgrade.md +1 -1
- package/docs/guides/wiki.md +4 -4
- package/docs/opencode-plugin-setup.md +1 -1
- package/docs/reference/env-context.md +2 -2
- package/docs/reference/plugin-context.md +4 -4
- package/docs/reference/plugin-guard.md +1 -1
- package/docs/token-footprint.md +18 -15
- package/docs/understand/take-it-out.md +1 -1
- package/harness.config.json +1 -1
- package/js/README.md +4 -1
- package/js/build/args.mjs +22 -20
- package/js/build/bundle.mjs +1 -3
- package/js/build/emit-claude.mjs +5 -4
- package/js/build/emit-opencode.mjs +9 -2
- package/js/build/generate.mjs +6 -6
- package/js/build/render.mjs +19 -12
- package/js/build/source.mjs +82 -7
- package/js/build/stubs.mjs +35 -6
- package/js/build/themes.mjs +1 -3
- package/js/cli-table.json +53 -0
- package/js/hosts/hooks-context.mjs +40 -27
- package/js/hosts/hooks.mjs +34 -1
- package/js/hosts/hosts.mjs +1 -3
- package/js/hosts/installs.mjs +13 -7
- package/js/hosts/mcp.mjs +1 -1
- package/js/hosts/native.mjs +60 -5
- package/js/hosts/opencode.mjs +37 -6
- package/js/hosts/settings.mjs +3 -1
- package/js/hosts/shim.mjs +1 -0
- package/js/inspect/checks-authoring.mjs +4 -1
- package/js/inspect/checks-constitution.mjs +2 -2
- package/js/inspect/checks-repo.mjs +164 -2
- package/js/inspect/diff.mjs +5 -1
- package/js/inspect/doctor.mjs +2 -1
- package/js/inspect/inventory.mjs +7 -9
- package/js/inspect/scorecard-checks.mjs +571 -0
- package/js/inspect/scorecard-svg.mjs +85 -0
- package/js/inspect/scorecard.mjs +839 -0
- package/js/lib/json.mjs +1 -1
- package/js/loop/graph.mjs +2 -1
- package/js/loop/state.mjs +30 -1
- package/js/maintain/migrate.mjs +2 -1
- package/js/maintain/uninstall.mjs +2 -3
- package/js/maintain/update.mjs +10 -3
- package/js/ui/menu.mjs +2 -2
- package/js/ui/tui.mjs +1 -0
- package/js/web/api.mjs +2 -0
- package/js/web/catalog.mjs +24 -7
- package/js/web/daemon.mjs +2 -1
- package/js/web/routes.mjs +2 -0
- package/js/web/server.mjs +1 -1
- package/js/web/user-files.mjs +1 -0
- package/package.json +11 -3
- package/registry.json +12 -75
- package/src/AGENT.md.tmpl +13 -22
- package/src/agents/docs.md +5 -0
- package/src/agents/researcher.md +1 -1
- package/src/bricks/apply.md +3 -0
- package/src/bricks/bruno-test.md +2 -2
- package/src/bricks/ci-fix.md +3 -3
- package/src/bricks/deps-audit.md +3 -3
- package/src/bricks/docs-update.md +2 -0
- package/src/bricks/migration-plan.md +2 -2
- package/src/bricks/test.md +17 -7
- package/src/doctrines/process.md +11 -7
- package/src/laws/universal.md +2 -2
- package/src/loops/api-endpoint.json +1 -1
- package/src/loops/bugfix.json +1 -1
- package/src/loops/ci-repair.json +1 -1
- package/src/loops/deps-upgrade.json +1 -1
- package/src/loops/enforce-architecture-rule.json +1 -1
- package/src/loops/feature.json +1 -1
- package/src/loops/fix-flaky-tests.json +1 -1
- package/src/loops/legacy-refactor.json +1 -1
- package/src/loops/legacy-tests.json +1 -1
- package/src/loops/refactor.json +1 -1
- package/src/loops/remove-dead-code.json +1 -1
- package/src/loops/spec-first-feature.json +1 -1
- package/src/loops/tdd.json +1 -1
- package/src/loops/update-contract.json +1 -1
- package/src/skills/brainstorm.md +14 -10
- package/src/skills/bruno.md +84 -0
- package/src/skills/codebase-design.md +41 -23
- package/src/skills/daydream/SKILL.md +3 -3
- package/src/skills/daydream/VENDOR.md +2 -2
- package/src/skills/daydream/instructions.md +1 -1
- package/src/skills/debug/ci.md +11 -0
- package/src/skills/debug.md +11 -8
- package/src/skills/dependencies.md +65 -0
- package/src/skills/develop.md +9 -4
- package/src/skills/document-project.md +106 -32
- package/src/skills/geneseed-code-review.md +13 -8
- package/src/skills/geneseed.md +4 -1
- package/src/skills/git-history.md +78 -0
- package/src/skills/handoff.md +4 -3
- package/src/skills/parallel-agents.md +1 -0
- package/src/skills/plan.md +5 -1
- package/src/skills/prototype.md +10 -7
- package/src/skills/research.md +6 -1
- package/src/skills/rule/consolidate.md +32 -0
- package/src/skills/rule.md +14 -4
- package/src/skills/security-audit.md +1 -1
- package/src/skills/ship.md +8 -5
- package/src/skills/teach.md +5 -3
- package/src/skills/wayfinder.md +19 -16
- package/src/skills/wiki.md +2 -2
- package/themes/_TEMPLATE.json +7 -16
- package/themes/biker.json +7 -16
- package/themes/commentator.json +7 -16
- package/themes/cyberpunk.json +7 -16
- package/themes/gamer.json +7 -16
- package/themes/imperial.json +7 -16
- package/themes/joker.json +7 -16
- package/themes/marvin.json +7 -16
- package/themes/mean.json +7 -16
- package/themes/military.json +7 -16
- package/themes/neutral.json +7 -16
- package/themes/pirate.json +7 -16
- package/themes/sports.json +7 -16
- package/themes/verstappen.json +7 -16
- package/themes/wizard.json +7 -16
- package/web/dist/assets/{Activity-kpWxd70F.js → Activity-4M4VQJOh.js} +1 -1
- package/web/dist/assets/{ActivityDetail-Ccr942VL.js → ActivityDetail-CnmDs43F.js} +1 -1
- package/web/dist/assets/{Dashboard-D1lE7jcg.js → Dashboard-lTaH58R9.js} +2 -2
- package/web/dist/assets/{Docs-BHyaggFy.js → Docs-DU5znu5f.js} +1 -1
- package/web/dist/assets/FilterInput-BSfpStOZ.js +1 -0
- package/web/dist/assets/{GreenhouseView-Sc_UDMOE.js → GreenhouseView-BxO84bKX.js} +1 -1
- package/web/dist/assets/{Installs-Gw-pJ3Zi.js → Installs-B1WcObxK.js} +1 -1
- package/web/dist/assets/{JournalView-_gxpsxXi.js → JournalView-0YNKcaaD.js} +1 -1
- package/web/dist/assets/{Laws-S9fq1r9T.js → Laws-DnDpRwpE.js} +1 -1
- package/web/dist/assets/Library-DBkKsBMe.js +1 -0
- package/web/dist/assets/{Loops-Da_JhlzM.js → Loops-P-6RCdzS.js} +1 -1
- package/web/dist/assets/Markdown-C6fRd4A4.js +67 -0
- package/web/dist/assets/{OperatorHudView--TkvDybH.js → OperatorHudView-BZZ3uJPH.js} +1 -1
- package/web/dist/assets/{Personal-CTXKHVbV.js → Personal-BoVw6OCW.js} +1 -1
- package/web/dist/assets/{Tabs-B0cdf8k6.js → Tabs-VYLhUTiX.js} +1 -1
- package/web/dist/assets/{activity-Dr7ZMBI3.js → activity-dcg5vF9j.js} +1 -1
- package/web/dist/assets/{drawable-BbWQxe82.js → drawable-CdNARIKC.js} +1 -1
- package/web/dist/assets/{index-ujKG1ZNf.js → index-hsabpYzv.js} +2 -2
- package/web/dist/assets/react-DRhoX0kS.js +9 -0
- package/web/dist/assets/{usePoll-DD8VjtSg.js → usePoll-BmR631BB.js} +1 -1
- package/web/dist/index.html +2 -2
- package/src/skills/bruno-collection-generator.md +0 -52
- package/src/skills/bruno-test-writer.md +0 -51
- package/src/skills/ci-fix.md +0 -49
- package/src/skills/consolidate-memory.md +0 -50
- package/src/skills/deps-audit.md +0 -46
- package/src/skills/domain-modeling.md +0 -27
- package/src/skills/git-archaeology.md +0 -36
- package/src/skills/git-rescue.md +0 -58
- package/src/skills/migrate.md +0 -34
- package/src/skills/opencode-theme.md +0 -42
- package/src/skills/repo-map.md +0 -25
- package/src/skills/tickets.md +0 -33
- package/web/dist/assets/FilterInput-CVyqmYWM.js +0 -1
- package/web/dist/assets/Library-MBxadRzn.js +0 -1
- package/web/dist/assets/Markdown-X2a3qvOh.js +0 -38
- package/web/dist/assets/react-B9Z_tXRV.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,51 @@ label. For the capability ↔ spec map, see [SHIPPED.md](SHIPPED.md).
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [3.18.0] — 2026-10-09
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- `geneseed scorecard [path]` scores the repo a harness is installed in — its CI, tests and docs, not the harness files — with the AI Harness Scorecard's 31 checks and five weighted pillars, ported to JavaScript (no Python). It prints the grade, each pillar and every failing check with its fix, and always exits 0. `--json` gives the raw result; `--svg PATH` writes a grade-and-pillars card (light and dark) and prints the matching badge URL.
|
|
15
|
+
- Skills can carry side files: `src/skills/<name>/<file>.md` is emitted beside the skill's `SKILL.md` on every host and read only when the skill points at it. `debug` moves its CI section and `rule` its memory consolidation into side files, so each costs about 0.5k tokens less per use.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Fewer, sharper skills: 56 become 47. `dependencies` replaces `deps-audit` and `migrate`, `bruno` replaces `bruno-collection-generator` and `bruno-test-writer`, and `git-history` replaces `git-archaeology` and `git-rescue`. `document-project` absorbs `repo-map`, `codebase-design` absorbs `domain-modeling` (the glossary is now `GLOSSARY.md`, `CONTEXT.md` still read), `debug` absorbs `ci-fix`, `rule` absorbs `consolidate-memory`, and `geneseed` absorbs `opencode-theme`. Every old name still works as a user-only `/name` alias of its new home, and so does `/grill-me` for `brainstorm`; an old name in `--exclude-skills` excludes the skill that absorbed it. Excluding one half of a merged pair (say `deps-audit`) now excludes the whole merged skill (`dependencies`, which also covers `migrate`). Parts of brainstorm, codebase-design, debug, develop, prototype, ship, wayfinder, parallel-agents, rule and geneseed-code-review adapt ideas from Matt Pocock's skills (github.com/mattpocock/skills, MIT).
|
|
19
|
+
- The wiki manifest is now `geneseed-wiki.jsonc`, so a shared `.claude/` or `.opencode/` folder shows who owns it. The next build or upgrade renames an existing `wiki.jsonc` (or older `wiki.json`) to the new name with its contents untouched, and adds the new name to a `.gitignore` that listed the old one. Until then the hook, the OpenCode plugins and the web console still read `wiki.jsonc`; that fallback goes in the release after 3.18.0. `GENESEED_WIKI` is unchanged.
|
|
20
|
+
- `document-project` and the docs agent default to Markdown. AsciiDoc is used only when you ask for it or the project's docs are already `.adoc`; the skill no longer proposes a switch on its own.
|
|
21
|
+
- `upgrade` and `migrate` print the alias and retired-name notices that `rebuild-all` already printed. `codebase-design` now triggers on "where should the seam go".
|
|
22
|
+
|
|
23
|
+
### Removed
|
|
24
|
+
- The `tickets` skill, with no alias: `wayfinder` charts the decisions and hands execution to `plan`, sliced into thin end-to-end pieces. A `tickets` entry in `--exclude-skills` is dropped with a notice.
|
|
25
|
+
|
|
26
|
+
### Repository
|
|
27
|
+
Not in the package; how Geneseed itself is built and checked.
|
|
28
|
+
- `SECURITY.md`, `CODEOWNERS`, a PR template and a weekly advisory scorecard run; `ARCHITECTURE.md` and decision records under `docs/adr/`.
|
|
29
|
+
- CI: a blocking `npm audit --audit-level=high` job, Dependabot, conventional PR titles (commitlint), `tsc --noEmit` with `checkJs` over `js/` and `bin/` (`npm run typecheck`), coverage with `c8`, a `lychee` link check, and the doctor scorecard ratchet with the README card and badge.
|
|
30
|
+
- Tests: fast-check property tests on the settings parser, a `fuzz/` target that drives the hook gates through the real entry point, and a weekly Stryker run. The mutation matrix runs on Windows, where two of its rows are real defects, and installs its devDependencies first.
|
|
31
|
+
- ESLint bans `eval`, implied eval, `new Function` and thrown literals in product code. Toolchain and web dependencies bumped by Dependabot (`web/dist` rebuilt).
|
|
32
|
+
|
|
33
|
+
## [3.17.0] — 2026-10-08
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Docs in AsciiDoc. `document-project`, the docs agent and the `docs-update` brick detect a project's docs format: a `.md`-only tree stays Markdown, an `.adoc`-only tree stays AsciiDoc, a mix asks you, and a project with no docs defaults to AsciiDoc. The choice is recorded as `Docs format:` in `AGENTS.md`. AsciiDoc mode uses header attributes, `.adoc` names and `[source,mermaid]` diagrams that GitHub renders, and it finishes with an `asciidoctor --failure-level=WARN` lint (or an explicit SKIPPED). With no docs yet, `document-project` interviews you first. Agent-runtime files stay Markdown.
|
|
38
|
+
- C4 architecture diagrams in `document-project`. The audience you name sets the levels: L1 and L2 always, L3 only for containers you call complex, never L4. Diagrams are Mermaid `flowchart`s styled as C4 (typed nodes, `intent · protocol` edges, a title and a legend), redrawn only inside their own section. The `AGENTS.md` it writes sends a cross-container change to the L2 diagram first.
|
|
39
|
+
- The context hook and the OpenCode plugin pick up `.adoc` files wherever they pick up `.md`; `README.adoc` and `CONTRIBUTING.adoc` load eagerly.
|
|
40
|
+
- Protected checks. List the checks and tests your agent is judged by in `.geneseed/protected-checks.txt` at the repo root, one path per line, and a write to any of them asks you first on Claude Code, is blocked on Bob and refused on OpenCode, citing *External Gate*. A check the agent can edit is not a check. Nothing changes until a project adds the file. Shell edits are not seen, so keep running the checks in CI.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Loops repair once, then stop for you. The test repair rings of every shipped template (`apply-test`, `fix-test`, `write-run`, `stabilise`) are now `max: 1`: one repair, then the attempt is discarded for one fresh try, then the loop stops. A second revision makes code worse, not better — 0.82 correct after one revision, 0.673 after two ([arXiv 2607.24604](https://arxiv.org/abs/2607.24604)). A ring's `max` now binds its own repair edge even when a wider ring shares its nodes; before, `review-fix` (max 3) quietly let `apply-test` retry four times.
|
|
45
|
+
- The `test` brick runs checks in order — build gate, then every focused check (none hides another), then the full suite only when all passed — and lists each one as `PASS`, `FAIL`, `ERROR` or `SKIPPED (reason)`. Each failure is four lines: purpose, property, a one-line diagnostic, the rerun command.
|
|
46
|
+
- `error` is a reserved loop outcome: a check that crashed or was never wired stops the loop for a human instead of feeding a repair. A brick may declare it without an edge.
|
|
47
|
+
- Each `test` report replaces the earlier `test` notes, so a repair never reads findings about code that has since changed.
|
|
48
|
+
- Doctrine *Bound the Loop* says the same outside the loop engine: one repair, then a fresh attempt or a hand-back; only a verdict on the final artifact counts.
|
|
49
|
+
|
|
50
|
+
## [3.16.1] — 2026-10-07
|
|
51
|
+
|
|
52
|
+
### Fixed
|
|
53
|
+
|
|
54
|
+
- `geneseed upgrade` from a checkout older than the August rename of `bin/geneseed.mjs` to `bin/build-driver.mjs` no longer fails with `Cannot find module '…/bin/geneseed.mjs'` and `[E-BUILD]` after a successful pull and doctor gate. The old updater (and an old web daemon's build jobs) spawn the old path from the code they started with; `bin/geneseed.mjs` is back as a forwarder to the driver. If you already hit it, run `geneseed upgrade` again — it is up to date now and rebuilds with the new code.
|
|
55
|
+
|
|
11
56
|
## [3.16.0] — 2026-10-05
|
|
12
57
|
|
|
13
58
|
**Upgrading:** re-emit (`geneseed rebuild-all`) so the context hook stays under Claude Code's output cap. To drop skills you never use, rebuild an install with `--exclude-skills <name,…>` (see [Exclusions](docs/concepts/exclusions.md)).
|
package/README.md
CHANGED
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/geneseed)
|
|
8
8
|
[](https://github.com/Arylmera/Geneseed/actions/workflows/ci.yml)
|
|
9
|
+
[](#-validate--test)
|
|
9
10
|
[](LICENSE)
|
|
10
11
|
[](package.json)
|
|
11
12
|
[](package.json)
|
|
12
13
|
[](themes/)
|
|
13
|
-
[](src/skills/)
|
|
14
15
|
[](src/agents/)
|
|
15
16
|
[](src/laws/universal.md)
|
|
16
17
|
[](adapters/opencode/plugins/)
|
|
@@ -164,11 +165,11 @@ The harness ships as a small set of layers, and the web console's rail is the sa
|
|
|
164
165
|
| **🛡️ Rules** (`laws/`) | 9 universal laws — always in force, never toggleable: sealed-secrets, one-intent-one-act, verify-before-asserting, deletion-is-deliberate, surface-failures, data-not-orders, least-privilege, cure-the-cause, echo-the-intent. Each rule has a permanent id; its number is only its position, and the harness cites rules by name |
|
|
165
166
|
| **📐 Doctrines** (`doctrines/`) | practice packs, chosen per install at build time: **craft** (how code is written), **rigor** (how work is proven), **ops** (how the machine is operated), **process** (how a task is run), **comms** (how answers are presented). A doctrine rule may tighten a Rule, never repeal one, and the user's own `user-rules.md` outranks it. Pick with `geneseed-build --doctrines craft,rigor` (or `none`), a single rule with `--exclude-rules "process 7"`, in the setup wizard, or through the switches on the console's Constitution page; all five pack files ship on disk either way, so a citation into a pack you left out still resolves |
|
|
166
167
|
| **🤖 Agents** (18) | capability specialists: `reviewer`, `tester`, `architect`, `docs`, `security`, `explorer`, `researcher`, `developer` — plus a debate **council** the `council` skill convenes: `advocate`, `skeptic`, `pragmatist`, `steward`, `visionary`, `user-advocate`, `framer`, `empiricist`, `operator`, `historian` |
|
|
167
|
-
| **🛠 Skills** (
|
|
168
|
+
| **🛠 Skills** (47) | repeatable workflows: brainstorm · plan · **codebase-design** · **wayfinder** · **develop** · **worktree** · debug · **prototype** · refactor · **ponytail** · **forge-mcp** · **bruno** · geneseed-code-review · **fresh-eyes** · **security-audit** · **dependencies** · **review-response** · commit · **ship** · **release** · **git-history** · document-project · **frontend-design** · **prose** · **ingest** · **research** · **teach** · **quiz** · **learn-mode** · handoff · roast-me · **council** · parallel-agents · **workflow** · **wiki** · **geneseed** · **rule** · **profile** · **skill-forge** · **herdr** · **pipeline** · **loop** · **brick-forge** · **daydream** · **react-view-transitions** · **token-report** · **explain-changes** |
|
|
168
169
|
| **🔌 Plugins** (OpenCode) | `geneseed-context` injects project docs *and your machine wiki* every session (and across compaction); `geneseed-learn` distils memory at session end; `geneseed-guard` enforces the safety Laws and protected wiki folders at the tool boundary; `geneseed-workflow` registers the `workflow` tool that runs saved orchestration scripts; `geneseed-notify` sends a native OS notification when a long run finishes; `geneseed-ponytail` holds a minimal-code mode (`/ponytail lite\|full\|ultra\|off`), opt-in, injecting the laziest-that-works ruleset every turn so it doesn't drift; `geneseed-activity` streams what each session is doing to the web console's Activity view |
|
|
169
170
|
| **🧠 Memory** (`memory/`) | one-fact-per-file durable knowledge, indexed by `MEMORY.md` (git-ignored, personal) |
|
|
170
171
|
| **📓 Notebook** (`notebook/`) | the agent's sovereign space — any medium (code, tools, data, notes), self-ruled via a seed-once charter, always git-ignored; only its `.gitignore` is build-asserted |
|
|
171
|
-
| **🌐 Wiki** (`wiki.jsonc`) | your own machine-wide knowledge base — typically an Obsidian vault — declared once per machine: entry notes load eager/lazy, the agent reads and **writes** it under the vault's own conventions, with an inbox fallback and guard-enforced protected folders |
|
|
172
|
+
| **🌐 Wiki** (`geneseed-wiki.jsonc`) | your own machine-wide knowledge base — typically an Obsidian vault — declared once per machine: entry notes load eager/lazy, the agent reads and **writes** it under the vault's own conventions, with an inbox fallback and guard-enforced protected folders |
|
|
172
173
|
| **🧭 Context** | the project's own docs — auto-discovered on OpenCode, or via a `context.json` manifest |
|
|
173
174
|
|
|
174
175
|
### 🤝 What the harness asks of you
|
|
@@ -291,6 +292,19 @@ node --test --test-reporter=tap "tests/**/*.test.mjs" # the suites (node expan
|
|
|
291
292
|
|
|
292
293
|
`doctor` checks each theme for unresolved tokens, dead/non-hermetic links, theme-key parity, author-time gates (every spec has a purpose line, the plugins parse, the learn-prompt literal stays extractable), and that a committed bundle still matches a fresh render of `src/`. CI (`.github/workflows/ci.yml`) runs both on every push and PR, on both Linux and Windows. Publishing is a separate, manually-triggered workflow (`.github/workflows/publish.yml`) — see [Contributing](#-contributing).
|
|
293
294
|
|
|
295
|
+
### AI Harness Scorecard
|
|
296
|
+
|
|
297
|
+
<p align="center"><img src="docs/assets/scorecard.svg" alt="AI Harness Scorecard for this repository: grade and score, with one bar per pillar" width="760"></p>
|
|
298
|
+
|
|
299
|
+
How safe this repository is to change with agents, scored by the [AI Harness Scorecard](https://github.com/markmishaev76/ai-harness-scorecard): 31 deterministic checks, no LLM, in five weighted pillars. Geneseed ships its own JavaScript port, so you can score any repository a harness is installed in — it grades the repository (CI, tests, docs), not the harness files:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
geneseed scorecard # grade, one line per pillar, and what to fix
|
|
303
|
+
geneseed scorecard --json # every check with its evidence
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
`geneseed scorecard --svg docs/assets/scorecard.svg` rewrites the card and prints the badge URL for the top of this page; `doctor` fails when either no longer matches the live score, and the checks this repository passes are a floor it may not drop below. A weekly workflow also runs the upstream Python tool as a cross-check.
|
|
307
|
+
|
|
294
308
|
## 🔄 Keeping it current
|
|
295
309
|
|
|
296
310
|
**Installed from npm** — one command, and it rebuilds every active install for you:
|
package/SHIPPED.md
CHANGED
|
@@ -20,7 +20,7 @@ historical references, 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.
|
|
23
|
+
> string. Current label: **3.18.0**.
|
|
24
24
|
|
|
25
25
|
## Capabilities
|
|
26
26
|
|
|
@@ -29,7 +29,7 @@ Every row below is present in the tree today; "Spec" names the design record.
|
|
|
29
29
|
| Capability | What it is | Spec |
|
|
30
30
|
| --- | --- | --- |
|
|
31
31
|
| **Themed generator** | One neutral `src/` → 14 themed bundles via `geneseed-build` (zero dependencies); structure stays theme-independent | [DESIGN.md](DESIGN.md) |
|
|
32
|
-
| **Laws / Agents / Skills / Memory / Notebook** | 9 laws, 18 agents,
|
|
32
|
+
| **Laws / Agents / Skills / Memory / Notebook** | 9 laws, 18 agents, 47 skills, the memory convention, and the agent's sovereign notebook | `2026-06-11-notebook-sovereign-space` · `2026-06-10-notebook-agent-own-space` |
|
|
33
33
|
| **Three-tier constitution** | The governance surface splits into an always-on **Ontology** (Telos, Evidence, Decisions, Conduct — the Pact absorbed into Telos), the nine always-on **Invariants**, and five toggleable **Doctrine** packs (craft, rigor, ops, process, comms) addressed `<pack> <n>`. Every rule carries a permanent id; its number is its position, computed at render time, and a citation renders as the rule's principle name. Packs are chosen at build time with `--doctrines` or the setup wizard; the whole catalogue ships on disk either way, so a citation into an inactive pack still resolves. Precedence, in order: Ontology + Invariants, then the user's `user-rules.md`, then the active Doctrines, then `PROFILE.md`. `doctor`'s `constitutionProblems` gates rule ids (declared, unique, never a retired one), the per-rule Principle column, every `{{LAW:<id>}}` / `{{DOCTRINE:<id>}}` citation in `src/`, and the rule that an always-on tier may never cite a toggleable one | `2026-08-19-three-tier-constitution-design` |
|
|
34
34
|
| **`doctor`** | One check: unresolved tokens, dead/non-hermetic links, theme-key parity, authoring gates, rendered-bundle drift | `2026-06-07-theme-aware-doctor-diff` |
|
|
35
35
|
| **`diff` + improvements export** | Deployed-vs-source drift report; export an `improvements.md` back-port | `2026-06-12-improvements-export` |
|
|
@@ -40,7 +40,7 @@ Every row below is present in the tree today; "Spec" names the design record.
|
|
|
40
40
|
| **Host hooks, per dialect** | Every host gets the hook surface its runtime offers, spoken in that runtime's own verdict dialect via `--host`: Claude Code asks (`permissionDecision`), OpenClaude asks exactly as Claude does (a Claude Code fork, under `~/.openclaude` / `.openclaude/`), Bob refuses by exit code 2 (five Claude-named events, nested `~/.bob/settings/settings.json`, no subagent event), OpenCode's guard throws. Laws I/IV are the hard tier everywhere; the consent rules ask where a host can ask and warn where it cannot. `tool-gate` fuses the two gates for hosts that run one command per event; `PreCompact` runs `learn` before Claude compacts; every block or ask lands in `notebook/gates.jsonl` on every host | [bob](adapters/bob/README.md) · [claude-code](adapters/claude-code/README.md) · [candidate hosts](docs/candidate-hosts.md) |
|
|
41
41
|
| **Workflow primitive** | Saved, code-driven subagent orchestration (`workflow` tool + runtime); shipped scripts include `dispatch` — decompose a goal, route each subtask to its owning agent, converge | `2026-06-09-opencode-workflow-primitive` |
|
|
42
42
|
| **Per-agent memory** | Each capability agent keeps durable lessons in `memory/agents/<name>.md`: read at dispatch, written back mechanically by `learn` on session/subagent end (OpenCode plugin + Claude `SubagentStop`; Bob has no subagent event); `learn --consolidate` rebuilds the index | `2026-07-04-orchestration-memory-upgrade` |
|
|
43
|
-
| **Wiki integration** | Machine-wide knowledge base (`wiki.jsonc`): eager/lazy loading, conventions, protected folders enforced at the tool boundary | `2026-06-11-wiki-knowledge-base` |
|
|
43
|
+
| **Wiki integration** | Machine-wide knowledge base (`geneseed-wiki.jsonc`): eager/lazy loading, conventions, protected folders enforced at the tool boundary | `2026-06-11-wiki-knowledge-base` |
|
|
44
44
|
| **Collaboration layer** | Negotiated postures (peer/mentor/expert/assistant/artisan, chosen at setup, inlined in AGENT.md), the bidirectional Pact, epistemically-typed memory (`force:` + Bridge rule), and a seed-once `PROFILE.md` identity with a web editor tab and a `profile` skill that interviews the user to draft it | `2026-07-12-collaboration-layer` |
|
|
45
45
|
| **Rule front door** | Both durable stores enter through the `rule` skill's fork (*standing rule, or fact to remember?* — always asked, never inferred): the memory branch elicits the binding `force` and shows the file before writing, the rule branch pressure-tests by reformulation and a mandatory counter-example. Doctrine process 1 carries the norm on every host; a `rule-gate` PreToolUse hook (Claude, Bob) and a `geneseed-guard` speed bump (OpenCode) hold it at the tool boundary | `2026-07-30-rule-front-door` |
|
|
46
46
|
| **Entity registry** | `registry.json` records a lifecycle status, version, owner and add-date for every agent and skill in the tree (maintainer-side — never rendered into a bundle, so it costs no session context). `doctor` gates it against `src/` in both directions, sweeps everything that ships for credentials, and holds each vendored skill folder to an immutable upstream commit. The status surfaces as a badge in the web catalog, shown only where it deviates from `approved` | `2026-07-31-entity-registry` |
|
|
@@ -19,7 +19,7 @@ Pick the first that resolves; remember the path for the rest of the session.
|
|
|
19
19
|
|
|
20
20
|
If none resolve, the user does not have Geneseed deployed here — say so and stop. Do not run `setup`, `build`, or `upgrade` on your own to "fix" this; that is a user decision.
|
|
21
21
|
|
|
22
|
-
A live harness root contains: `AGENT.md`, `ontology/universal.md`, `laws/universal.md`, `doctrines/<pack>.md`, `agents/<name>.md`, `skills/<name>/SKILL.md`, `memory/MEMORY.md`, `notebook/NOTEBOOK.md`, `themes/`, and (on OpenCode) `plugins/`, `workflows/`, `opencode.jsonc`, `wiki.jsonc`.
|
|
22
|
+
A live harness root contains: `AGENT.md`, `ontology/universal.md`, `laws/universal.md`, `doctrines/<pack>.md`, `agents/<name>.md`, `skills/<name>/SKILL.md`, `memory/MEMORY.md`, `notebook/NOTEBOOK.md`, `themes/`, and (on OpenCode) `plugins/`, `workflows/`, `opencode.jsonc`, `geneseed-wiki.jsonc`.
|
|
23
23
|
|
|
24
24
|
The constitution is **three tiers**: an always-on **Ontology** (four prose sections — Telos, Evidence, Decisions, Conduct), nine always-on **Rules** numbered I–IX, and the **Doctrines** — practice packs (`craft`, `rigor`, `ops`, `process`, `comms`) chosen per install at build time. `AGENT.md` carries all three inline (whole at the `full` footprint, heading-plus-first-line at `lean`), *and* the complete text ships as files beside it. All five pack files ship even when a pack was not built in, so a citation into an inactive pack still resolves on disk — which is why a question about a rule is answered from the tier file, never only from `AGENT.md`.
|
|
25
25
|
|
|
@@ -207,7 +207,7 @@ the harness live entirely in the global config dir with zero per-repo files.
|
|
|
207
207
|
context needs re-pushing.
|
|
208
208
|
- **Machine wiki (AGENT.md §8):** the same block carries a `MACHINE WIKI` segment
|
|
209
209
|
for the user's own knowledge base(s) — typically an Obsidian vault — declared in
|
|
210
|
-
`wiki.jsonc` (`$GENESEED_WIKI` → `$GENESEED_HARNESS/wiki.jsonc` → beside the
|
|
210
|
+
`geneseed-wiki.jsonc` (`$GENESEED_WIKI` → `$GENESEED_HARNESS/geneseed-wiki.jsonc` → beside the
|
|
211
211
|
install). Per wiki: eager entries inject in full, lazy entries list, and the
|
|
212
212
|
`conventions` / `inbox` / `protected` metadata is surfaced — on the **same**
|
|
213
213
|
budgets, compaction and transform paths as the project context. Schema and
|
|
@@ -385,7 +385,7 @@ behaviour** — nothing changes the machine's current agent/model unless you opt
|
|
|
385
385
|
- **Runtime guard plugin** (`geneseed-guard.js`, installed with the others). Enforces
|
|
386
386
|
the safety Laws at the tool boundary: **blocks** writes to private-key/credential
|
|
387
387
|
files (Sealed Secrets), catastrophic shell like `rm -rf /` (Deletion Is Deliberate), and any mutation under
|
|
388
|
-
a declared wiki's `protected` folders (AGENT.md §8, from `wiki.jsonc`); **warns** on
|
|
388
|
+
a declared wiki's `protected` folders (AGENT.md §8, from `geneseed-wiki.jsonc`); **warns** on
|
|
389
389
|
`.env` writes and force-push. `GENESEED_GUARD=off` disables it, `=warn` downgrades
|
|
390
390
|
blocks to warnings. Every **block** appends one line to the install's
|
|
391
391
|
`notebook/gates.jsonl` (rule `law-1` / `law-4` / `process-1` / `wiki`, a timestamp,
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
// is added per request, outside the cached block; unknown -> omitted.
|
|
28
28
|
//
|
|
29
29
|
// MACHINE WIKI (AGENT.md §8): the same block also carries the user's own knowledge
|
|
30
|
-
// base(s) — typically an Obsidian vault — declared once per machine in `wiki.jsonc`
|
|
31
|
-
// ($GENESEED_WIKI -> $GENESEED_HARNESS/wiki.jsonc -> beside this plugin's install).
|
|
30
|
+
// base(s) — typically an Obsidian vault — declared once per machine in `geneseed-wiki.jsonc`
|
|
31
|
+
// ($GENESEED_WIKI -> $GENESEED_HARNESS/geneseed-wiki.jsonc -> beside this plugin's install).
|
|
32
32
|
// Each wiki's eager entries inject in full and lazy entries list, drawing on the
|
|
33
33
|
// SAME budgets as the project context; its conventions/inbox/protected metadata is
|
|
34
|
-
// surfaced so the agent knows the house rules before writing. No wiki
|
|
34
|
+
// surfaced so the agent knows the house rules before writing. No wiki manifest, or an
|
|
35
35
|
// empty `wikis` list, costs nothing.
|
|
36
36
|
//
|
|
37
37
|
// DELIVERY (invisible by default): the context rides into each request's message
|
|
@@ -156,8 +156,12 @@ function log(msg) { if (DEBUG) console.error(`[geneseed-context] ${msg}`) }
|
|
|
156
156
|
// (OpenCode's own native roots) are deliberately absent: OpenCode already has them, and
|
|
157
157
|
// injecting them again re-sent the whole harness on every request until 2026-09.
|
|
158
158
|
const EAGER_ROOT = new Set([
|
|
159
|
-
".cursorrules", "README.md", "
|
|
159
|
+
".cursorrules", "README.md", "README.adoc", "CONTRIBUTING.md", "CONTRIBUTING.adoc",
|
|
160
|
+
"user-rules.md", "PROFILE.md",
|
|
160
161
|
])
|
|
162
|
+
// Project docs are Markdown or AsciiDoc (document-project writes AsciiDoc when the user asks
|
|
163
|
+
// for it). Same set as js/hosts/hooks-context.mjs DOC_EXT.
|
|
164
|
+
const isDoc = (name) => /\.(md|adoc)$/i.test(name)
|
|
161
165
|
// Doc trees walked recursively; everything found is lazy (listed, not injected).
|
|
162
166
|
const LAZY_DIRS = ["docs", "doc", "documentation", "architecture", "adr", "ADR"]
|
|
163
167
|
// Never descend into these.
|
|
@@ -174,14 +178,16 @@ async function isDir(p) { try { return (await fs.stat(p)).isDirectory() } catch
|
|
|
174
178
|
// holds the installed AGENT.md: the repo root for a project install, the config dir for a
|
|
175
179
|
// global one. Same list and seed hashes as SESSION_FILES/SEED_SHA256 in js/hosts/hooks.mjs;
|
|
176
180
|
// tests/unit/claude.test.mjs gates both copies against SESSION_SEEDS in js/build/stubs.mjs.
|
|
177
|
-
// context.json is not here: it is a manifest, honoured by resolveSource. wiki.jsonc
|
|
178
|
-
// here either: the MACHINE WIKI block below already renders it in full.
|
|
181
|
+
// context.json is not here: it is a manifest, honoured by resolveSource. geneseed-wiki.jsonc
|
|
182
|
+
// is not here either: the MACHINE WIKI block below already renders it in full.
|
|
179
183
|
const SESSION_FILES = ["user-rules.md", "PROFILE.md", "memory/MEMORY.md", "anamnesis/MEMORY.md",
|
|
180
184
|
"notebook/NOTEBOOK.md"]
|
|
181
185
|
const SEED_SHA256 = new Set([
|
|
182
186
|
"5c2e92fb1acde041e02d9ebf158460d8ff7a07cbc2b447859631604610130721", // user-rules.md
|
|
183
187
|
"29e012c3c4349ea62a9082cbd04bb25599a0ec280d62c33f6e10742809471816", // PROFILE.md
|
|
184
|
-
"
|
|
188
|
+
"3a82c78ccc5d745033c16c354f5eda9d79350c704e9da8d605d380fb1e108d7a", // geneseed-wiki.jsonc
|
|
189
|
+
// The wiki seed before the rename: the build renames an untouched one byte for byte.
|
|
190
|
+
"cccc917c34e6b990e821620bdb4739090151885ab71b6e85046f30a3b71fa5e8", // wiki.jsonc (legacy)
|
|
185
191
|
"99c786049c260f6baf7aec68a5c0f59907861afe9b867da009440613bdddb907", // MEMORY.md
|
|
186
192
|
"9acb5c9d9cb5dac572104480f05b48cc144e676869bd60e86d8a15b1f6308184", // NOTEBOOK.md
|
|
187
193
|
])
|
|
@@ -217,8 +223,8 @@ async function readJson(p) {
|
|
|
217
223
|
}
|
|
218
224
|
|
|
219
225
|
// Strip JSONC niceties — // and /* */ comments plus trailing commas — string-aware,
|
|
220
|
-
// so a "https://…" or "C:/Users/…" inside quotes is untouched. wiki
|
|
221
|
-
// with a commented example, so its readers must tolerate comments.
|
|
226
|
+
// so a "https://…" or "C:/Users/…" inside quotes is untouched. the wiki manifest is
|
|
227
|
+
// seeded with a commented example, so its readers must tolerate comments.
|
|
222
228
|
function stripJsonc(text) {
|
|
223
229
|
let out = "", inStr = false, esc = false
|
|
224
230
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -277,11 +283,13 @@ function globToRegExp(glob) {
|
|
|
277
283
|
return new RegExp("^" + re + "$")
|
|
278
284
|
}
|
|
279
285
|
|
|
280
|
-
// First
|
|
286
|
+
// First heading line of a markdown or AsciiDoc file, for the lazy listing. An AsciiDoc
|
|
287
|
+
// title is "= Title"; the space is required so a setext "====" underline never matches.
|
|
281
288
|
function firstHeading(text) {
|
|
282
289
|
for (const line of text.split("\n")) {
|
|
283
290
|
const s = line.trim()
|
|
284
291
|
if (s.startsWith("#")) return s.replace(/^#+\s*/, "").trim()
|
|
292
|
+
if (/^=+\s/.test(s)) return s.replace(/^=+\s*/, "").trim()
|
|
285
293
|
}
|
|
286
294
|
return ""
|
|
287
295
|
}
|
|
@@ -425,7 +433,7 @@ async function walkMd(dir, root, depth, acc) {
|
|
|
425
433
|
if (e.isDirectory()) {
|
|
426
434
|
if (EXCLUDE_DIRS.has(e.name)) continue
|
|
427
435
|
await walkMd(full, root, depth + 1, acc)
|
|
428
|
-
} else if (e.isFile() && e.name
|
|
436
|
+
} else if (e.isFile() && isDoc(e.name)) {
|
|
429
437
|
acc.push(full)
|
|
430
438
|
}
|
|
431
439
|
}
|
|
@@ -448,7 +456,7 @@ async function discover(root) {
|
|
|
448
456
|
if (!e.isFile()) continue
|
|
449
457
|
const abs = path.join(root, e.name)
|
|
450
458
|
if (EAGER_ROOT.has(e.name)) add(eager, abs)
|
|
451
|
-
else if (e.name
|
|
459
|
+
else if (isDoc(e.name)) add(lazy, abs) // misc root .md / .adoc
|
|
452
460
|
}
|
|
453
461
|
|
|
454
462
|
// 2. Doc trees (recursive) -> lazy.
|
|
@@ -469,8 +477,10 @@ async function discover(root) {
|
|
|
469
477
|
try { pkgs = await fs.readdir(base, { withFileTypes: true }) } catch { continue }
|
|
470
478
|
for (const p of pkgs) {
|
|
471
479
|
if (!p.isDirectory() || EXCLUDE_DIRS.has(p.name)) continue
|
|
472
|
-
const
|
|
473
|
-
|
|
480
|
+
for (const name of ["README.md", "README.adoc"]) {
|
|
481
|
+
const readme = path.join(base, p.name, name)
|
|
482
|
+
if (await isFile(readme) && !eager.has(readme)) add(lazy, readme)
|
|
483
|
+
}
|
|
474
484
|
}
|
|
475
485
|
}
|
|
476
486
|
|
|
@@ -542,13 +552,13 @@ async function resolveSource(root, harness = null) {
|
|
|
542
552
|
return { mode: "discover" }
|
|
543
553
|
}
|
|
544
554
|
|
|
545
|
-
// ---- machine wiki (wiki.jsonc)
|
|
555
|
+
// ---- machine wiki (geneseed-wiki.jsonc) ------------------------------------------
|
|
546
556
|
// The user's own knowledge base(s) — typically an Obsidian vault — declared once per
|
|
547
557
|
// machine, not per repo (AGENT.md §8). Same injection mechanics as project context,
|
|
548
558
|
// different scope. Resolution (first match wins, mirroring the learn plugin):
|
|
549
559
|
// 1. $GENESEED_WIKI explicit manifest path
|
|
550
|
-
// 2. $GENESEED_HARNESS/wiki.jsonc
|
|
551
|
-
// 3. <plugin dir>/../wiki.jsonc
|
|
560
|
+
// 2. $GENESEED_HARNESS/geneseed-wiki.jsonc pinned install dir
|
|
561
|
+
// 3. <plugin dir>/../geneseed-wiki.jsonc auto-locate: beside the installed AGENT.md
|
|
552
562
|
async function resolveWikiFile() {
|
|
553
563
|
const explicit = process.env.GENESEED_WIKI
|
|
554
564
|
if (explicit && (await isFile(explicit))) return explicit
|
|
@@ -560,8 +570,8 @@ async function resolveWikiFile() {
|
|
|
560
570
|
if (harness) bases.push(harness)
|
|
561
571
|
bases.push(path.resolve(PLUGIN_DIR, ".."))
|
|
562
572
|
for (const base of bases) {
|
|
563
|
-
// wiki.json
|
|
564
|
-
for (const name of ["wiki.jsonc", "wiki.json"]) {
|
|
573
|
+
// wiki.jsonc and wiki.json are the legacy names from earlier seeds — still honoured.
|
|
574
|
+
for (const name of ["geneseed-wiki.jsonc", "wiki.jsonc", "wiki.json"]) {
|
|
565
575
|
const p = path.join(base, name)
|
|
566
576
|
if (await isFile(p)) return p
|
|
567
577
|
}
|
|
@@ -594,7 +604,7 @@ async function walkWikiDir(dir, depth, acc) {
|
|
|
594
604
|
// folders on demand instead.
|
|
595
605
|
const WIKI_LAZY_LIMIT = envNum("GENESEED_WIKI_LAZY_LIMIT", 200)
|
|
596
606
|
|
|
597
|
-
// Parse wiki
|
|
607
|
+
// Parse the wiki manifest into renderable wikis: [{ name, root, desc, conventions, inbox,
|
|
598
608
|
// protected, eager:[{rel,abs,desc}], lazy:[...], truncated }]. Entry paths resolve
|
|
599
609
|
// against the wiki's own root and may name a single note OR a folder — a folder
|
|
600
610
|
// applies its load mode to every note beneath it (`.` covers the whole vault).
|
|
@@ -605,7 +615,7 @@ const WIKI_LAZY_LIMIT = envNum("GENESEED_WIKI_LAZY_LIMIT", 200)
|
|
|
605
615
|
async function wikiSets() {
|
|
606
616
|
const file = await resolveWikiFile()
|
|
607
617
|
if (!file) return []
|
|
608
|
-
const data = await readJsonc(file) //
|
|
618
|
+
const data = await readJsonc(file) // the manifest is JSONC: stub ships commented
|
|
609
619
|
const wikis = Array.isArray(data?.wikis) ? data.wikis : []
|
|
610
620
|
const out = []
|
|
611
621
|
for (const w of wikis) {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// - Persist Insight (rule vs memory): speed-bump the first write to user-rules.md
|
|
8
8
|
// memory file — that choice belongs to the user, via the rule skill.
|
|
9
9
|
// - Wiki (AGENT.md §8): block mutations under a declared wiki's `protected`
|
|
10
|
-
// folders — the user's knowledge base sets its own no-go zones in wiki.jsonc.
|
|
10
|
+
// folders — the user's knowledge base sets its own no-go zones in geneseed-wiki.jsonc.
|
|
11
11
|
// High-confidence patterns only, so legitimate work is never caught. Borderline cases
|
|
12
12
|
// (.env edits, force-push) are WARNED, not blocked.
|
|
13
13
|
//
|
|
@@ -171,13 +171,13 @@ const SHELL_TOOLS = ["bash", "shell", "exec", "command", "terminal", "run"]
|
|
|
171
171
|
const hasAny = (name, parts) => parts.some((s) => name.includes(s))
|
|
172
172
|
|
|
173
173
|
// ---- protected wiki folders (AGENT.md §8) --------------------------------------
|
|
174
|
-
// wiki.jsonc (the machine-level knowledge-base manifest) may list `protected` folders
|
|
174
|
+
// geneseed-wiki.jsonc (the machine-level knowledge-base manifest) may list `protected` folders
|
|
175
175
|
// per wiki. Mutating anything under one is denied. Same resolution chain as the
|
|
176
|
-
// context plugin: $GENESEED_WIKI -> $GENESEED_HARNESS/wiki.jsonc -> beside the install.
|
|
176
|
+
// context plugin: $GENESEED_WIKI -> $GENESEED_HARNESS/geneseed-wiki.jsonc -> beside the install.
|
|
177
177
|
async function isFile(p) { try { return (await fs.stat(p)).isFile() } catch { return false } }
|
|
178
178
|
async function isDir(p) { try { return (await fs.stat(p)).isDirectory() } catch { return false } }
|
|
179
179
|
|
|
180
|
-
// wiki
|
|
180
|
+
// The wiki manifest is JSONC (the seeded stub carries a commented example): strip // and
|
|
181
181
|
// /* */ comments plus trailing commas before parsing — string-aware, so quoted
|
|
182
182
|
// "https://…" or "C:/…" values are untouched. Kept in sync with the context plugin's
|
|
183
183
|
// copy (plugins stay self-contained, like the other shared helpers).
|
|
@@ -226,8 +226,8 @@ async function wikiFile() {
|
|
|
226
226
|
if (process.env.GENESEED_HARNESS) bases.push(process.env.GENESEED_HARNESS)
|
|
227
227
|
bases.push(path.resolve(PLUGIN_DIR, ".."))
|
|
228
228
|
for (const base of bases) {
|
|
229
|
-
// wiki.json
|
|
230
|
-
for (const name of ["wiki.jsonc", "wiki.json"]) {
|
|
229
|
+
// wiki.jsonc and wiki.json are the legacy names from earlier seeds — still honoured.
|
|
230
|
+
for (const name of ["geneseed-wiki.jsonc", "wiki.jsonc", "wiki.json"]) {
|
|
231
231
|
const p = path.join(base, name)
|
|
232
232
|
if (await isFile(p)) return p
|
|
233
233
|
}
|
|
@@ -235,7 +235,7 @@ async function wikiFile() {
|
|
|
235
235
|
return null
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
// Cached absolute prefixes, refreshed on a short TTL so a
|
|
238
|
+
// Cached absolute prefixes, refreshed on a short TTL so a manifest edit lands
|
|
239
239
|
// without a restart. Compared slash-normalized and case-insensitive — vault paths on
|
|
240
240
|
// Windows and macOS are case-insensitive in practice, and for a guard the rare
|
|
241
241
|
// case-only over-match is the safe direction.
|
|
@@ -288,6 +288,32 @@ function ruleStoreTarget(p) {
|
|
|
288
288
|
return nocase(abs).startsWith(nocase(mem) + "/") ? "memory" : null
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
// ---- protected checks (External Gate) ------------------------------------------
|
|
292
|
+
// Twin of js/hosts/hooks.mjs's `protectedCheck` — keep in step. A project lists the checks its agent
|
|
293
|
+
// must not edit in `.geneseed/protected-checks.txt` at its git root, one repo-relative path per
|
|
294
|
+
// line; the list protects itself. Found from the WRITE TARGET up to the first `.git`, re-read on
|
|
295
|
+
// every write (a short file, read only on a write call). No ask tier here, so a hit is a deny.
|
|
296
|
+
const SENSOR_LIST = ".geneseed/protected-checks.txt"
|
|
297
|
+
function protectedCheck(p) {
|
|
298
|
+
try {
|
|
299
|
+
const target = path.resolve(p)
|
|
300
|
+
let root = path.dirname(target)
|
|
301
|
+
while (!existsSync(path.join(root, ".git"))) {
|
|
302
|
+
const up = path.dirname(root)
|
|
303
|
+
if (up === root) return null
|
|
304
|
+
root = up
|
|
305
|
+
}
|
|
306
|
+
const text = readFileSync(path.join(root, SENSOR_LIST), "utf8")
|
|
307
|
+
const fold = (s) => nocase(s.replace(/\\/g, "/"))
|
|
308
|
+
const rel = fold(path.relative(root, target))
|
|
309
|
+
const entries = [SENSOR_LIST, ...text.split(/\r?\n/).map((l) => l.trim().replace(/[\\/]+$/, ""))
|
|
310
|
+
.filter((l) => l && !l.startsWith("#"))]
|
|
311
|
+
return entries.find((e) => rel === fold(e) || rel.startsWith(fold(e) + "/")) ?? null
|
|
312
|
+
} catch {
|
|
313
|
+
return null // no list (the common case) or an unreadable one: nothing is protected
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
291
317
|
// Mutation-class tools for the wiki check — wider than WRITE_TOOLS because moving,
|
|
292
318
|
// renaming, or deleting a protected note is as destructive as overwriting it. Same
|
|
293
319
|
// substring stance: over-matching the class is harmless (see WRITE_TOOLS note).
|
|
@@ -437,6 +463,8 @@ export const GeneseedGuard = async (ctx) => {
|
|
|
437
463
|
const p = pickPath(args)
|
|
438
464
|
if (p && SECRET_RE.some((re) => re.test(p))) { await deny(`write to secret/key file ${p} (Sealed Secrets)`, "law-1"); return }
|
|
439
465
|
if (p && SECRET_WARN_RE.some((re) => re.test(p))) log(`WARN: writing ${p} — keep secrets out of tracked files (Sealed Secrets)`)
|
|
466
|
+
const sensor = p && protectedCheck(p)
|
|
467
|
+
if (sensor) { await deny(`${p} is a protected check (${sensor}, listed in ${SENSOR_LIST}) — a check the agent can edit is not a check (External Gate)`, "rigor-5"); return }
|
|
440
468
|
const store = p && ruleStoreTarget(p)
|
|
441
469
|
if (store && !RULE_STORE_BUMPED.has(p)) {
|
|
442
470
|
RULE_STORE_BUMPED.add(p)
|
package/bin/geneseed-cli.mjs
CHANGED
|
@@ -155,6 +155,11 @@ const VERBS = {
|
|
|
155
155
|
// lives entirely inside this port, so — like the three above — it has no recorded cell to
|
|
156
156
|
// compare against, only the absolute gates named beside it in `NATIVE`.
|
|
157
157
|
loop: () => import('../js/loop/cli.mjs').then((m) => m.cmdLoop),
|
|
158
|
+
// A fifth native verb, and a port rather than an invention: the AI Harness Scorecard
|
|
159
|
+
// (upstream Python, pinned in the module's docblock). No Geneseed Python subparser ever
|
|
160
|
+
// existed for it, so there is no recorded cell — its absolute gate is
|
|
161
|
+
// tests/unit/scorecard.test.mjs. Lazy like every row: the 31 checks load only when asked.
|
|
162
|
+
scorecard: () => import('../js/inspect/scorecard.mjs').then((m) => m.cmdScorecard),
|
|
158
163
|
};
|
|
159
164
|
|
|
160
165
|
function die(code, msg) {
|
package/bin/geneseed.mjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The generator's PRE-RENAME path, kept as a forwarder to `bin/build-driver.mjs`.
|
|
4
|
+
*
|
|
5
|
+
* WHY A RENAMED FILE STILL HAS TO EXIST. `geneseed upgrade` runs the updater it started with:
|
|
6
|
+
* its module graph is loaded before the `git pull`, so an updater older than the rename
|
|
7
|
+
* (5c970dc9, 2026-08) pulls the new tree and then spawns `bin/geneseed.mjs` to rebuild —
|
|
8
|
+
* a file the tree it just pulled no longer has. The doctor gate passes (it spawns
|
|
9
|
+
* `geneseed-cli.mjs`, which never moved), and the rebuild dies with MODULE_NOT_FOUND and
|
|
10
|
+
* `[E-BUILD]`. An old web daemon's build jobs spawn the same path. Nothing in this tree
|
|
11
|
+
* calls it, and it has no npm name on purpose: it is a landing pad for old processes, not a
|
|
12
|
+
* fourth entry point.
|
|
13
|
+
*
|
|
14
|
+
* Importing the driver entry runs it on this process's argv, which is the argv the old
|
|
15
|
+
* caller passed — so the forwarding is the import and nothing else.
|
|
16
|
+
*/
|
|
17
|
+
import './build-driver.mjs';
|
|
@@ -61,7 +61,7 @@ The install records it (`Excluded rules: process 7` beside its pack list) and ev
|
|
|
61
61
|
Every installed skill costs a line in the host's skill listing, every session. Skills you never use can be left out of an install:
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
geneseed build --emit claude-global --exclude-skills "bruno
|
|
64
|
+
geneseed build --emit claude-global --exclude-skills "bruno,react-view-transitions"
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
The skill's files are not written, its catalogue row goes, and an OpenCode `/command` that runs it goes too. The install records the list (`Excluded skills: …` under its skill section), and every rebuild and upgrade keeps it. `--exclude-skills none` puts them all back.
|
package/docs/concepts/hooks.md
CHANGED
|
@@ -36,6 +36,7 @@ Geneseed writes its hooks into the host's settings file. Every hook calls one pr
|
|
|
36
36
|
**rule-gate.** Runs before every file write.
|
|
37
37
|
- Content that looks like a credential (a cloud access key, a GitHub or Anthropic token, a Slack token, a private-key block) going into any file other than `.env*` gets a prompt citing *Sealed Secrets*.
|
|
38
38
|
- A write to `user-rules.md`, `MEMORY.md` or a file in the install's `memory/` gets a prompt citing *Persist Insight*: whether something is a standing rule or a fact to remember is your call, settled through the `rule` [skill](skills.md).
|
|
39
|
+
- A write to a check the project protects gets a prompt citing *External Gate* (on Bob, it is blocked). A project opts in with `.geneseed/protected-checks.txt` at its git root: one repo-relative file or folder per line, `#` for comments — the checks and tests the agent is judged by. A check the agent can edit is not a check. The list protects itself. Only file-write tools are seen: a shell `sed -i` or redirect is not, so run the same checks in CI as the boundary that holds.
|
|
39
40
|
- Ordinary edits pass untouched.
|
|
40
41
|
|
|
41
42
|
**learn.** Takes the tail of the transcript and asks a model to distil what is worth keeping into one-fact-per-file entries under `memory/`, skipping duplicates. It needs `GENESEED_LLM` set to a model command, for example:
|
|
@@ -21,6 +21,9 @@ brick's behalf. This applies to `plan` and `review` as much as to the newer bric
|
|
|
21
21
|
brick whose finding the next brick needs writes it to a file in the OS temp directory and reports
|
|
22
22
|
that file's path with the outcome.
|
|
23
23
|
|
|
24
|
+
One exception keeps the verdict fresh: each `test` report replaces every earlier `test` note, so a
|
|
25
|
+
repair reads only the findings for the code as it stands, never a trace from an older version.
|
|
26
|
+
|
|
24
27
|
## `description` and `rules`: what a template is for, and what every brick must do
|
|
25
28
|
|
|
26
29
|
A template's JSON carries two prose fields, and they are never confused:
|
package/docs/concepts/loops.md
CHANGED
|
@@ -47,6 +47,17 @@ all gets exactly 1 — no re-entry — which is also why **every cycle inside an
|
|
|
47
47
|
named inner loop** in the graph JSON: `geneseed loop check` (and `loop init`, which runs the same
|
|
48
48
|
check) refuses a graph with an undeclared cycle inside an iteration, before a single node runs.
|
|
49
49
|
|
|
50
|
+
**A ring's `max` is also its own repair budget.** The edge that returns to a ring's first node
|
|
51
|
+
from inside it — `test` → `apply` — is charged to the smallest ring holding both ends, and that
|
|
52
|
+
ring stops at its `max` even when a wider ring raises the node's shared budget. The shipped test
|
|
53
|
+
rings (`apply-test`, `fix-test`, `write-run`, `stabilise`) are `max: 1`: one repair, then the
|
|
54
|
+
attempt is discarded for one fresh try, then the loop stops for you. A second revision makes the
|
|
55
|
+
result worse, not better — 0.82 correct after one, 0.673 after two (arXiv 2607.24604).
|
|
56
|
+
|
|
57
|
+
**`error` always stops.** A brick may declare the outcome `error` without an edge for it; the
|
|
58
|
+
engine sends it to `$stop`. The shipped `test` brick reports it when a check crashed or its
|
|
59
|
+
evidence was never wired — no verdict is not a failure to repair.
|
|
60
|
+
|
|
50
61
|
## Limits
|
|
51
62
|
|
|
52
63
|
- **No parallel nodes.** The graph runs one node at a time; there is no fan-out.
|
package/docs/concepts/skills.md
CHANGED
|
@@ -42,6 +42,10 @@ The full list, with each description, is in the catalog.
|
|
|
42
42
|
|
|
43
43
|
Every skill ends with one beat of reflection: if a step misled or a step was missing, the agent proposes the exact edit to that skill and applies it only with your assent. Most runs end with nothing to change.
|
|
44
44
|
|
|
45
|
+
### Old names keep working
|
|
46
|
+
|
|
47
|
+
When two skills merge, the old name still works as a slash command: `/deps-audit` runs `dependencies`, and `/ci-fix` runs `debug`. The old names are user-only, so the agent's list shows each skill once. If you had left out a skill that was later merged, a rebuild leaves out the merged skill instead. A skill removed with no replacement is dropped from that list, with a note.
|
|
48
|
+
|
|
45
49
|
### What they cost
|
|
46
50
|
|
|
47
51
|
The skill *bodies* are read only when used. The *descriptions* are listed to the agent every turn so it can pick the right one — that list is part of the always-on cost described in [Footprint](footprint.md).
|
package/docs/extending.md
CHANGED
|
@@ -349,6 +349,10 @@ Intent became IX.
|
|
|
349
349
|
|
|
350
350
|
## 3 — Adding a SKILL or an AGENT
|
|
351
351
|
|
|
352
|
+
**First, try not to.** Skills rot really fast. Can an existing skill absorb this, or can one be
|
|
353
|
+
merged or deleted to make room? Every skill adds a description to the always-on list and one more
|
|
354
|
+
candidate for routing to choose between, so add only what no sharpened existing skill covers.
|
|
355
|
+
|
|
352
356
|
**Order matters at the first two steps.** `missingReferencedSpecs` (in `js/build/bundle.mjs`) *aborts the
|
|
353
357
|
build* when `AGENT.md.tmpl` names a spec with no file — so the file comes before the table row, or
|
|
354
358
|
nothing builds at all. The refusal is clean: `tests/unit/emit_gates.test.mjs:265` proves a refused
|
|
@@ -396,6 +400,35 @@ Two traps worth knowing before you write the spec:
|
|
|
396
400
|
which drags in the `web/dist` rebuild — and an unknown category lands under Personal in the
|
|
397
401
|
Library (`splitSkills` in `web/src/pages/Library.jsx`) with no warning.
|
|
398
402
|
|
|
403
|
+
**Merging or removing a skill keeps its old name working.** When a skill absorbs another, put
|
|
404
|
+
`<!-- aliases: old-name, other-old-name -->` on its own line under the surviving skill's purpose
|
|
405
|
+
blockquote. Every host then answers `/old-name` with the merged skill's body. Claude Code,
|
|
406
|
+
OpenClaude and Bob get a user-only `skills/<alias>/SKILL.md`, and OpenCode gets a
|
|
407
|
+
`command/<alias>.md`. Neither form is counted as a skill. An install that excluded the old name
|
|
408
|
+
now excludes the merged skill (`resolveSkillNames` in `js/build/source.mjs`), so a rebuild still
|
|
409
|
+
works. A skill removed with no successor goes into `RETIRED_SKILL_IDS` in the same file: its
|
|
410
|
+
exclusion is dropped with a notice instead of failing the rebuild, and the name can never be
|
|
411
|
+
used for a new skill. The doctor (`aliasProblems`) refuses an alias that is malformed, duplicated,
|
|
412
|
+
already a skill, retired, or the name of an OpenCode command.
|
|
413
|
+
|
|
414
|
+
**A rarely-needed half goes in a side file.** A skill body is paid in full every time the skill
|
|
415
|
+
loads. A branch only some runs need, or a big reference, can move into
|
|
416
|
+
`src/skills/<name>/<file>.md` beside the flat spec. The body keeps one pointer line, such as debug's
|
|
417
|
+
`**CI run red** → read [debug/ci.md](debug/ci.md) and follow it.` The side file costs nothing
|
|
418
|
+
until the agent follows that pointer, and then it costs its own size. So move a half only when
|
|
419
|
+
most runs skip it. The flat spec stays the source, so steps 1–9 are unchanged. The side file is
|
|
420
|
+
themed like a spec, and `{{LAW:id}}` citations resolve in it and are gated. Every emit writes it
|
|
421
|
+
next to the skill: `skills/<name>/<file>.md` in the flat bundle, and beside `SKILL.md` on every
|
|
422
|
+
native host, through `claim()`. Native hosts get a code-span path instead of the link:
|
|
423
|
+
`<this-skill-directory>` where the host announces the skill's directory, and the resolved
|
|
424
|
+
directory on Bob and in OpenCode commands, aliases included (`pointSideFiles` in
|
|
425
|
+
`js/hosts/native.mjs`). The doctor (`sideFileProblems`) requires a flat spec for the folder, `.md`
|
|
426
|
+
files one level down, no `SKILL.md`, and a link to every side file from the body.
|
|
427
|
+
`tests/unit/side_files.test.mjs` resolves every emitted pointer. The dead-link scan cannot,
|
|
428
|
+
because a code span is not a link. Measured on 2026-10-08, with the neutral theme at the lean
|
|
429
|
+
footprint: debug's SKILL.md went from 1.64k to 1.18k tokens and rule's from 2.33k to 1.83k
|
|
430
|
+
(chars/4).
|
|
431
|
+
|
|
399
432
|
|
|
400
433
|
---
|
|
401
434
|
|
package/docs/guides/headless.md
CHANGED
|
@@ -11,7 +11,7 @@ Once the harness is installed for OpenCode (global or per repo), OpenCode can ru
|
|
|
11
11
|
|
|
12
12
|
```
|
|
13
13
|
opencode run "review the staged diff and list any correctness bugs" # one shot, prints to stdout
|
|
14
|
-
opencode run -m anthropic/claude-sonnet-
|
|
14
|
+
opencode run -m anthropic/claude-sonnet-5 "…" # pin a model for this run
|
|
15
15
|
cat issue.md | opencode run "triage this and propose a fix plan" # pipe input in
|
|
16
16
|
```
|
|
17
17
|
|
|
@@ -21,8 +21,8 @@ The context step sorts every doc it finds into one of two lists:
|
|
|
21
21
|
|
|
22
22
|
With no configuration, the context step finds these files:
|
|
23
23
|
|
|
24
|
-
- **Eager:** root `AGENTS.md`, `AGENT.md`, `CLAUDE.md` and `.cursorrules`, plus `README.md` and `CONTRIBUTING.md`. The root file your tool already loads by itself is skipped, so you never pay for it twice.
|
|
25
|
-
- **Lazy:** `docs/`, `doc/`, `documentation/`, `architecture/`, `adr/` and `ADR/`, monorepo `packages/*/README.md` and `apps/*/README.md`, and any other root `*.md`.
|
|
24
|
+
- **Eager:** root `AGENTS.md`, `AGENT.md`, `CLAUDE.md` and `.cursorrules`, plus `README.md` and `CONTRIBUTING.md` (or their AsciiDoc `.adoc` twins). The root file your tool already loads by itself is skipped, so you never pay for it twice.
|
|
25
|
+
- **Lazy:** `docs/`, `doc/`, `documentation/`, `architecture/`, `adr/` and `ADR/`, monorepo `packages/*/README.md` and `apps/*/README.md`, and any other root `*.md`. AsciiDoc docs (`.adoc`) are found everywhere a `.md` is.
|
|
26
26
|
- **Never scanned:** `node_modules`, `.git`, `dist`, `build`, `vendor`, `.next`, `target`, `.venv`, `__pycache__`, `.opencode` and `.harness`.
|
|
27
27
|
|
|
28
28
|
On Claude Code, Bob and OpenClaude the whole injection stays under 9,000 characters, because Claude Code hands the model only a short preview of a hook output past about 10,000 and keeps the rest in a file. Your session files come first, then eager files whole; an eager file that does not fit is listed instead, to be read on demand. On OpenCode eager files share a 48 KB budget. On every host, three or more docs in one folder are listed as that folder with a count (`docs/guides/ — 30 docs`), not file by file.
|
|
@@ -6,12 +6,12 @@ kind: "concept"
|
|
|
6
6
|
section: "Set up"
|
|
7
7
|
description: "Let the agent read PDF, Word, PowerPoint, Excel and HTML through a converter."
|
|
8
8
|
---
|
|
9
|
-
The agent's doc discovery
|
|
9
|
+
The agent's doc discovery sees markdown (`.md`) and AsciiDoc (`.adoc`) only. The `ingest` skill teaches the agent to convert a PDF, Word, PowerPoint, Excel or HTML file, or a URL, to markdown before reading it. Install one converter and the skill uses it:
|
|
10
10
|
|
|
11
11
|
- **MarkItDown**: the broadest. Use [its MCP server](mcp-markitdown.md) (preferred on a host with MCP: nothing to install per call, one cheap tool), or `pip install markitdown`.
|
|
12
12
|
- **Pandoc**: excellent for Office and HTML, and a single binary.
|
|
13
13
|
- **Docling** (IBM): best for complex tables and scanned PDFs.
|
|
14
14
|
|
|
15
|
-
When an MCP converter is available, the skill prefers it. A prompt like
|
|
15
|
+
When an MCP converter is available, the skill prefers it. A prompt like "`convert file:///path/to/spec.pdf to markdown`" then just works. The skill never installs a converter silently. If none is present, it tells you which one to add.
|
|
16
16
|
|
|
17
17
|
Converter not found, or an MCP converter listed but not connecting? See [Model, agent and MCP problems](../reference/troubleshoot-tools.md).
|
package/docs/guides/uninstall.md
CHANGED
|
@@ -33,7 +33,7 @@ It also removes the install's row from the registry of installs. Start a new ses
|
|
|
33
33
|
## What it keeps
|
|
34
34
|
|
|
35
35
|
- **`memory/` and `notebook/`.** These are never deleted. `--archive-memory` moves both aside, into sibling `archived-memory/<timestamp>/` and `archived-notebook/<timestamp>/` folders, instead of leaving them in place. Even then they are moved, not deleted.
|
|
36
|
-
- **`wiki.jsonc
|
|
36
|
+
- **`geneseed-wiki.jsonc`** (or `wiki.jsonc`, its name before the rename), **`context.json`** and the **`improvements/`** folder. None of these are in the manifest.
|
|
37
37
|
- **Other installs.** Removing the global install does not touch any project install. Uninstall lists the project installs that remain, each with the command that removes it. If a repo carries installs for several hosts, uninstall removes one and tells you to run it again for the next.
|
|
38
38
|
|
|
39
39
|
## The hook shim
|