@sublang/playbook 18.0.0 → 18.1.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/README.md +4 -3
- package/docs/configuration.md +22 -12
- package/package.json +9 -2
- package/reference/sdlc/code.md +8 -5
- package/reference/sdlc/code.playbook/bin/launch-config.js +65 -35
- package/reference/sdlc/code.playbook/bin/playbook.js +2 -2
- package/reference/sdlc/code.playbook/code.fsm.js +10 -7
- package/reference/sdlc/code.playbook/code.fsm.ts +10 -7
- package/reference/sdlc/code.playbook/code.gears.md +10 -7
- package/reference/sdlc/code.playbook/code.playbook.js +1 -1
- package/reference/sdlc/code.playbook/code.playbook.ts +1 -1
- package/reference/sdlc/code.playbook/seed-config.d.ts +12 -0
- package/reference/sdlc/code.playbook/seed-config.js +22 -0
package/README.md
CHANGED
|
@@ -32,8 +32,9 @@ Run `playbook` for an interactive tmux UI powered by [cligent](https://github.co
|
|
|
32
32
|
Out of the box, Playbook includes **CODE** for implementation, **REVIEW** for commit-based review and fixes, **DECIDE** for independently proposed and reviewed specification decisions, **DEV** for repository-aware planning of a development request, **BRANCH** for checking out a new branch for a GitHub issue, **PR** for publishing, checking, and merging that branch through a pull request, and **INSPECT** for explaining supplied material or a running interface without changing the repository.
|
|
33
33
|
CODE and DECIDE call REVIEW as a nested playbook; DEV calls CODE, or DECIDE and then CODE, and wraps them in BRANCH and PR when the request names a GitHub issue or asks for a pull request.
|
|
34
34
|
|
|
35
|
-
The shared starter config selects
|
|
36
|
-
|
|
35
|
+
The shared starter config selects the Captain's and players' adapter from
|
|
36
|
+
locally visible credentials: Claude first, then Codex, and where both are
|
|
37
|
+
configured only `dev.reviewer` seeds on Codex. If neither is
|
|
37
38
|
configured, it seeds Claude and prints a notice. CODE, REVIEW, DECIDE, BRANCH, and PR bind
|
|
38
39
|
their local roles explicitly to `dev.coder` and `dev.reviewer`, so nested and later
|
|
39
40
|
engagements share a conversation only where their bindings name the same
|
|
@@ -51,7 +52,7 @@ If an SDK is missing or older than cligent supports, Playbook prints the pinned
|
|
|
51
52
|
Prerequisites:
|
|
52
53
|
|
|
53
54
|
- Node.js >= 20.6.0
|
|
54
|
-
- Credentials for the configured adapter: authenticated [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) or `ANTHROPIC_API_KEY` for Claude; authenticated [Codex CLI](https://github.com/openai/codex) or `OPENAI_API_KEY` for Codex
|
|
55
|
+
- Credentials for the configured adapter: authenticated [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) or `ANTHROPIC_API_KEY` / `CLAUDE_CODE_OAUTH_TOKEN` for Claude; authenticated [Codex CLI](https://github.com/openai/codex) or `OPENAI_API_KEY` / `CODEX_ACCESS_TOKEN` for Codex
|
|
55
56
|
- [GitHub CLI](https://cli.github.com/) (`gh`) authenticated for the repository's GitHub remote, for `/branch`, `/pr`, and `/dev` requests that name an issue or ask for a pull request
|
|
56
57
|
|
|
57
58
|
Interactive `playbook` additionally needs tmux 3.3 or newer and [`glow`](https://github.com/charmbracelet/glow#installation) on `PATH`; headless `playbook run` does not.
|
package/docs/configuration.md
CHANGED
|
@@ -64,16 +64,23 @@ one; see [Modules supplied at launch](#modules-supplied-at-launch). Every
|
|
|
64
64
|
manifest role must be present exactly once. The launcher injects the rest — you do not write host wiring by
|
|
65
65
|
hand.
|
|
66
66
|
|
|
67
|
-
The launcher seeds
|
|
68
|
-
|
|
69
|
-
for Claude or GPT-6 Sol for Codex, with Captain and Coder at `high` effort
|
|
70
|
-
and Reviewer and Analyst at `xhigh`. If neither adapter is configured, it
|
|
71
|
-
seeds Claude and prints a notice. Credentials do not prove SDK availability
|
|
72
|
-
or remaining quota; launch still checks the configured adapter. Existing
|
|
73
|
-
configs are left unchanged.
|
|
67
|
+
The launcher seeds the Captain and all stable players from locally visible
|
|
68
|
+
credentials (an API key in the environment, or `~/.claude` / `~/.codex`):
|
|
74
69
|
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
| Credentials | Seeded agents |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| Claude only, or neither | every agent on Claude Opus 5.5 (with a notice when neither is configured) |
|
|
73
|
+
| Codex only | every agent on GPT-6 Sol |
|
|
74
|
+
| both | `dev.reviewer` on GPT-6 Sol, every other agent on Claude Opus 5.5 |
|
|
75
|
+
|
|
76
|
+
Captain, Coder, and Inspector run at `high` effort and Reviewer and Analyst at
|
|
77
|
+
`xhigh`. Credentials do not prove SDK availability or remaining quota; launch
|
|
78
|
+
still checks the configured adapter. Existing configs are left unchanged.
|
|
79
|
+
Embedding hosts seed the same agent settings through
|
|
80
|
+
`seedConfigText({ env, homeDir })` from `@sublang/playbook/seed-config`.
|
|
81
|
+
|
|
82
|
+
The Claude default is shown below. Each Codex agent also carries
|
|
83
|
+
`permissions.writablePaths: ['.git']`:
|
|
77
84
|
|
|
78
85
|
```yaml
|
|
79
86
|
captain:
|
|
@@ -273,9 +280,12 @@ It may not finish the specialist’s task or invent a decision; discarding work
|
|
|
273
280
|
Preparation has a 150-second limit.
|
|
274
281
|
|
|
275
282
|
Adapter readiness is intentionally light: `claude` is ready with local
|
|
276
|
-
Claude Code auth
|
|
277
|
-
|
|
278
|
-
|
|
283
|
+
Claude Code auth, `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or
|
|
284
|
+
`CLAUDE_CODE_OAUTH_TOKEN`; `codex` with local Codex CLI auth,
|
|
285
|
+
`OPENAI_API_KEY`, `CODEX_API_KEY`, or `CODEX_ACCESS_TOKEN`. Only presence
|
|
286
|
+
counts; no token is checked. A known adapter that is not ready blocks the
|
|
287
|
+
launch and prints the help text. The same check picks the starter config's
|
|
288
|
+
lineup.
|
|
279
289
|
|
|
280
290
|
## Skills across agents
|
|
281
291
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sublang/playbook",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Composable XState v5 playbook runtime with compiled Captain, CODE, REVIEW, DECIDE, DEV, BRANCH, PR, and INSPECT workflows driven by GEARS specs.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -83,6 +83,8 @@
|
|
|
83
83
|
"reference/sdlc/code.playbook/host-capabilities.d.ts",
|
|
84
84
|
"reference/sdlc/code.playbook/machine-identity.js",
|
|
85
85
|
"reference/sdlc/code.playbook/machine-identity.d.ts",
|
|
86
|
+
"reference/sdlc/code.playbook/seed-config.js",
|
|
87
|
+
"reference/sdlc/code.playbook/seed-config.d.ts",
|
|
86
88
|
"reference/sdlc/code.playbook/code.gears.md",
|
|
87
89
|
"reference/sdlc/code.playbook/playbook.config.template.yaml",
|
|
88
90
|
"reference/sdlc/code.playbook/bin/playbook.js",
|
|
@@ -212,6 +214,10 @@
|
|
|
212
214
|
"types": "./reference/sdlc/code.playbook/machine-identity.d.ts",
|
|
213
215
|
"default": "./reference/sdlc/code.playbook/machine-identity.js"
|
|
214
216
|
},
|
|
217
|
+
"./seed-config": {
|
|
218
|
+
"types": "./reference/sdlc/code.playbook/seed-config.d.ts",
|
|
219
|
+
"default": "./reference/sdlc/code.playbook/seed-config.js"
|
|
220
|
+
},
|
|
215
221
|
"./slc/*": "./slc/*",
|
|
216
222
|
"./review/playbook": {
|
|
217
223
|
"types": "./reference/sdlc/review.playbook/review.playbook.d.ts",
|
|
@@ -276,6 +282,7 @@
|
|
|
276
282
|
"test:acceptance": "node scripts/run-live-acceptance.mjs",
|
|
277
283
|
"smoke:release": "node scripts/release-smoke.mjs",
|
|
278
284
|
"check:links": "node scripts/check-links.mjs",
|
|
285
|
+
"prepush": "bash scripts/prepush.sh",
|
|
279
286
|
"playbook": "node reference/sdlc/code.playbook/bin/playbook.js"
|
|
280
287
|
},
|
|
281
288
|
"publishConfig": {
|
|
@@ -283,7 +290,7 @@
|
|
|
283
290
|
"provenance": true
|
|
284
291
|
},
|
|
285
292
|
"dependencies": {
|
|
286
|
-
"@sublang/cligent": "^0.36.
|
|
293
|
+
"@sublang/cligent": "^0.36.1",
|
|
287
294
|
"@sublang/spex": "^3.0.0",
|
|
288
295
|
"p-queue": "^9.3.1",
|
|
289
296
|
"xstate": "^5.19.4",
|
package/reference/sdlc/code.md
CHANGED
|
@@ -10,14 +10,14 @@ Roles:
|
|
|
10
10
|
|
|
11
11
|
The caller supplies a coding request together with any relevant context.
|
|
12
12
|
|
|
13
|
-
A new
|
|
13
|
+
A new change intent, including standalone documentation or status maintenance that does not implement an identified unfinished IR task, follows either one direct implementation phase or an IR sequence consisting of a new-IR phase followed by one phase for each IR task.
|
|
14
14
|
An existing IR follows one IR-task phase for each remaining unfinished task, starting with its next unfinished task.
|
|
15
15
|
Each completed phase ends with one new Coder commit (owned by `code`).
|
|
16
16
|
After each `code`-owned commit, Captain shall call the `review` playbook and wait until it passes with no unsettled findings.
|
|
17
17
|
Playbook `review` owns every review round and every review-fix commit.
|
|
18
18
|
Do not rewrite any existing commit.
|
|
19
19
|
|
|
20
|
-
When `review` passes a direct
|
|
20
|
+
When `review` passes a direct phase, `code` is complete.
|
|
21
21
|
When `review` passes a new IR or a nonfinal IR-task phase, Captain shall continue with the next unfinished IR-task phase.
|
|
22
22
|
When `review` passes the final IR-task phase, `code` is complete.
|
|
23
23
|
When `review` returns an authored abort or failure, or a terminal result that does not establish that the supplied scope was evaluated with no unsettled findings, `code` shall start no further phase and shall report the failure and the last `code`-owned commit to its caller.
|
|
@@ -29,15 +29,18 @@ At the start of the first phase, Captain shall relay to Coder the complete calle
|
|
|
29
29
|
> Run results: <run-results>
|
|
30
30
|
|
|
31
31
|
```markdown
|
|
32
|
-
First determine whether the
|
|
32
|
+
First determine whether the request starts a new change intent or continues an existing IR with unfinished work.
|
|
33
33
|
If the request may continue an existing IR but does not identify it unambiguously, ask Boss before changing files.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Treat standalone documentation or status maintenance that does not implement an identified unfinished IR task as a new change intent, even when it refers to an IR with no unfinished task.
|
|
36
|
+
Do not invent or reopen an IR task, claim unperformed work complete, or infer an owner decision not supplied by the caller.
|
|
37
|
+
|
|
38
|
+
For a new change intent, assess whether it can be completed well in one commit.
|
|
36
39
|
If it can, implement and test it, update the affected specs, and ensure @specs/map.md remains accurate.
|
|
37
40
|
If it cannot, decompose it into tasks sized to exactly one commit each, add a new IR under @specs/intents, and do not implement any IR task in this phase.
|
|
38
41
|
Plan affected spec updates before, with, or after their corresponding code changes, either as standalone IR tasks or as explicit work within related tasks.
|
|
39
42
|
|
|
40
|
-
For an existing IR, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.
|
|
43
|
+
For an existing IR with unfinished work, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.
|
|
41
44
|
Do not implement a later task in this phase.
|
|
42
45
|
Mark the IR's progress and deliverables when relevant.
|
|
43
46
|
After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.
|
|
@@ -1438,23 +1438,36 @@ export function adaptersFromComposedConfig(config) {
|
|
|
1438
1438
|
return [...adapters];
|
|
1439
1439
|
}
|
|
1440
1440
|
|
|
1441
|
+
// DR-097: each agent's documented environment forms, any one of which, or the
|
|
1442
|
+
// agent's sign-in folder, counts it as ready. No value is read.
|
|
1443
|
+
const CREDENTIAL_FORMS = {
|
|
1444
|
+
claude: {
|
|
1445
|
+
names: ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN"],
|
|
1446
|
+
folder: ".claude",
|
|
1447
|
+
},
|
|
1448
|
+
codex: {
|
|
1449
|
+
names: ["OPENAI_API_KEY", "CODEX_API_KEY", "CODEX_ACCESS_TOKEN"],
|
|
1450
|
+
folder: ".codex",
|
|
1451
|
+
},
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1441
1454
|
export function checkReadiness(adapters, env = process.env, home = homedir()) {
|
|
1442
1455
|
const failingAdapters = [];
|
|
1443
1456
|
const unknownAdapters = [];
|
|
1444
1457
|
for (const adapter of adapters) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1458
|
+
const forms = Object.hasOwn(CREDENTIAL_FORMS, adapter)
|
|
1459
|
+
? CREDENTIAL_FORMS[adapter]
|
|
1460
|
+
: undefined;
|
|
1461
|
+
if (!forms) {
|
|
1462
|
+
unknownAdapters.push(adapter);
|
|
1449
1463
|
continue;
|
|
1450
1464
|
}
|
|
1451
|
-
if (
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1465
|
+
if (
|
|
1466
|
+
!forms.names.some((name) => env[name]) &&
|
|
1467
|
+
!existsSync(join(home, forms.folder))
|
|
1468
|
+
) {
|
|
1469
|
+
failingAdapters.push(adapter);
|
|
1456
1470
|
}
|
|
1457
|
-
unknownAdapters.push(adapter);
|
|
1458
1471
|
}
|
|
1459
1472
|
return { failingAdapters, unknownAdapters };
|
|
1460
1473
|
}
|
|
@@ -1651,9 +1664,14 @@ const SEED_LINEUP = Object.freeze([
|
|
|
1651
1664
|
Object.freeze({ adapter: "codex", model: "gpt-6-sol" }),
|
|
1652
1665
|
]);
|
|
1653
1666
|
|
|
1667
|
+
// DR-096: where both adapters are ready, this player alone takes the
|
|
1668
|
+
// second row, so review comes from a different provider than the work.
|
|
1669
|
+
const SECOND_ADAPTER_PLAYER = "dev.reviewer";
|
|
1670
|
+
|
|
1654
1671
|
/**
|
|
1655
1672
|
* PBCLI-11: the lineup a seed would write here, and whether anything
|
|
1656
|
-
* actually probed ready.
|
|
1673
|
+
* actually probed ready. `reviewer` is the row `dev.reviewer` takes;
|
|
1674
|
+
* every other agent takes `lineup`.
|
|
1657
1675
|
*
|
|
1658
1676
|
* Only the credential check participates. The SDK probe constructs an
|
|
1659
1677
|
* adapter and awaits `isAvailable()`, which reads whatever happens to
|
|
@@ -1666,7 +1684,12 @@ export function selectSeedLineup(env = process.env, home = homedir()) {
|
|
|
1666
1684
|
(entry) =>
|
|
1667
1685
|
checkReadiness([entry.adapter], env, home).failingAdapters.length === 0,
|
|
1668
1686
|
);
|
|
1669
|
-
|
|
1687
|
+
const lineup = ready[0] ?? SEED_LINEUP[0];
|
|
1688
|
+
return {
|
|
1689
|
+
lineup,
|
|
1690
|
+
reviewer: ready.length > 1 ? ready[1] : lineup,
|
|
1691
|
+
anyReady: ready.length > 0,
|
|
1692
|
+
};
|
|
1670
1693
|
}
|
|
1671
1694
|
|
|
1672
1695
|
/**
|
|
@@ -1675,9 +1698,7 @@ export function selectSeedLineup(env = process.env, home = homedir()) {
|
|
|
1675
1698
|
* byte copy it always was. A different adapter rewrites the agent
|
|
1676
1699
|
* scalars in place, which the Document API carries comments through.
|
|
1677
1700
|
*/
|
|
1678
|
-
function seedTextForLineup(
|
|
1679
|
-
const template = readFileSync(templatePath, "utf8");
|
|
1680
|
-
if (lineup.adapter === SEED_LINEUP[0].adapter) return template;
|
|
1701
|
+
function seedTextForLineup(template, { lineup, reviewer }) {
|
|
1681
1702
|
const doc = parseYamlDocument(template);
|
|
1682
1703
|
const players = doc.getIn(["players"]);
|
|
1683
1704
|
const agentPaths = [
|
|
@@ -1687,18 +1708,35 @@ function seedTextForLineup(templatePath, lineup) {
|
|
|
1687
1708
|
String(pair.key),
|
|
1688
1709
|
]),
|
|
1689
1710
|
];
|
|
1711
|
+
let changed = false;
|
|
1690
1712
|
for (const path of agentPaths) {
|
|
1691
|
-
|
|
1692
|
-
|
|
1713
|
+
const entry = path[1] === SECOND_ADAPTER_PLAYER ? reviewer : lineup;
|
|
1714
|
+
if (entry.adapter === SEED_LINEUP[0].adapter) continue;
|
|
1715
|
+
changed = true;
|
|
1716
|
+
doc.setIn([...path, "adapter"], entry.adapter);
|
|
1717
|
+
doc.setIn([...path, "model"], entry.model);
|
|
1693
1718
|
// A codex agent writes git metadata under its sandbox; claude's
|
|
1694
1719
|
// auto mode needs no grant, so the key exists for one adapter only.
|
|
1695
|
-
|
|
1696
|
-
doc.setIn([...path, "permissions", "writablePaths"], [".git"]);
|
|
1697
|
-
} else {
|
|
1698
|
-
doc.deleteIn([...path, "permissions", "writablePaths"]);
|
|
1699
|
-
}
|
|
1720
|
+
doc.setIn([...path, "permissions", "writablePaths"], [".git"]);
|
|
1700
1721
|
}
|
|
1701
|
-
return String(doc);
|
|
1722
|
+
return changed ? String(doc) : template;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* PBCLI-100: the starter text a seed writes under this environment and
|
|
1727
|
+
* home. The public `seed-config` facade and the launcher both call it,
|
|
1728
|
+
* so every host seeding through it selects the same agent settings.
|
|
1729
|
+
*/
|
|
1730
|
+
export function seedConfigText(
|
|
1731
|
+
templatePath,
|
|
1732
|
+
env = process.env,
|
|
1733
|
+
homeDir = env.HOME ?? homedir(),
|
|
1734
|
+
) {
|
|
1735
|
+
const selection = selectSeedLineup(env, homeDir);
|
|
1736
|
+
return {
|
|
1737
|
+
text: seedTextForLineup(readFileSync(templatePath, "utf8"), selection),
|
|
1738
|
+
anyReady: selection.anyReady,
|
|
1739
|
+
};
|
|
1702
1740
|
}
|
|
1703
1741
|
|
|
1704
1742
|
function seedUserConfigIfMissing(
|
|
@@ -1710,21 +1748,13 @@ function seedUserConfigIfMissing(
|
|
|
1710
1748
|
) {
|
|
1711
1749
|
if (existsSync(userConfigPath)) return;
|
|
1712
1750
|
mkdirSync(dirname(userConfigPath), { recursive: true });
|
|
1713
|
-
const {
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
copyFileSync(templatePath, userConfigPath, constants.COPYFILE_EXCL);
|
|
1717
|
-
} else {
|
|
1718
|
-
// Still one exclusive create, so two seeding calls cannot disagree.
|
|
1719
|
-
writeFileSync(userConfigPath, seedTextForLineup(templatePath, lineup), {
|
|
1720
|
-
encoding: "utf8",
|
|
1721
|
-
flag: "wx",
|
|
1722
|
-
});
|
|
1723
|
-
}
|
|
1751
|
+
const { text, anyReady } = seedConfigText(templatePath, env, homeDir);
|
|
1752
|
+
// One exclusive create, so two seeding calls cannot disagree.
|
|
1753
|
+
writeFileSync(userConfigPath, text, { encoding: "utf8", flag: "wx" });
|
|
1724
1754
|
onNotice(`playbook: created config at ${userConfigPath}\n`);
|
|
1725
1755
|
if (!anyReady) {
|
|
1726
1756
|
onNotice(
|
|
1727
|
-
`playbook: no adapter probed ready; seeded ${
|
|
1757
|
+
`playbook: no adapter probed ready; seeded ${SEED_LINEUP[0].adapter}\n`,
|
|
1728
1758
|
);
|
|
1729
1759
|
}
|
|
1730
1760
|
}
|
|
@@ -993,9 +993,9 @@ function helpText({
|
|
|
993
993
|
"",
|
|
994
994
|
"Adapter setup:",
|
|
995
995
|
" claude: npm install -g @anthropic-ai/claude-agent-sdk, then run",
|
|
996
|
-
" Claude Code once or set ANTHROPIC_API_KEY.",
|
|
996
|
+
" Claude Code once or set ANTHROPIC_API_KEY (or CLAUDE_CODE_OAUTH_TOKEN).",
|
|
997
997
|
" codex: npm install -g @openai/codex-sdk, then run Codex CLI once",
|
|
998
|
-
" or set OPENAI_API_KEY.",
|
|
998
|
+
" or set OPENAI_API_KEY (or CODEX_API_KEY, CODEX_ACCESS_TOKEN).",
|
|
999
999
|
" Each SDK is an optional peer dependency, so you install only the",
|
|
1000
1000
|
" vendors your config actually names.",
|
|
1001
1001
|
"",
|
|
@@ -14,15 +14,18 @@ const REVIEW_PLAYBOOK_ID = 'review';
|
|
|
14
14
|
// Prompts and result contracts (verbatim from code.gears.md)
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
16
|
const FIRST_PHASE_PROMPT = [
|
|
17
|
-
'First determine whether the
|
|
17
|
+
'First determine whether the request starts a new change intent or continues an existing IR with unfinished work.',
|
|
18
18
|
'If the request may continue an existing IR but does not identify it unambiguously, ask Boss before changing files.',
|
|
19
19
|
'',
|
|
20
|
-
'
|
|
20
|
+
'Treat standalone documentation or status maintenance that does not implement an identified unfinished IR task as a new change intent, even when it refers to an IR with no unfinished task.',
|
|
21
|
+
'Do not invent or reopen an IR task, claim unperformed work complete, or infer an owner decision not supplied by the caller.',
|
|
22
|
+
'',
|
|
23
|
+
'For a new change intent, assess whether it can be completed well in one commit.',
|
|
21
24
|
'If it can, implement and test it, update the affected specs, and ensure @specs/map.md remains accurate.',
|
|
22
25
|
'If it cannot, decompose it into tasks sized to exactly one commit each, add a new IR under @specs/intents, and do not implement any IR task in this phase.',
|
|
23
26
|
'Plan affected spec updates before, with, or after their corresponding code changes, either as standalone IR tasks or as explicit work within related tasks.',
|
|
24
27
|
'',
|
|
25
|
-
'For an existing IR, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
|
|
28
|
+
'For an existing IR with unfinished work, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
|
|
26
29
|
'Do not implement a later task in this phase.',
|
|
27
30
|
"Mark the IR's progress and deliverables when relevant.",
|
|
28
31
|
"After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.",
|
|
@@ -86,8 +89,8 @@ const REVIEW_IR_TASK_PHASE_TEMPLATE = [
|
|
|
86
89
|
].join('\n');
|
|
87
90
|
const NEEDS_BOSS_REPLY_DESCRIPTION = "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.";
|
|
88
91
|
const FIRST_PHASE_RESULT = {
|
|
89
|
-
directCommit: "Coder completed the new
|
|
90
|
-
irCommit: "Coder decomposed the new
|
|
92
|
+
directCommit: "Coder completed the new change intent, including standalone documentation or status maintenance that does not implement an identified unfinished IR task (even when it refers to an IR with no unfinished task), as one direct implementation phase and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.",
|
|
93
|
+
irCommit: "Coder decomposed the new change intent into a new IR, implemented no IR task, and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, and `irNumber` identifying the created IR.",
|
|
91
94
|
moreTasks: "Coder continued an existing IR, implemented exactly its next unfinished task, made its one new commit, and at least one IR task remains unfinished; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.",
|
|
92
95
|
finalTask: "Coder continued an existing IR, implemented its final unfinished task, and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.",
|
|
93
96
|
needsBossReply: NEEDS_BOSS_REPLY_DESCRIPTION,
|
|
@@ -99,8 +102,8 @@ const IR_TASK_PHASE_RESULT = {
|
|
|
99
102
|
};
|
|
100
103
|
const STATE_DESCRIPTIONS = {
|
|
101
104
|
ready: 'Waiting for a coding request.',
|
|
102
|
-
firstPhase: 'Coder runs the first
|
|
103
|
-
reviewNewIntentPhase: 'REVIEW examines the commit of the direct
|
|
105
|
+
firstPhase: 'Coder runs the first phase: a direct change, a new IR, or an existing IR\'s next unfinished task.',
|
|
106
|
+
reviewNewIntentPhase: 'REVIEW examines the commit of the direct or new-IR phase.',
|
|
104
107
|
irTaskPhase: "Coder implements the identified IR's next unfinished task.",
|
|
105
108
|
reviewIrTaskPhase: 'REVIEW examines the commit of the IR-task phase.',
|
|
106
109
|
awaitBossReply: "Waiting for Boss to answer the acting agent's question.",
|
|
@@ -251,15 +251,18 @@ export type CodeEvent =
|
|
|
251
251
|
// ---------------------------------------------------------------------------
|
|
252
252
|
|
|
253
253
|
const FIRST_PHASE_PROMPT = [
|
|
254
|
-
'First determine whether the
|
|
254
|
+
'First determine whether the request starts a new change intent or continues an existing IR with unfinished work.',
|
|
255
255
|
'If the request may continue an existing IR but does not identify it unambiguously, ask Boss before changing files.',
|
|
256
256
|
'',
|
|
257
|
-
'
|
|
257
|
+
'Treat standalone documentation or status maintenance that does not implement an identified unfinished IR task as a new change intent, even when it refers to an IR with no unfinished task.',
|
|
258
|
+
'Do not invent or reopen an IR task, claim unperformed work complete, or infer an owner decision not supplied by the caller.',
|
|
259
|
+
'',
|
|
260
|
+
'For a new change intent, assess whether it can be completed well in one commit.',
|
|
258
261
|
'If it can, implement and test it, update the affected specs, and ensure @specs/map.md remains accurate.',
|
|
259
262
|
'If it cannot, decompose it into tasks sized to exactly one commit each, add a new IR under @specs/intents, and do not implement any IR task in this phase.',
|
|
260
263
|
'Plan affected spec updates before, with, or after their corresponding code changes, either as standalone IR tasks or as explicit work within related tasks.',
|
|
261
264
|
'',
|
|
262
|
-
'For an existing IR, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
|
|
265
|
+
'For an existing IR with unfinished work, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
|
|
263
266
|
'Do not implement a later task in this phase.',
|
|
264
267
|
"Mark the IR's progress and deliverables when relevant.",
|
|
265
268
|
"After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.",
|
|
@@ -330,9 +333,9 @@ const NEEDS_BOSS_REPLY_DESCRIPTION =
|
|
|
330
333
|
|
|
331
334
|
const FIRST_PHASE_RESULT = {
|
|
332
335
|
directCommit:
|
|
333
|
-
"Coder completed the new
|
|
336
|
+
"Coder completed the new change intent, including standalone documentation or status maintenance that does not implement an identified unfinished IR task (even when it refers to an IR with no unfinished task), as one direct implementation phase and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.",
|
|
334
337
|
irCommit:
|
|
335
|
-
"Coder decomposed the new
|
|
338
|
+
"Coder decomposed the new change intent into a new IR, implemented no IR task, and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, and `irNumber` identifying the created IR.",
|
|
336
339
|
moreTasks:
|
|
337
340
|
"Coder continued an existing IR, implemented exactly its next unfinished task, made its one new commit, and at least one IR task remains unfinished; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.",
|
|
338
341
|
finalTask:
|
|
@@ -351,9 +354,9 @@ const IR_TASK_PHASE_RESULT = {
|
|
|
351
354
|
const STATE_DESCRIPTIONS = {
|
|
352
355
|
ready: 'Waiting for a coding request.',
|
|
353
356
|
firstPhase:
|
|
354
|
-
'Coder runs the first
|
|
357
|
+
'Coder runs the first phase: a direct change, a new IR, or an existing IR\'s next unfinished task.',
|
|
355
358
|
reviewNewIntentPhase:
|
|
356
|
-
'REVIEW examines the commit of the direct
|
|
359
|
+
'REVIEW examines the commit of the direct or new-IR phase.',
|
|
357
360
|
irTaskPhase: "Coder implements the identified IR's next unfinished task.",
|
|
358
361
|
reviewIrTaskPhase: 'REVIEW examines the commit of the IR-task phase.',
|
|
359
362
|
awaitBossReply: "Waiting for Boss to answer the acting agent's question.",
|
|
@@ -17,15 +17,18 @@ When the request may continue an existing IR that it does not identify unambiguo
|
|
|
17
17
|
|
|
18
18
|
When the first coding phase begins for the caller's coding request and any relevant context, Captain shall prompt Coder:
|
|
19
19
|
|
|
20
|
-
> First determine whether the
|
|
20
|
+
> First determine whether the request starts a new change intent or continues an existing IR with unfinished work.
|
|
21
21
|
> If the request may continue an existing IR but does not identify it unambiguously, ask Boss before changing files.
|
|
22
22
|
>
|
|
23
|
-
>
|
|
23
|
+
> Treat standalone documentation or status maintenance that does not implement an identified unfinished IR task as a new change intent, even when it refers to an IR with no unfinished task.
|
|
24
|
+
> Do not invent or reopen an IR task, claim unperformed work complete, or infer an owner decision not supplied by the caller.
|
|
25
|
+
>
|
|
26
|
+
> For a new change intent, assess whether it can be completed well in one commit.
|
|
24
27
|
> If it can, implement and test it, update the affected specs, and ensure @specs/map.md remains accurate.
|
|
25
28
|
> If it cannot, decompose it into tasks sized to exactly one commit each, add a new IR under @specs/intents, and do not implement any IR task in this phase.
|
|
26
29
|
> Plan affected spec updates before, with, or after their corresponding code changes, either as standalone IR tasks or as explicit work within related tasks.
|
|
27
30
|
>
|
|
28
|
-
> For an existing IR, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.
|
|
31
|
+
> For an existing IR with unfinished work, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.
|
|
29
32
|
> Do not implement a later task in this phase.
|
|
30
33
|
> Mark the IR's progress and deliverables when relevant.
|
|
31
34
|
> After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.
|
|
@@ -49,8 +52,8 @@ When the first coding phase begins for the caller's coding request and any relev
|
|
|
49
52
|
> > Run results: <run-results>
|
|
50
53
|
|
|
51
54
|
Results:
|
|
52
|
-
- `directCommit`: Coder completed the new
|
|
53
|
-
- `irCommit`: Coder decomposed the new
|
|
55
|
+
- `directCommit`: Coder completed the new change intent, including standalone documentation or status maintenance that does not implement an identified unfinished IR task (even when it refers to an IR with no unfinished task), as one direct implementation phase and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.
|
|
56
|
+
- `irCommit`: Coder decomposed the new change intent into a new IR, implemented no IR task, and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, and `irNumber` identifying the created IR.
|
|
54
57
|
- `moreTasks`: Coder continued an existing IR, implemented exactly its next unfinished task, made its one new commit, and at least one IR task remains unfinished; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.
|
|
55
58
|
- `finalTask`: Coder continued an existing IR, implemented its final unfinished task, and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.
|
|
56
59
|
|
|
@@ -58,7 +61,7 @@ Results:
|
|
|
58
61
|
|
|
59
62
|
Captain waits until `review` passes with no unsettled findings; playbook `review` owns every review round and every review-fix commit, and no existing commit is rewritten.
|
|
60
63
|
|
|
61
|
-
When a direct
|
|
64
|
+
When a direct or new-IR phase ends with its one new `code`-owned commit, Captain shall call playbook `review`:
|
|
62
65
|
|
|
63
66
|
> > Original intent: <caller-input>
|
|
64
67
|
> > Review scope: the commit <code-commit> from this coding phase and its resulting repository state.
|
|
@@ -69,7 +72,7 @@ On each successful nested REVIEW, Captain shall freeze the previous phase review
|
|
|
69
72
|
CODE scope commit and evaluated revision remain distinct because REVIEW may create fix commits.
|
|
70
73
|
No caller option, player prose or repository status text supplies this record.
|
|
71
74
|
The record survives permitted restoration and the current task's Boss-answer continuation, and a fresh run or explicit Boss interruption clears it.
|
|
72
|
-
When `review` passes a direct
|
|
75
|
+
When `review` passes a direct phase, `code` is complete and returns to its caller the exact last `code`-owned commit, the exact final evaluated repository revision, and the fact that every phase's review passed with no unsettled findings.
|
|
73
76
|
When `review` passes a new-IR phase, Captain continues with the next unfinished IR-task phase.
|
|
74
77
|
When `review` returns an authored abort or failure, or a terminal result that does not establish that the supplied scope was evaluated with no unsettled findings, `code` starts no further phase and reports the failure and the last `code`-owned commit to its caller.
|
|
75
78
|
When the nested `review` call fails outside that authored result contract, `code` parks as failed and retains the control-plane error instead of reporting an authored review outcome.
|
|
@@ -86,7 +86,7 @@ const runtimeSpec = {
|
|
|
86
86
|
"roleStates": {
|
|
87
87
|
"firstPhase": {
|
|
88
88
|
"role": "coder",
|
|
89
|
-
"label": "Coder runs the first
|
|
89
|
+
"label": "Coder runs the first phase: a direct change, a new IR, or an existing IR's next unfinished task."
|
|
90
90
|
},
|
|
91
91
|
"irTaskPhase": {
|
|
92
92
|
"role": "coder",
|
|
@@ -123,7 +123,7 @@ const runtimeSpec = {
|
|
|
123
123
|
"roleStates": {
|
|
124
124
|
"firstPhase": {
|
|
125
125
|
"role": "coder",
|
|
126
|
-
"label": "Coder runs the first
|
|
126
|
+
"label": "Coder runs the first phase: a direct change, a new IR, or an existing IR's next unfinished task."
|
|
127
127
|
},
|
|
128
128
|
"irTaskPhase": {
|
|
129
129
|
"role": "coder",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
|
|
4
|
+
export interface SeedConfigOptions {
|
|
5
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
6
|
+
readonly homeDir?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SeedConfigText {
|
|
9
|
+
readonly text: string;
|
|
10
|
+
readonly anyReady: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function seedConfigText(options?: SeedConfigOptions): SeedConfigText;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
|
|
4
|
+
// DR-096: the public `@sublang/playbook/seed-config` facade. The text is
|
|
5
|
+
// the launcher's own seed (playbook-cli-11); this module only fixes the
|
|
6
|
+
// bundled template and narrows the accepted options (playbook-cli-100).
|
|
7
|
+
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { seedConfigText as seedText } from './bin/launch-config.js';
|
|
10
|
+
|
|
11
|
+
const TEMPLATE_PATH = fileURLToPath(
|
|
12
|
+
new URL('./playbook.config.template.yaml', import.meta.url),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export function seedConfigText(options = {}) {
|
|
16
|
+
const env = options.env ?? process.env;
|
|
17
|
+
return seedText(
|
|
18
|
+
TEMPLATE_PATH,
|
|
19
|
+
env,
|
|
20
|
+
...(options.homeDir !== undefined ? [options.homeDir] : []),
|
|
21
|
+
);
|
|
22
|
+
}
|