geneseed 4.14.0 → 4.16.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 +38 -0
- package/README.md +1 -1
- package/SHIPPED.md +1 -1
- package/adapters/opencode/plugins/geneseed-guard.js +16 -1
- package/adapters/opencode/plugins/geneseed-learn.js +28 -12
- package/adapters/opencode/plugins/geneseed-workflow.js +19 -2
- package/docs/concepts/foreman-mode.md +6 -4
- package/docs/concepts/rules.md +2 -1
- package/docs/guides/install-bob.md +3 -1
- package/docs/guides/upgrade.md +2 -0
- package/harness.config.json +1 -1
- package/js/build/emit-claude.mjs +4 -2
- package/js/build/stubs.mjs +13 -0
- package/js/inspect/cost.mjs +1 -1
- package/js/web/api.mjs +6 -2
- package/js/web/daemon.mjs +2 -2
- package/js/web/handler.mjs +11 -0
- package/package.json +1 -1
- package/src/AGENT.md.tmpl +2 -1
- package/src/doctrines/comms.md +21 -0
- package/src/modes/foreman.md +13 -5
- package/src/skills/pipeline.md +22 -10
- package/themes/_TEMPLATE.json +1 -0
- package/themes/biker.json +1 -0
- package/themes/commentator.json +1 -0
- package/themes/cyberpunk.json +1 -0
- package/themes/gamer.json +1 -0
- package/themes/imperial.json +1 -0
- package/themes/joker.json +1 -0
- package/themes/marvin.json +1 -0
- package/themes/mean.json +1 -0
- package/themes/military.json +1 -0
- package/themes/neutral.json +1 -0
- package/themes/pirate.json +1 -0
- package/themes/sports.json +1 -0
- package/themes/verstappen.json +1 -0
- package/themes/wizard.json +1 -0
- package/web/dist/assets/{Activity-44rbWUQI.js → Activity-Dc29x6Bb.js} +1 -1
- package/web/dist/assets/{ActivityDetail-Dk9k893a.js → ActivityDetail-CJYuaryv.js} +1 -1
- package/web/dist/assets/Dashboard--9UIBMVY.js +2 -0
- package/web/dist/assets/Docs-D8LcDC4g.js +5 -0
- package/web/dist/assets/{GreenhouseView-BXAOIznW.js → GreenhouseView-Lv_I5ndg.js} +1 -1
- package/web/dist/assets/{Installs-DAXF403D.js → Installs-BcfJ4xiR.js} +3 -3
- package/web/dist/assets/JournalView-ja3ZSAEQ.js +1 -0
- package/web/dist/assets/{KitApply-pjhvGha0.js → KitApply-BxOMaoKP.js} +1 -1
- package/web/dist/assets/{Kits-BCCKjSIY.js → Kits-nt_j0zos.js} +1 -1
- package/web/dist/assets/{Laws-BgBUpRkE.js → Laws-BORuVOX_.js} +2 -2
- package/web/dist/assets/{Library-CW0Zl6RF.js → Library-p8zDj1na.js} +1 -1
- package/web/dist/assets/{Loops-BvFOzjEw.js → Loops-CvWzox50.js} +2 -2
- package/web/dist/assets/{OperatorHudView-Bs4unPNi.js → OperatorHudView-DLFP3hH7.js} +1 -1
- package/web/dist/assets/{Personal-C23XaT-a.js → Personal-Bc16aeB8.js} +1 -1
- package/web/dist/assets/{Tabs-b2XFu4OS.js → Tabs-C57CyhSn.js} +1 -1
- package/web/dist/assets/drawable-UEPfHNCy.js +1 -0
- package/web/dist/assets/index-CKCvC6Qx.js +2 -0
- package/web/dist/index.html +1 -1
- package/web/src/pages/Laws.jsx +5 -0
- package/web/dist/assets/Dashboard-DCoUN60W.js +0 -2
- package/web/dist/assets/Docs-C_je2lRG.js +0 -5
- package/web/dist/assets/JournalView-DZ184wKB.js +0 -1
- package/web/dist/assets/drawable-BERkKhx_.js +0 -1
- package/web/dist/assets/index-C9R_bRw_.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,44 @@ label. For the capability ↔ spec map, see [SHIPPED.md](SHIPPED.md).
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [4.16.0] — 2026-10-23
|
|
12
|
+
|
|
13
|
+
Minor: foreman mode now really delegates, and the web console updates and rebuilds a clone without a manual restart.
|
|
14
|
+
|
|
15
|
+
### Upgrade notes
|
|
16
|
+
- Run `geneseed rebuild-all`; nothing changes on a machine until something re-emits. The precedence table changes in every install, not only in foreman ones.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Foreman mode: the mode now binds how work runs. It has its own row in the precedence table; `PROFILE.md`, posture and theme still only inform.
|
|
20
|
+
- Foreman triage is a count, not a judgement: a task is substantial when it changes 2 or more files, needs a new or changed test, needs about 5 reads before the first edit, or asks for a feature, a fix, a review, an audit or an investigation. When unsure, the session delegates.
|
|
21
|
+
- Pipelines: the parent session dispatches one background crew lead and goes back to the user. The crew lead picks the crew from the whole agent roster (the floor is only the minimum) and runs the relay. `pipeline` now has a per-host table; on a host where the crew lead cannot start subagents, it plays each role in turn.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- Web console: a git clone always gets the **Update** button, also when the npm release check gets no answer.
|
|
25
|
+
- Web console: a rebuild on a console running old code (after a manual `git pull`) now restarts it and reloads the page.
|
|
26
|
+
- Docs: the pipeline loop limit is 2 repair rounds, then one fresh developer, not "five rounds".
|
|
27
|
+
|
|
28
|
+
## [4.15.1] — 2026-10-23
|
|
29
|
+
|
|
30
|
+
Patch: two host fixes. The first npm release since 4.14.0, so it also carries 4.15.0's comms 4 doctrine.
|
|
31
|
+
|
|
32
|
+
### Upgrade notes
|
|
33
|
+
- Run `geneseed rebuild-all`; nothing changes on a machine until something re-emits.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- OpenCode 1.x: the guard and learn plugins no longer write `[geneseed-guard] …` / `[geneseed-learn] …` lines in red over the TUI input box. Their messages now go to OpenCode's own log (`client.app.log`, service `geneseed-guard` / `geneseed-learn`). OpenCode 2 and a refused log call still use stderr. The workflow plugin's one-line load banner stays on stderr.
|
|
37
|
+
- IBM Bob: the first reply now starts with the theme's readiness line. Both Bob carriers (global `rules/geneseed.md`, the project's `AGENTS.md` block) open with a short first-reply override; without it, Bob's own greeting won. A bare "hi" still gets Bob's fixed welcome. Run `geneseed rebuild-all` to pick it up.
|
|
38
|
+
|
|
39
|
+
## [4.15.0] — 2026-10-22
|
|
40
|
+
|
|
41
|
+
Minor: a new comms doctrine rule makes every generated agent write its prose in ASD-STE100 Simplified Technical English.
|
|
42
|
+
|
|
43
|
+
### Upgrade notes
|
|
44
|
+
- Run `geneseed rebuild-all` to get the new rule; nothing changes on a machine until something re-emits. To keep the old behaviour on one install, rebuild it with `--exclude-rules "comms 4"`.
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
- **Doctrine comms 4, Simplified Technical English.** Every generated agent writes the prose of its replies and reports in ASD-STE100: one topic per sentence (at most 20 words in an instruction, 25 in a description), imperative steps with one action each and the condition first, the active voice, no -ing forms, no dropped articles, and one word per meaning. Code, commands, paths, identifiers and quoted output stay as they are. STE is an English standard and the ontology imposes no language, so an answer in another language keeps that language and the same sentence rules; the theme keeps its voice. It is in the toggleable comms pack, not a law, per DESIGN.md Decision 7: `--exclude-rules "comms 4"` turns it off. The carrier ceilings rise by 1 003 characters (full) and 472 (lean). Run `geneseed rebuild-all` to get it.
|
|
48
|
+
|
|
11
49
|
## [4.14.0] — 2026-10-22
|
|
12
50
|
|
|
13
51
|
Minor: the React kit supports webpack projects as well as Vite.
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
[](themes/)
|
|
14
14
|
[](src/skills/)
|
|
15
15
|
[](src/agents/)
|
|
16
|
-
[](src/doctrines/)
|
|
17
17
|
[](adapters/opencode/plugins/)
|
|
18
18
|
[](#-supported-harnesses)
|
|
19
19
|
|
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: **4.
|
|
23
|
+
> string. Current label: **4.16.0**.
|
|
24
24
|
|
|
25
25
|
## Capabilities
|
|
26
26
|
|
|
@@ -153,7 +153,21 @@ const gateOff = (marker) => new RegExp(`^${marker}:[ \\t]*off[ \\t]*\\r?$`, "m")
|
|
|
153
153
|
const DELETION_GATE_OFF = gateOff("Deletion gate")
|
|
154
154
|
const SECRETS_GATE_OFF = gateOff("Secrets gate")
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
// OpenCode 1.x paints a plugin's stderr over its TUI input box (in red); its own log API
|
|
157
|
+
// writes to the log file instead. The V1 entry sets LOG_CLIENT; V2 and tests have no
|
|
158
|
+
// client and keep stderr, as does a refused log call, so no line is lost. Never throws:
|
|
159
|
+
// a log line must not break a tool call.
|
|
160
|
+
let LOG_CLIENT = null
|
|
161
|
+
function say(msg, level = "warn") {
|
|
162
|
+
try {
|
|
163
|
+
if (LOG_CLIENT?.app?.log) {
|
|
164
|
+
Promise.resolve(LOG_CLIENT.app.log({ body: { service: "geneseed-guard", level, message: msg } })).catch(() => console.error(`[geneseed-guard] ${msg}`))
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
} catch { /* fall through to stderr */ }
|
|
168
|
+
console.error(`[geneseed-guard] ${msg}`)
|
|
169
|
+
}
|
|
170
|
+
const log = (msg) => say(msg)
|
|
157
171
|
|
|
158
172
|
// ---- the gate ledger ------------------------------------------------------------
|
|
159
173
|
// The twin of `ledger()` in js/hosts/hooks.mjs: one JSON line per BLOCK into
|
|
@@ -1369,6 +1383,7 @@ async function recordUse(tool, args) {
|
|
|
1369
1383
|
}
|
|
1370
1384
|
|
|
1371
1385
|
export const GeneseedGuard = async (ctx) => {
|
|
1386
|
+
if (ctx?.client) LOG_CLIENT = ctx.client
|
|
1372
1387
|
// The session's working directory, the same resolution the context plugin uses.
|
|
1373
1388
|
const root = () => ctx?.worktree || ctx?.directory || process.cwd()
|
|
1374
1389
|
return {
|
|
@@ -38,6 +38,21 @@ import * as path from "node:path"
|
|
|
38
38
|
import { fileURLToPath } from "node:url"
|
|
39
39
|
import { homedir } from "node:os"
|
|
40
40
|
|
|
41
|
+
// OpenCode 1.x paints a plugin's stderr over its TUI input box (in red); its own log API
|
|
42
|
+
// writes to the log file instead. The V1 entry sets LOG_CLIENT; V2 and tests have no
|
|
43
|
+
// client and keep stderr, as does a refused log call, so no line is lost. Never throws:
|
|
44
|
+
// a log line must not break a tool call.
|
|
45
|
+
let LOG_CLIENT = null
|
|
46
|
+
function say(msg, level = "warn") {
|
|
47
|
+
try {
|
|
48
|
+
if (LOG_CLIENT?.app?.log) {
|
|
49
|
+
Promise.resolve(LOG_CLIENT.app.log({ body: { service: "geneseed-learn", level, message: msg } })).catch(() => console.error(`[geneseed-learn] ${msg}`))
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
} catch { /* fall through to stderr */ }
|
|
53
|
+
console.error(`[geneseed-learn] ${msg}`)
|
|
54
|
+
}
|
|
55
|
+
|
|
41
56
|
// The plugin's own directory. In the global install the layout is
|
|
42
57
|
// <cfg>/plugins/geneseed-learn.js beside <cfg>/memory, so the store can be found
|
|
43
58
|
// relative to this file — no manual `export GENESEED_HARNESS` needed.
|
|
@@ -471,17 +486,18 @@ async function storeDistil(output, { isChild, memDir, agentName, existing }) {
|
|
|
471
486
|
const lesson = output.trim().split("\n")[0].replace(/^[-*]\s*/, "").trim()
|
|
472
487
|
if (lesson.length < 10 || lesson.length > 300) return // junk / truncation guard
|
|
473
488
|
const f = await appendAgentLesson(memDir, agentName, lesson)
|
|
474
|
-
|
|
489
|
+
say(`agent lesson -> ${f}`, "info")
|
|
475
490
|
return
|
|
476
491
|
}
|
|
477
492
|
const res = await writeMemories(output, memDir, existing)
|
|
478
493
|
for (const [verb, slugs] of [["wrote", res.created], ["updated", res.updated], ["archived", res.archived]]) {
|
|
479
|
-
if (slugs.length)
|
|
494
|
+
if (slugs.length) say(`${verb} ${slugs.length} memory file(s): ${slugs.join(", ")}`, "info")
|
|
480
495
|
}
|
|
481
496
|
}
|
|
482
497
|
|
|
483
498
|
export const GeneseedLearn = async (ctx) => {
|
|
484
499
|
const { client } = ctx ?? {}
|
|
500
|
+
if (client) LOG_CLIENT = client
|
|
485
501
|
const ours = new Set() // throwaway distil sessions — never mine our own output
|
|
486
502
|
const timers = new Map() // sid -> debounce timer, re-armed on each idle
|
|
487
503
|
const inFlight = new Set() // sid currently being distilled (guards against overlap)
|
|
@@ -510,8 +526,8 @@ export const GeneseedLearn = async (ctx) => {
|
|
|
510
526
|
if (!memDir) {
|
|
511
527
|
if (!warnedNoDir) {
|
|
512
528
|
warnedNoDir = true
|
|
513
|
-
|
|
514
|
-
"
|
|
529
|
+
say(
|
|
530
|
+
"no memory dir — set $GENESEED_HARNESS or $GENESEED_MEMORY."
|
|
515
531
|
)
|
|
516
532
|
}
|
|
517
533
|
return
|
|
@@ -524,8 +540,8 @@ export const GeneseedLearn = async (ctx) => {
|
|
|
524
540
|
notes = notes.slice(-MAX_NOTES_CHARS)
|
|
525
541
|
model = model ?? envModel()
|
|
526
542
|
if (!model) {
|
|
527
|
-
|
|
528
|
-
"
|
|
543
|
+
say(
|
|
544
|
+
"could not determine a model — set $GENESEED_MODEL=provider/model."
|
|
529
545
|
)
|
|
530
546
|
return
|
|
531
547
|
}
|
|
@@ -540,7 +556,7 @@ export const GeneseedLearn = async (ctx) => {
|
|
|
540
556
|
const newId = session?.id ?? session?.data?.id
|
|
541
557
|
if (!newId) {
|
|
542
558
|
// Prompting with {id: undefined} would 404 silently on every idle.
|
|
543
|
-
|
|
559
|
+
say("session.create returned no id — skipping this distil.")
|
|
544
560
|
return
|
|
545
561
|
}
|
|
546
562
|
ours.add(newId)
|
|
@@ -558,7 +574,7 @@ export const GeneseedLearn = async (ctx) => {
|
|
|
558
574
|
await storeDistil(output, { isChild, memDir, agentName, existing })
|
|
559
575
|
} catch (err) {
|
|
560
576
|
// Never break the session over a memory write.
|
|
561
|
-
|
|
577
|
+
say(`skipped: ${err?.message ?? err}`)
|
|
562
578
|
} finally {
|
|
563
579
|
inFlight.delete(sid)
|
|
564
580
|
}
|
|
@@ -647,7 +663,7 @@ async function setupV2(ctx) {
|
|
|
647
663
|
if (!memDir) {
|
|
648
664
|
if (!warnedNoDir) {
|
|
649
665
|
warnedNoDir = true
|
|
650
|
-
|
|
666
|
+
say("no memory dir — set $GENESEED_HARNESS or $GENESEED_MEMORY.")
|
|
651
667
|
}
|
|
652
668
|
return
|
|
653
669
|
}
|
|
@@ -660,7 +676,7 @@ async function setupV2(ctx) {
|
|
|
660
676
|
model = env ? { providerID: env.providerID, id: env.modelID } : null
|
|
661
677
|
}
|
|
662
678
|
if (!model) {
|
|
663
|
-
|
|
679
|
+
say("could not determine a model — set $GENESEED_MODEL=provider/model.")
|
|
664
680
|
return
|
|
665
681
|
}
|
|
666
682
|
|
|
@@ -673,7 +689,7 @@ async function setupV2(ctx) {
|
|
|
673
689
|
})
|
|
674
690
|
await storeDistil(String(reply?.text ?? ""), { isChild, memDir, agentName, existing })
|
|
675
691
|
} catch (err) {
|
|
676
|
-
|
|
692
|
+
say(`skipped: ${err?.message ?? err}`)
|
|
677
693
|
} finally {
|
|
678
694
|
inFlight.delete(sid)
|
|
679
695
|
}
|
|
@@ -695,7 +711,7 @@ async function setupV2(ctx) {
|
|
|
695
711
|
if (prev) clearTimeout(prev)
|
|
696
712
|
timers.set(sid, setTimeout(() => { timers.delete(sid); runDistill(sid) }, DEBOUNCE_MS))
|
|
697
713
|
}
|
|
698
|
-
})().catch((err) =>
|
|
714
|
+
})().catch((err) => say(`event stream ended: ${err?.message ?? err}`))
|
|
699
715
|
return () => {
|
|
700
716
|
controller.abort()
|
|
701
717
|
for (const t of timers.values()) clearTimeout(t)
|
|
@@ -25,7 +25,21 @@ import * as path from "node:path"
|
|
|
25
25
|
import { fileURLToPath, pathToFileURL } from "node:url"
|
|
26
26
|
|
|
27
27
|
const DEBUG = !!process.env.GENESEED_DEBUG
|
|
28
|
-
function dlog(m) { if (DEBUG)
|
|
28
|
+
function dlog(m) { if (DEBUG) say(m, "debug") }
|
|
29
|
+
// OpenCode 1.x paints a plugin's stderr over its TUI input box (in red); its own log API
|
|
30
|
+
// writes to the log file instead. The V1 entry sets LOG_CLIENT; V2 and tests have no
|
|
31
|
+
// client and keep stderr, as does a refused log call, so no line is lost. Never throws:
|
|
32
|
+
// a log line must not break a tool call.
|
|
33
|
+
let LOG_CLIENT = null
|
|
34
|
+
function say(msg, level = "warn") {
|
|
35
|
+
try {
|
|
36
|
+
if (LOG_CLIENT?.app?.log) {
|
|
37
|
+
Promise.resolve(LOG_CLIENT.app.log({ body: { service: "geneseed-workflow", level, message: msg } })).catch(() => console.error(`[geneseed-workflow] ${msg}`))
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
} catch { /* fall through to stderr */ }
|
|
41
|
+
console.error(`[geneseed-workflow] ${msg}`)
|
|
42
|
+
}
|
|
29
43
|
|
|
30
44
|
const __dir = path.dirname(fileURLToPath(import.meta.url))
|
|
31
45
|
const WORKFLOWS_DIR = process.env.GENESEED_WORKFLOWS_DIR
|
|
@@ -184,7 +198,9 @@ const NAME_DESCRIPTION = "saved workflow name; omit to list available"
|
|
|
184
198
|
const ARGS_DESCRIPTION = "inputs passed to the workflow script as `args` (object, or a JSON object string)"
|
|
185
199
|
|
|
186
200
|
// One-line load banner (always on) so a restart visibly confirms the plugin loaded and the
|
|
187
|
-
// `workflow` tool is registered. tests/host_canary.mjs reads it as V1's proof of load
|
|
201
|
+
// `workflow` tool is registered. tests/host_canary.mjs reads it as V1's proof of load, so it
|
|
202
|
+
// stays on stderr, not `say`: `opencode debug config` never prints a `client.app.log` line. It
|
|
203
|
+
// runs once, before the TUI draws, so it does not land in the input box as `say`'s lines did.
|
|
188
204
|
async function loadBanner(warn = "") {
|
|
189
205
|
try {
|
|
190
206
|
const names = await listWorkflows()
|
|
@@ -194,6 +210,7 @@ async function loadBanner(warn = "") {
|
|
|
194
210
|
|
|
195
211
|
export const GeneseedWorkflow = async (ctx) => {
|
|
196
212
|
const { client, directory, worktree } = ctx
|
|
213
|
+
if (client) LOG_CLIENT = client
|
|
197
214
|
let toolHelper
|
|
198
215
|
try { ({ tool: toolHelper } = await import("@opencode-ai/plugin")) }
|
|
199
216
|
catch { dlog("@opencode-ai/plugin tool helper unavailable — registering a raw tool def") }
|
|
@@ -21,8 +21,10 @@ The agent works every task itself, turn by turn. Nothing is spawned; you talk to
|
|
|
21
21
|
|
|
22
22
|
The session sorts every request:
|
|
23
23
|
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
24
|
+
- **Substantial** — handed to a **pipeline** running in the background. The session tells you it is running and stays free to answer you. A task counts as substantial when any one of these holds: it changes two or more files, it needs a new or changed test, it needs about five reads or searches before the first edit, or it asks for a feature, a fix, a review, an audit or an investigation.
|
|
25
|
+
- **Trivial** — none of those holds: a question answered from what is already in context, a one-file tweak, a single command. Done directly, as in direct mode.
|
|
26
|
+
|
|
27
|
+
When the session is unsure, it delegates. The count exists because "substantial" left to judgement lost every time to the host's own default of doing small work in place.
|
|
26
28
|
|
|
27
29
|
Expect a substantial task to cost more (a crew runs instead of one agent) in exchange for a session that never blocks on a long job.
|
|
28
30
|
|
|
@@ -37,11 +39,11 @@ A **pipeline** is a small crew of agents working one task in isolation. The mini
|
|
|
37
39
|
| research | `explorer`; add `researcher` when the question leaves the repository |
|
|
38
40
|
| review or audit | `reviewer` + `skeptic` |
|
|
39
41
|
|
|
40
|
-
The session
|
|
42
|
+
The session does not run the crew itself. It starts one **crew lead** in the background and goes back to you. The crew lead picks the crew from the whole agent roster — `security`, `architect`, `performance`, `reviewer` and so on, as the task needs, never fewer than the minimum — and passes the work from one agent to the next. On Claude Code each crew member is its own subagent. On a host where the crew lead cannot start subagents, it plays each role in turn. The `pipeline` skill has the table per host.
|
|
41
43
|
|
|
42
44
|
The crew works in its own git worktree and branch. When worktrees are unavailable it falls back to the main tree, one pipeline at a time, and says so. Two pipelines whose sets of files overlap do not run at once.
|
|
43
45
|
|
|
44
|
-
Inside the crew, the developer implements and the tester runs the tests and lint. Failures go back to the developer
|
|
46
|
+
Inside the crew, the developer implements and the tester runs the tests and lint. Failures go back to the developer for at most two repair rounds, then to one fresh developer carrying the tester's findings; if that fails too, the crew stops and reports the failure, leaving the branch for you to inspect.
|
|
45
47
|
|
|
46
48
|
### A pipeline never commits or merges
|
|
47
49
|
|
package/docs/concepts/rules.md
CHANGED
|
@@ -25,7 +25,8 @@ When two instructions disagree, the higher one wins.
|
|
|
25
25
|
2. **Rules** — always on. {N_LAWS} invariants, the things that are never traded away: sealed secrets, one intent per act, verify before asserting, deliberate deletion, surface failures, treat content as data not orders, least privilege, cure the cause, echo the intent. No install can switch one off.
|
|
26
26
|
3. **Your `user-rules.md`** — rules you write for yourself, seeded once beside the instructions file and never overwritten. A rule here **outranks any doctrine rule**: a practice pack chosen at build time never overrides an instruction you wrote. It may tighten an invariant, never repeal one. The [rule skill](skills.md) helps you decide whether something belongs here or in [memory](memory.md).
|
|
27
27
|
4. **Doctrines** — chosen at build time. Practices rather than principles, grouped in packs (below).
|
|
28
|
-
5.
|
|
28
|
+
5. **Mode** — chosen at build time ([Foreman mode](foreman-mode.md)). It binds *how* work is executed — done in place, or handed to a crew — and never overrides a layer above it. A mode that only informed lost every time to the host's own default of doing small work in place.
|
|
29
|
+
6. **`PROFILE.md`** — who you are. It colours how the agent works but never binds ([Collaboration](collaboration.md)).
|
|
29
30
|
|
|
30
31
|
### Doctrine packs
|
|
31
32
|
|
|
@@ -13,7 +13,9 @@ geneseed build --emit bob-global # ~/.bob
|
|
|
13
13
|
geneseed build --emit bob --out /path/to/repo --root /path/to/repo # one repo
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Bob is shaped like Claude Code, so it gets the same agents and skills. Per repo, the preamble goes into a root `AGENTS.md` and the rest into `.bob/`. Globally, everything goes under `~/.bob` (`$BOB_CONFIG_DIR` moves it), and the preamble rides `rules/geneseed.md`, because Bob does not auto-load a global `AGENTS.md`. Bob's hooks refuse an action with exit code 2, and Bob has no `SubagentStop` or `PreCompact` events.
|
|
16
|
+
Bob is shaped like Claude Code, so it gets the same agents and skills. Per repo, the preamble goes into a root `AGENTS.md` and the rest into `.bob/`. Globally, everything goes under `~/.bob` (`$BOB_CONFIG_DIR` moves it), and the preamble rides `rules/geneseed.md`, because Bob does not auto-load a global `AGENTS.md`. Bob's hooks refuse an action with exit code 2, and Bob has no `SubagentStop` or `PreCompact` events.
|
|
17
|
+
|
|
18
|
+
Both scopes open Bob's carrier with a short first-reply override, so the theme's readiness line (for example "🧬 Gene-seed implanted…") starts the first reply. Without it, Bob's own greeting outranked the instruction lower in the file. A bare greeting such as "hi" still gets Bob's fixed welcome; the line appears on the first real request. That much was checked by hand in the Bob IDE (2026-10); the rest of the Bob support has not been verified against a live install.
|
|
17
19
|
|
|
18
20
|
---
|
|
19
21
|
|
package/docs/guides/upgrade.md
CHANGED
|
@@ -34,6 +34,8 @@ geneseed upgrade imperial # the same, switching the theme
|
|
|
34
34
|
|
|
35
35
|
A dirty working tree, or a folder that is not a git clone, is reported rather than force-updated. `update` and `sync-self` are aliases of `upgrade`. `bootstrap` does the same and then opens the setup wizard (`--no-setup` skips the wizard).
|
|
36
36
|
|
|
37
|
+
In the web console, the **Update** button on the Overview runs `upgrade`. On a clone it is always there, also when the release check cannot reach npm, because `main` moves between releases. When the update is done, the console restarts and the page reloads. If you ran `git pull` yourself, the Overview shows a banner that the console runs old code. Click **Rebuild** on an install that is behind: the console restarts after the rebuild and reloads the page. If no install is behind, click **Restart** in the banner.
|
|
38
|
+
|
|
37
39
|
## What an upgrade keeps
|
|
38
40
|
|
|
39
41
|
A rebuild only replaces files the install's manifest says Geneseed owns. It leaves these alone:
|
package/harness.config.json
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
"_comment": "version is a human-readable release label only; the canonical identity of an installed harness is the source fingerprint stamped in .geneseed-version (see `harness version`), not this string.",
|
|
3
3
|
"theme": "neutral",
|
|
4
4
|
"product": "Geneseed",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.16.0"
|
|
6
6
|
}
|
package/js/build/emit-claude.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from './emit-common.mjs';
|
|
22
22
|
import { kitsBody, renderAll, renderFile } from './render.mjs';
|
|
23
23
|
import {
|
|
24
|
-
BOB_RULES_STUB, ensureContextStub, ensureExcludesStub, ensureNotebookIndex,
|
|
24
|
+
BOB_RULES_STUB, bobFirstReply, ensureContextStub, ensureExcludesStub, ensureNotebookIndex,
|
|
25
25
|
ensureProfileStub,
|
|
26
26
|
ensureRulesStub, ensureWikiStub, WIKI_FILE,
|
|
27
27
|
} from './stubs.mjs';
|
|
@@ -100,7 +100,7 @@ export function emitClaudeRender(cfg, job) {
|
|
|
100
100
|
if (isBob && agentText !== null) {
|
|
101
101
|
const rulesMd = path.join(cfgDir, 'rules', 'geneseed.md');
|
|
102
102
|
claimAndWrite(cfgDir, rulesMd, 'rules/geneseed.md', scope === 'project' ? BOB_RULES_STUB
|
|
103
|
-
: stripCapabilityLinks(prefixedAgentText('../') || agentText), owned);
|
|
103
|
+
: bobFirstReply(theme.LOADED_SIGIL) + stripCapabilityLinks(prefixedAgentText('../') || agentText), owned);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
ensureAgentOverridesStub(cfg, cfgDir);
|
|
@@ -179,6 +179,8 @@ export function emitClaudeRender(cfg, job) {
|
|
|
179
179
|
let claudeMdText = null;
|
|
180
180
|
if (agentText !== null && !(isBob && scope === 'global')) {
|
|
181
181
|
claudeMdText = stripCapabilityLinks(prefixedAgentText(lawsPrefix) || agentText);
|
|
182
|
+
// Bob only: its greeting persona outranks the template's sigil paragraph — see `bobFirstReply`.
|
|
183
|
+
if (isBob) claudeMdText = bobFirstReply(theme.LOADED_SIGIL) + claudeMdText;
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
// WIRE — see `claudeWire` below. One child per emit, so the two halves are two
|
package/js/build/stubs.mjs
CHANGED
|
@@ -139,6 +139,19 @@ file (\`~/.bob/rules/geneseed.md\`) so the global preamble does not stack on top
|
|
|
139
139
|
the project's own. Follow the root \`AGENTS.md\`.
|
|
140
140
|
`;
|
|
141
141
|
|
|
142
|
+
/**
|
|
143
|
+
* The paragraph a Bob emit puts FIRST in what Bob auto-loads (global `rules/geneseed.md`,
|
|
144
|
+
* the project's `AGENTS.md` block). Tried live in the Bob IDE (2026-10-23): with only the
|
|
145
|
+
* template's sigil paragraph — below the banner — the first reply to a real request carried
|
|
146
|
+
* no sigil; with this paragraph on top it did. Bob's own greeting persona outranks a rule
|
|
147
|
+
* that does not say it overrides it. A bare "hi" still gets Bob's fixed welcome either way.
|
|
148
|
+
*/
|
|
149
|
+
export function bobFirstReply(sigil) {
|
|
150
|
+
return '**First-reply override — highest priority.** Your very first reply in every '
|
|
151
|
+
+ 'conversation MUST begin with this exact line, before anything else and instead of any '
|
|
152
|
+
+ `default greeting or self-introduction:\n\n> ${sigil}\n\n`;
|
|
153
|
+
}
|
|
154
|
+
|
|
142
155
|
/** `_build_render.PROFILE_FILE`. */
|
|
143
156
|
const PROFILE_FILE = 'PROFILE.md';
|
|
144
157
|
|
package/js/inspect/cost.mjs
CHANGED
|
@@ -35,7 +35,7 @@ import { isFile, readText } from '../lib/fs.mjs';
|
|
|
35
35
|
* console draws it on the cost chart, so it lives here, once, where both read it. Raise it by
|
|
36
36
|
* exactly the measured overrun and say why in that test's docblock, which keeps its history.
|
|
37
37
|
*/
|
|
38
|
-
export const CARRIER_CEILING = { full:
|
|
38
|
+
export const CARRIER_CEILING = { full: 63_299, lean: 39_661 };
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* A frontmatter value as the host shows it. The emit writes descriptions JSON-escaped
|
package/js/web/api.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* bind paths to these functions are `routes.mjs`. This file imports none of its route siblings,
|
|
9
9
|
* which is what keeps the old `api ↔ actions` and `api ↔ activity` cycles from coming back.
|
|
10
10
|
*/
|
|
11
|
-
import { readdirSync, statSync } from 'node:fs';
|
|
11
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
|
|
14
14
|
import { CONFIG, CREDIT_RE, ROOT, SRC, THEMES, discoverNames, loadKits, makeCfg, upstreamOf } from '../build/source.mjs';
|
|
@@ -500,7 +500,11 @@ export function apiLoopsActive() {
|
|
|
500
500
|
}
|
|
501
501
|
|
|
502
502
|
/** Is a newer release on npm than the source this console serves? See update-check.mjs. */
|
|
503
|
-
|
|
503
|
+
// `git`: a clone updates by `git pull` whatever npm says — main moves between releases, and a
|
|
504
|
+
// work proxy can leave the release check unanswered — so the console always offers Update there.
|
|
505
|
+
// `.git` is a file in a worktree, a folder in a clone; existence covers both.
|
|
506
|
+
export const apiUpdate = (state) => ({ ...updateStatus(state, releaseLabel()),
|
|
507
|
+
git: existsSync(path.join(ROOT, '.git')) });
|
|
504
508
|
|
|
505
509
|
/** The source's release label, or null when it cannot be read. */
|
|
506
510
|
function releaseLabel() {
|
package/js/web/daemon.mjs
CHANGED
|
@@ -357,7 +357,7 @@ export async function statusDaemon(theme = null, port = null) {
|
|
|
357
357
|
* `rebuild-all` when the running one is stale. EXCEPT inside a web job (`GENESEED_WEB_JOB`,
|
|
358
358
|
* set by `js/web/jobs.mjs`): restarting here would kill the process tracking that very job,
|
|
359
359
|
* leaving the console stuck on 'running'; the server restarts itself after an update job, and
|
|
360
|
-
*
|
|
360
|
+
* after a rebuild-all job when it is stale (`js/web/handler.mjs`). `onlyIfStale` is `rebuild-all`'s: after an `upgrade`
|
|
361
361
|
* (which bounces, then runs `rebuild-all`) the console is current, and bouncing it again
|
|
362
362
|
* would drop the open tab for nothing. Never throws — no rebuild fails over a console.
|
|
363
363
|
*/
|
|
@@ -368,7 +368,7 @@ export async function bounceDaemonIfRunning(theme, log, { onlyIfStale = false }
|
|
|
368
368
|
}
|
|
369
369
|
if (process.env.GENESEED_WEB_JOB) {
|
|
370
370
|
log(onlyIfStale
|
|
371
|
-
? '[geneseed] the web console is running old code —
|
|
371
|
+
? '[geneseed] the web console is running old code — it restarts itself after this job.'
|
|
372
372
|
: '[geneseed] web daemon will restart itself after this job to load the new code.');
|
|
373
373
|
return;
|
|
374
374
|
}
|
package/js/web/handler.mjs
CHANGED
|
@@ -300,6 +300,17 @@ export function makeHandler(state, jm, token, dist, holder = null) {
|
|
|
300
300
|
if (jid === null) return sendJson(res, { error: 'busy' }, 409, ae);
|
|
301
301
|
return sendJson(res, { job_id: jid }, 202, ae);
|
|
302
302
|
}
|
|
303
|
+
// A rebuild-all on a STALE server (a manual `git pull` under it) restarts it afterwards, as
|
|
304
|
+
// an update does: otherwise the rebuild lands and the page keeps serving the old code.
|
|
305
|
+
// `restarts` tells the console to wait for the new server and reload.
|
|
306
|
+
if (action === 'build-all' && state.daemonStale?.()) {
|
|
307
|
+
const jid = jm.start(action, actionCommands(action), (rc) => {
|
|
308
|
+
state.refresh();
|
|
309
|
+
if (rc === 0) requestRestart(state.theme);
|
|
310
|
+
});
|
|
311
|
+
if (jid === null) return sendJson(res, { error: 'busy' }, 409, ae);
|
|
312
|
+
return sendJson(res, { job_id: jid, restarts: true }, 202, ae);
|
|
313
|
+
}
|
|
303
314
|
// Build resolves its argv off the install (`buildActionCmds`); the other actions take no
|
|
304
315
|
// axes and self-resolve the deployed theme downstream.
|
|
305
316
|
const cmds = action === 'build' ? buildActionCmds(state, body) : actionCommands(action);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "Every field here is argued and gated in tests/unit/package_manifest.test.mjs. `files`, `bin` and `engines` each have a test that fails when reality drifts from this file; `version` mirrors harness.config.json and is gated too. Read that docblock before editing this one.",
|
|
3
3
|
"name": "geneseed",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.16.0",
|
|
5
5
|
"description": "A portable, theme-able harness you implant once and use everywhere to grow a disciplined AI coding agent.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ai",
|
package/src/AGENT.md.tmpl
CHANGED
|
@@ -59,7 +59,8 @@ never move.
|
|
|
59
59
|
| 2 | `user-rules.md` (§1) | may tighten a {{LAW}}, never repeal one |
|
|
60
60
|
| 3 | The {{ONTOLOGY}} | never against a user rule |
|
|
61
61
|
| 4 | The active {{DOCTRINES}} (§2) | may tighten, never repeal |
|
|
62
|
-
| 5 |
|
|
62
|
+
| 5 | The mode | binds how work runs |
|
|
63
|
+
| 6 | `PROFILE.md`, posture, theme | inform only; never bind |
|
|
63
64
|
|
|
64
65
|
---
|
|
65
66
|
|
package/src/doctrines/comms.md
CHANGED
|
@@ -70,3 +70,24 @@ interfaces first. Genuine forks only, never naming or formatting; trivial work o
|
|
|
70
70
|
Where {{LAW:echo-the-intent}} checks the goal before, this exposes the choices made during —
|
|
71
71
|
never a call it says to ask about.
|
|
72
72
|
<!-- LEAN:end -->
|
|
73
|
+
|
|
74
|
+
### {{DOCTRINE:simplified-technical-english}} Simplified Technical English
|
|
75
|
+
<!-- LEAN:begin -->
|
|
76
|
+
Write every prose reply and report in ASD-STE100 Simplified Technical English, the
|
|
77
|
+
controlled language of aerospace maintenance: a tired reader, or one reading a second
|
|
78
|
+
language, gets one meaning on the first read. Code, commands, paths, identifiers and quoted
|
|
79
|
+
output stay as they are. One topic per sentence: at most 20 words in an instruction, 25 in
|
|
80
|
+
a description, six sentences per paragraph. Write an instruction in the imperative, one
|
|
81
|
+
action per sentence, condition first (`If the test fails, read the log.`); a warning comes
|
|
82
|
+
before its step. Use the active voice and simple tenses, no -ing forms, no dropped
|
|
83
|
+
articles. Use one word per meaning, every time: `use`, not `utilize`; `stop`, not
|
|
84
|
+
`terminate`. Project names are technical names and stay. Keep noun clusters to three words.
|
|
85
|
+
In another language, answer in it ({{ONT_CONDUCT}}) and keep these sentence rules; the
|
|
86
|
+
theme keeps its voice, and this rule governs the sentences that carry the content.
|
|
87
|
+
<!-- LEAN:else -->
|
|
88
|
+
Write every prose reply and report in ASD-STE100 Simplified Technical English; code,
|
|
89
|
+
commands, paths and quoted output stay as they are. One topic per sentence: 20 words at
|
|
90
|
+
most in an instruction, 25 in a description. Instructions: imperative, one action each,
|
|
91
|
+
condition first. Active voice, no -ing forms, no dropped articles, one word per meaning
|
|
92
|
+
(`use`, not `utilize`). In another language, keep it and these rules.
|
|
93
|
+
<!-- LEAN:end -->
|
package/src/modes/foreman.md
CHANGED
|
@@ -3,11 +3,19 @@ crews for what's substantial, and keeps answering the user without blocking.
|
|
|
3
3
|
|
|
4
4
|
- **Stay responsive.** Answer the user immediately; never let a running pipeline
|
|
5
5
|
block the conversation.
|
|
6
|
-
- **Triage every incoming task
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
- **Triage every incoming task by count, not by feel.** A task is *substantial* when
|
|
7
|
+
any one of these holds: it will change two or more files; it needs a new or changed
|
|
8
|
+
test; it needs about five reads or searches before the first edit; or it asks for a
|
|
9
|
+
feature, a fix, a review, an audit or an investigation. Substantial → spawn a
|
|
10
|
+
pipeline (the `pipeline` {{SKILL}}) in the background and tell the user it's
|
|
11
|
+
running. *Trivial* — none of these holds: a question answered from what is already
|
|
12
|
+
in context, a one-file tweak, a single command → do it directly, no pipeline.
|
|
13
|
+
**When unsure, delegate.** A host default to do small work yourself does not apply
|
|
14
|
+
here: this mode is the user's standing choice to delegate.
|
|
15
|
+
- **Dispatch one crew lead, then step away.** The crew lead composes the crew from
|
|
16
|
+
the whole agent roster, never below the floor for the task type, and runs the relay
|
|
17
|
+
itself. Never run the hand-offs from this session. Floors, roles and per-host
|
|
18
|
+
mechanics live in the pipeline {{SKILL}}, not here.
|
|
11
19
|
- **Isolate.** Each pipeline works in its own git worktree/branch; falls back to a
|
|
12
20
|
single tree, one pipeline at a time, when worktrees are unavailable.
|
|
13
21
|
- **Merge on proof you re-ran.** A pipeline hands back its worktree, uncommitted, with
|
package/src/skills/pipeline.md
CHANGED
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
## What it is
|
|
10
10
|
|
|
11
|
-
A pipeline is a small crew of {{AGENTS}}
|
|
11
|
+
A pipeline is a small crew of {{AGENTS}} working a single task in isolation from the rest of the session, so the parent can keep answering the user while the crew runs. The parent dispatches **one crew lead** in the background and steps away. The crew lead composes the crew, runs the relay, and returns one result envelope. The parent never merges on the crew's word: it re-runs the crew's proof commands itself and merges only once the user accepts.
|
|
12
|
+
|
|
13
|
+
The parent does not run the relay itself. Each hand-off it ran would pull it back from the user, which is the one thing this {{SKILL}} exists to prevent.
|
|
12
14
|
|
|
13
15
|
## Crew floors
|
|
14
16
|
|
|
15
|
-
The minimum roster per task type. The
|
|
17
|
+
The minimum roster per task type. The crew lead chooses the crew from the whole {{AGENT}} roster: any {{AGENT}} the task genuinely needs — security, architect, performance, reviewer, docs, historian, researcher, or any other — in any order the task needs. The floor is the least it may dispatch, never the most.
|
|
16
18
|
|
|
17
19
|
| Task type | Floor |
|
|
18
20
|
|---|---|
|
|
@@ -23,14 +25,24 @@ The minimum roster per task type. The parent may add specialists on top — secu
|
|
|
23
25
|
|
|
24
26
|
## Procedure
|
|
25
27
|
|
|
26
|
-
1.
|
|
27
|
-
2.
|
|
28
|
-
3.
|
|
29
|
-
4.
|
|
30
|
-
5. **
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
1. Take the triage as given. Under foreman mode the task reached you because it met the mode's count; invoked directly, the user's request is the trigger. Do not weigh it again and fall back to doing the work yourself.
|
|
29
|
+
2. **Dispatch the crew lead** in the background, using the host's general-purpose {{AGENT}}. Before dispatch, record BASE and set the crew's **write set**; check it against every other pipeline in flight ({{DOCTRINE:one-writer-per-file}}) — two crews whose sets overlap do not run at once. The brief carries: the task as the user stated it, the task type, BASE, the write set, the proof commands, a pointer to this {{SKILL}}, and the rule that it never commits, pushes or merges. Tell the user the pipeline is running, then go back to the user. Do not wait on the crew lead.
|
|
30
|
+
3. **The crew lead composes the crew** from the floor above and adds any {{AGENT}} the task needs. It dispatches each crew member as its own subagent, each handed what the previous one produced. Where the crew lead holds no dispatch tool (see *Hosts* below), it plays each role in turn itself, with the same hand-offs and the same envelope. Steps 4 to 7 are the crew lead's, except where they name the parent.
|
|
31
|
+
4. Isolate the crew's work in its own git worktree/branch. Where worktrees are unavailable, fall back to a single tree, one pipeline at a time, and say so. The crew writes only inside its write set; shared append files (registries, index tables, lockfiles, generated bundles) are the parent's to apply, never a crew's.
|
|
32
|
+
5. **The dev↔tester loop:** the developer implements, the tester validates — runs the tests and lint, produces raw logs. On failure, the findings go back to the developer — two repair rounds at most, then one fresh developer (a higher model tier where the host offers one) carrying the tester's findings ({{DOCTRINE:bound-the-loop}}); if that fails too, stop, do not merge, and report the failure with the branch left in place for inspection. A conflict or ambiguity between the plan, the findings and the code is yours to decide: decide it, record `Ruling: <choice> — <why> — <cost if wrong>` in the worklog, and continue. Stop only for what {{LAW:echo-the-intent}}, {{LAW:deletion-is-deliberate}} or {{DOCTRINE:consent-before-push}} names; every ruling goes in the final report ({{DOCTRINE:declare-the-judgment-calls}}). When the plan names a `Spec:`, the analyst reads it once; the spec outranks the plan.
|
|
33
|
+
6. **Pick the shape by host** (table below). Where the host offers a workflow tool the {{SKILL}} may use without a fresh opt-in, a saved script may stand in for the crew lead: analyst → developer → (tester ⇄ developer)×≤2 → fresh developer → proof ([workflow {{SKILL}}](workflow.md)). Everywhere else the crew lead runs the same stages, with the same hand-offs and the same delivery contract.
|
|
34
|
+
7. On success, hand the worktree back to the parent uncommitted, with the mechanical proof (test + lint output, exit codes) and the list of files it changed in the result envelope, rebased on the current base so a conflict with a sibling resolves on the crew's branch. The pipeline never commits, pushes, or merges ({{DOCTRINE:consent-before-push}}): the parent records BASE before dispatch and, before re-running the proof, confirms the branch descends from it and the diff is non-empty (an empty diff is a failure; never `HEAD~1`); it re-runs the proof commands itself, shows the user the diff and the output, and commits and merges once the user accepts.
|
|
35
|
+
|
|
36
|
+
## Hosts
|
|
37
|
+
|
|
38
|
+
| Host | Crew lead runs in the background | Crew lead can dispatch the crew | Shape |
|
|
39
|
+
|---|---|---|---|
|
|
40
|
+
| Claude Code | yes — a subagent started from the main conversation runs in the background by default | yes — subagents nest up to three layers by default (`CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH`), and a background subagent keeps the `Agent` tool | crew lead → crew subagents. Not the `Workflow` tool: it refuses without the user's opt-in for that task, and the mode is not one |
|
|
41
|
+
| OpenCode | the `task` tool starts a child session; whether the parent's turn waits is unverified | unverified — `permission.task` decides which subagents an agent may start | crew lead → crew subagents where it holds `task`; else roles in turn. A saved `workflow` script (no opt-in needed) may stand in for the crew lead |
|
|
42
|
+
| OpenClaude | expected as Claude Code (it reads Claude's subagent dialect); unverified | unverified | as Claude Code; else roles in turn |
|
|
43
|
+
| Bob | unverified — Bob reads Claude's subagent dialect | unverified | crew lead plays the roles in turn |
|
|
44
|
+
|
|
45
|
+
Check, do not assume: before the first pipeline of a session on a host marked unverified, look at the tools you and the crew lead actually hold ({{DOCTRINE:tool-discovery}}). When a fact above proves wrong, report it through this {{SKILL}}'s self-improvement loop.
|
|
34
46
|
|
|
35
47
|
## Delivery contract
|
|
36
48
|
|
package/themes/_TEMPLATE.json
CHANGED
|
@@ -122,6 +122,7 @@
|
|
|
122
122
|
"DOC_CODES_THAT_PERSIST": "<themed title for the comms rule Codes That Persist>",
|
|
123
123
|
"DOC_STRUCTURE_BESIDE_PROSE": "<themed title for the comms rule Structure Beside Prose>",
|
|
124
124
|
"DOC_DECLARE_THE_JUDGMENT_CALLS": "<themed title for the comms rule Declare the Judgment Calls>",
|
|
125
|
+
"DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "<themed title for the comms rule Simplified Technical English>",
|
|
125
126
|
"INTRO_DOC": "<themed intro: this file is the entry point an AI agent reads in this repo>",
|
|
126
127
|
"INTRO_AGENTS": "<themed intro to the Agents section — delegate focused work to a specialist>",
|
|
127
128
|
"INTRO_SKILLS": "<themed intro to the Skills section — run the written procedure before improvising>",
|
package/themes/biker.json
CHANGED
|
@@ -122,6 +122,7 @@
|
|
|
122
122
|
"DOC_CODES_THAT_PERSIST": "Tag Every Part on the Bench · Codes That Persist",
|
|
123
123
|
"DOC_STRUCTURE_BESIDE_PROSE": "Draw the Route Only If It Turns · Structure Beside Prose",
|
|
124
124
|
"DOC_DECLARE_THE_JUDGMENT_CALLS": "Own Up to Every Back Road Taken · Declare the Judgment Calls",
|
|
125
|
+
"DOC_SIMPLIFIED_TECHNICAL_ENGLISH": "Short Words, Clear Signals · Simplified Technical English",
|
|
125
126
|
"INTRO_DOC": "This file is the route briefing a coding rider reads when it joins this repository.",
|
|
126
127
|
"INTRO_AGENTS": "Send the right rider for focused work instead of riding the whole run yourself; each has one job and reports exactly what it did.",
|
|
127
128
|
"INTRO_SKILLS": "A Skill is a move you've drilled for a recurring task. Run the matching move before you wing it.",
|