@afokapu/atdd-bun 0.10.5 → 0.10.7
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 +9 -5
- package/conventions/delivery/delivery.board.convention.yaml +1 -1
- package/conventions/delivery/delivery.config-schema.convention.yaml +5 -5
- package/conventions/delivery/delivery.operating-model.convention.yaml +7 -1
- package/conventions/tester.htmx/tester.htmx.journey-no-acceptance-marker.convention.yaml +1 -1
- package/detectors/htmx_e2e_detector/checks/_e2e.mjs +4 -1
- package/integrity.json +9 -9
- package/package.json +1 -1
- package/src/board.ts +17 -5
- package/src/delivery.ts +5 -5
package/README.md
CHANGED
|
@@ -171,6 +171,10 @@ From 0.10.5 only coordinators and drivers use the board: a writer or reviewer ta
|
|
|
171
171
|
launch prompt and returns its answer as output, which the driver posts. `atdd-bun chat wait` takes several
|
|
172
172
|
topics and `--skip heartbeat`; reviewers answer in JSON.
|
|
173
173
|
|
|
174
|
+
From 0.10.7 only a browser spec (a `*.e2e.*` file, or a Playwright file bound by `// Train:`/`// Journey:` or a
|
|
175
|
+
`test:train:`/`test:journey:` URN) is a journey spec. A `bun:test` file may carry the `Acceptance:` line
|
|
176
|
+
traceability needs, including a test proving `acc:train:…` and the journey test `atdd-bun.topology.e2e-location` requires.
|
|
177
|
+
|
|
174
178
|
Every key is optional; these are the defaults:
|
|
175
179
|
|
|
176
180
|
```yaml
|
|
@@ -181,11 +185,11 @@ delivery:
|
|
|
181
185
|
# board: { url: http://127.0.0.1:2586 } # opt-in: agents talk through a local board; absent, there is none
|
|
182
186
|
independence: different-model # a reviewer's model wrote none of the work it reviews; or fresh-process
|
|
183
187
|
stages: # models in preference order: the first, then recorded fallbacks
|
|
184
|
-
plan: { writer: [codex, claude-opus], reviewer: [glm, claude-opus, codex] }
|
|
185
|
-
red: { writer: [glm, claude-sonnet, claude-opus, codex] }
|
|
186
|
-
green: { writer: [glm, claude-sonnet, claude-opus, codex] }
|
|
187
|
-
refactor: { writer: [glm, claude-sonnet, claude-opus, codex] }
|
|
188
|
-
final: { reviewer: [codex, glm, claude-opus] }
|
|
188
|
+
plan: { writer: [codex, claude-opus, kimi], reviewer: [glm, claude-opus, codex, kimi] }
|
|
189
|
+
red: { writer: [glm, claude-sonnet, deepseek-flash, claude-opus, codex, kimi] }
|
|
190
|
+
green: { writer: [glm, claude-sonnet, deepseek-flash, claude-opus, codex, kimi] }
|
|
191
|
+
refactor: { writer: [glm, claude-sonnet, deepseek-flash, claude-opus, codex, kimi] }
|
|
192
|
+
final: { reviewer: [codex, glm, claude-opus, kimi] }
|
|
189
193
|
fallback: { after_failures: 3, within_minutes: 10, when_exhausted: block } # or wait
|
|
190
194
|
commands: {} # per model: { author: "...", review: "..." } overriding delivery.operating-model's defaults
|
|
191
195
|
```
|
|
@@ -27,7 +27,7 @@ terms:
|
|
|
27
27
|
atdd-bun chat post writes the header: from, to, participants and conversation always; kind (task, result, review-request, verdict, rebuttal, status, blocked, heartbeat), stage, reply-to, repo, branch, worktree, goal, base and sha when they apply. The body carries the whole instruction or answer, however long: the thread is the history. A heartbeat carries nothing else; anything that needs an answer goes as blocked, never inside a heartbeat.
|
|
28
28
|
- term_id: commands
|
|
29
29
|
text: >-
|
|
30
|
-
Post with the body on standard input; read what is on a topic; wait for the next message addressed to you on one or several topics (comma-separated), skipping kinds you do not act on (--skip heartbeat), bounded by --timeout so a tool call ends (exit 2: nothing yet
|
|
30
|
+
Post with the body on standard input; read what is on a topic; wait for the next message addressed to you on one or several topics (comma-separated), skipping kinds you do not act on (--skip heartbeat), bounded by --timeout so a tool call ends (exit 2: nothing yet). wait remembers where each identity stopped on each set of topics (~/.atdd-board/cursors), so re-arming a watcher is always the same command; --since still overrides it; and, for a human, atdd-bun chat alone opens the board: every topic on the left, the selected conversation on the right, live.
|
|
31
31
|
values:
|
|
32
32
|
post: atdd-bun chat post <topic> --to <agent> --kind <kind> [--reply-to <id>] < message.md
|
|
33
33
|
read: atdd-bun chat read <topic> [--mine]
|
|
@@ -21,11 +21,11 @@ content:
|
|
|
21
21
|
root: docs/delivery/tranches
|
|
22
22
|
independence: different-model
|
|
23
23
|
stages:
|
|
24
|
-
plan: { writer: [codex, claude-opus], reviewer: [glm, claude-opus, codex] }
|
|
25
|
-
red: { writer: [glm, claude-sonnet, claude-opus, codex] }
|
|
26
|
-
green: { writer: [glm, claude-sonnet, claude-opus, codex] }
|
|
27
|
-
refactor: { writer: [glm, claude-sonnet, claude-opus, codex] }
|
|
28
|
-
final: { reviewer: [codex, glm, claude-opus] }
|
|
24
|
+
plan: { writer: [codex, claude-opus, kimi], reviewer: [glm, claude-opus, codex, kimi] }
|
|
25
|
+
red: { writer: [glm, claude-sonnet, deepseek-flash, claude-opus, codex, kimi] }
|
|
26
|
+
green: { writer: [glm, claude-sonnet, deepseek-flash, claude-opus, codex, kimi] }
|
|
27
|
+
refactor: { writer: [glm, claude-sonnet, deepseek-flash, claude-opus, codex, kimi] }
|
|
28
|
+
final: { reviewer: [codex, glm, claude-opus, kimi] }
|
|
29
29
|
|
|
30
30
|
independence defaults to different-model. A stage may name a writer, a reviewer or both. The 0.9
|
|
31
31
|
names plan_review, test_review, code_review and final_review are still read, as plan, red, refactor
|
|
@@ -29,7 +29,7 @@ terms:
|
|
|
29
29
|
One line each, for the coordinator to wait on: PROGRAM_EVENT <tranche> <STAGE <stage> <sha> | WORKER_START <role> <model> <sha> | WORKER_END <role> <model> <verdict> | FALLBACK <role> <from>→<to> <reason> | PR_OPENED <url> | MERGED <sha> | BLOCKED <reason> | HEARTBEAT>.
|
|
30
30
|
- term_id: commands
|
|
31
31
|
text: >-
|
|
32
|
-
The default headless command per model, overridden per model under delivery.commands.<model>.author (the writer's command) and .review. {prompt} and {worktree} are substituted. Claude runs take project settings only, so a user's own allowances cannot widen a reviewer. glm
|
|
32
|
+
The default headless command per model, overridden per model under delivery.commands.<model>.author (the writer's command) and .review. {prompt} and {worktree} are substituted. Claude runs take project settings only, so a user's own allowances cannot widen a reviewer. glm, kimi (Moonshot kimi-k3, a replacement for codex and claude-opus) and deepseek-flash (a smaller coding model beside claude-sonnet) run through Pi, which has no permission system; kimi and deepseek-flash take their keys from MOONSHOT_API_KEY and DEEPSEEK_API_KEY, or point delivery.commands at a wrapper that provides them. Through Pi, as a reviewer it gets the read tool alone (the driver puts the diff in its prompt and records its output), and as a writer it runs only inside a container or sandbox limited to the worktree.
|
|
33
33
|
values:
|
|
34
34
|
codex:
|
|
35
35
|
author: codex exec --cd {worktree} --sandbox workspace-write "{prompt}"
|
|
@@ -43,6 +43,12 @@ terms:
|
|
|
43
43
|
glm:
|
|
44
44
|
author: cd {worktree} && pi -p --no-session --no-extensions --no-skills --no-context-files --tools read,bash,edit,write "{prompt}"
|
|
45
45
|
review: cd {worktree} && pi -p --no-session --no-extensions --no-skills --no-context-files --tools read "{prompt}"
|
|
46
|
+
kimi:
|
|
47
|
+
author: cd {worktree} && pi --provider moonshotai --model kimi-k3 -p --no-session --no-extensions --no-skills --no-context-files --tools read,bash,edit,write "{prompt}"
|
|
48
|
+
review: cd {worktree} && pi --provider moonshotai --model kimi-k3 -p --no-session --no-extensions --no-skills --no-context-files --tools read "{prompt}"
|
|
49
|
+
deepseek-flash:
|
|
50
|
+
author: cd {worktree} && pi --provider deepseek --model deepseek-flash -p --no-session --no-extensions --no-skills --no-context-files --tools read,bash,edit,write "{prompt}"
|
|
51
|
+
review: cd {worktree} && pi --provider deepseek --model deepseek-flash -p --no-session --no-extensions --no-skills --no-context-files --tools read "{prompt}"
|
|
46
52
|
- term_id: example_record
|
|
47
53
|
text: >-
|
|
48
54
|
A tranche in progress: the plan written by codex and approved by glm; red, green and refactor written by glm; the final review requesting changes. Only the finding's outcome is ever added to an earlier entry.
|
|
@@ -3,7 +3,7 @@ rule_id: tester.htmx.journey-no-acceptance-marker
|
|
|
3
3
|
kind: rule
|
|
4
4
|
status: active
|
|
5
5
|
name: A journey spec carries no Acceptance or WMBT header
|
|
6
|
-
statement: A journey spec MUST NOT carry `// Acceptance:` or `// WMBT:` header lines. Bun/htmx realization of `tester.vite.journey-no-acceptance-marker`,
|
|
6
|
+
statement: A journey spec, meaning a browser spec (a `*.e2e.*` file, or a Playwright file bound to a train or journey), MUST NOT carry `// Acceptance:` or `// WMBT:` header lines; a bun test file is never a journey spec. Bun/htmx realization of `tester.vite.journey-no-acceptance-marker`,
|
|
7
7
|
strict.
|
|
8
8
|
terms:
|
|
9
9
|
- term_id: browser_spec
|
|
@@ -65,7 +65,10 @@ export function collectSpecs(roots, excludes) {
|
|
|
65
65
|
return [...out.values()];
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
// A journey spec is a browser spec: a *.e2e.* file, or a Playwright file whose header binds a train or journey. A bun:test
|
|
69
|
+
// file is never one, so a Bun test proving acc:train:… (or the journey test atdd-bun.topology.e2e-location requires) may
|
|
70
|
+
// carry the Acceptance: line traceability needs.
|
|
71
|
+
export const isJourneySpec = (spec) => spec.e2eNamed || (spec.playwright && spec.journeyMarked);
|
|
69
72
|
|
|
70
73
|
/** The plan subjects a spec can bind to, read with the package's own loader. */
|
|
71
74
|
export async function planOf(root) {
|
package/integrity.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.10.
|
|
2
|
+
"version": "0.10.7",
|
|
3
3
|
"files": {
|
|
4
4
|
"HOOK_AUDIT.md": "5329d840db37671b1918f688ead26865473b87db73dbc75f7c8b2a8bbe8d6d43",
|
|
5
5
|
"PLANNER_PORT.md": "fb5935bac8b7ac18994de21e43ace3a5ef8cd55f85b0e3349fca261280054f11",
|
|
6
|
-
"README.md": "
|
|
6
|
+
"README.md": "54f6959a5fe0a83ff7ba07856f5385eff0bff0607bfe7b2287897b2d8378825e",
|
|
7
7
|
"bunfig.toml": "b9fc65eca9014c5179380259d70e76385a6a80788fa9a2df5fb3eaa5554fd2fe",
|
|
8
8
|
"conventions/atdd-bun.planner/atdd-bun.planner.acceptance-identity.convention.yaml": "81c5c773d5ee15e8d99a2c237aa9846b110b2533df45ebf407d0e3ec6ed3dd03",
|
|
9
9
|
"conventions/atdd-bun.planner/atdd-bun.planner.identity-required.convention.yaml": "e96d7c1455d0c221072d82e7f2b55da1c4f9ceb17718eb6ed96affdeef37675b",
|
|
@@ -92,13 +92,13 @@
|
|
|
92
92
|
"conventions/coder.htmx/coder.htmx.verb-endpoint-same-origin.convention.yaml": "2128d26153e2d1f19c65a1e1665d4f82c78395daedb336d6ef1256e82b74aedb",
|
|
93
93
|
"conventions/coder.htmx/coder.htmx.verb-mutation-signals-progress.convention.yaml": "ae85c5ea01a09a3c41b939988088a8e5da91848d2710a1f0a1e0c86308638ac9",
|
|
94
94
|
"conventions/delivery/delivery.approved-sha-resolves.convention.yaml": "12eadbc75b081e9469b9a0a7dbf3fdd30c8b34f66e647efa8bf2528119e61547",
|
|
95
|
-
"conventions/delivery/delivery.board.convention.yaml": "
|
|
96
|
-
"conventions/delivery/delivery.config-schema.convention.yaml": "
|
|
95
|
+
"conventions/delivery/delivery.board.convention.yaml": "51ff4c16dddf20504f34accc5ddcd150108fab70e4e6f5bdb36cec71d0c905a8",
|
|
96
|
+
"conventions/delivery/delivery.config-schema.convention.yaml": "4c7b449074a8bd9f89b70c702ab3e0df229105ddb4458772f894f0bf37813e9f",
|
|
97
97
|
"conventions/delivery/delivery.evidence-schema.convention.yaml": "9c6a22495dfb3e2fcbca700392866c3b5660084b1f5594230eb320b499d5e5cb",
|
|
98
98
|
"conventions/delivery/delivery.findings-resolved.convention.yaml": "f6574e93a9a2d4bdd6d6f5645720b1a647fc0048d95f7675fe783d2ddb62c05b",
|
|
99
99
|
"conventions/delivery/delivery.merge-gate.convention.yaml": "48745ec23625b68be9b8fe2c6dcdcba88b2b38924f6593abf6987391ff0ee243",
|
|
100
100
|
"conventions/delivery/delivery.model-allowed.convention.yaml": "1071a727f61bacd21d70921885826974234ef0a58c0a814454b9375a0432b0fe",
|
|
101
|
-
"conventions/delivery/delivery.operating-model.convention.yaml": "
|
|
101
|
+
"conventions/delivery/delivery.operating-model.convention.yaml": "7982157d957938ade878a114d3f5c2bdaa8f02e52ac21614634d9af4be9692f1",
|
|
102
102
|
"conventions/delivery/delivery.review.convention.yaml": "b033579c2d90fcb098d325dcc07c7b084e048cca43fbe8e28159ec08a412018a",
|
|
103
103
|
"conventions/delivery/delivery.reviewer-independent.convention.yaml": "8c436d66ef5b1f6d6bc5dd55a235caca2490020e88665eecadb2ca41cd4149c5",
|
|
104
104
|
"conventions/delivery/delivery.stages-complete.convention.yaml": "0e5dd8057ad51eff7906e3424a9686a81be1814447afb9a30acab43d39e49b4c",
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"conventions/tester.htmx/tester.htmx.fragment-asserts-returned-markup.convention.yaml": "b8de8e893b582d8ec4b7c24acfa16da0eccd14e6e58e92baf9e9c10c9749ea0c",
|
|
151
151
|
"conventions/tester.htmx/tester.htmx.journey-binding-header.convention.yaml": "7a9f8655cccdd12ab7e2d49af128c9f9572ad441823ca6e32cbae26b26b2d714",
|
|
152
152
|
"conventions/tester.htmx/tester.htmx.journey-layer-assembly.convention.yaml": "86948c4b1e928eef07329afeec73660ea51878e3baa58350f61a8f81c20e9fc3",
|
|
153
|
-
"conventions/tester.htmx/tester.htmx.journey-no-acceptance-marker.convention.yaml": "
|
|
153
|
+
"conventions/tester.htmx/tester.htmx.journey-no-acceptance-marker.convention.yaml": "f0d96af21e057e8ae02bb0d3c3f6e10c136e5b3eb19d94a7d0f034bc0b0a87e0",
|
|
154
154
|
"conventions/tester.htmx/tester.htmx.journey-urn-format.convention.yaml": "6771d984ddb6fe564a6803e971e934242d0a53bd8711826e66d4d0c3283f8aed",
|
|
155
155
|
"conventions/tester.htmx/tester.htmx.presentation-smoke-coverage.convention.yaml": "b18565f55f9f92f7b292bae8231c2343e72f910dc9e228b307db318eb62488cc",
|
|
156
156
|
"conventions/tester.htmx/tester.htmx.responsive-harness.convention.yaml": "cec312935ab592c2c7a5c6a94623a02df771a9e2ca2035b721ef1006c4800fdf",
|
|
@@ -675,7 +675,7 @@
|
|
|
675
675
|
"detectors/delivery_evidence/fixtures/dirty/docs/delivery/tranches/orphan/README.md": "7d480b1fff6546615e55d866784bc2267d06d0221dfde8d0ba0353fa1ea0db9a",
|
|
676
676
|
"detectors/delivery_evidence/fixtures/dirty/docs/delivery/tranches/ui/evidence.yaml": "777b4f7921e302dfce79cd764bba8b44091ac9c7ee04a00f5dbb663342ec68a0",
|
|
677
677
|
"detectors/htmx_e2e_detector/atdd.implementation.yaml": "454bb123039d57c992d27d9274e20fa3452ac41a237eb55471be62a4bcfebf06",
|
|
678
|
-
"detectors/htmx_e2e_detector/checks/_e2e.mjs": "
|
|
678
|
+
"detectors/htmx_e2e_detector/checks/_e2e.mjs": "efec6c8e3dc8b95bf3acd826397ecdedac64c06d367791a4017a272bc0660896",
|
|
679
679
|
"detectors/htmx_e2e_detector/checks/_map.json": "94dec08ff4764cd6a3dba139f12a7c984292e95a9354b0cdaa0ce9d4180e2c89",
|
|
680
680
|
"detectors/htmx_e2e_detector/checks/a11y_harness.mjs": "d66e2a6da3c3820db72fdaddcd53efb9c0e39d03bc559a59b800923d5c944700",
|
|
681
681
|
"detectors/htmx_e2e_detector/checks/covered_train_is_routed.mjs": "c7a8db0e932ba96489477fb5a8589cd9eba868db6fbaaa8012802a4da78fa0f8",
|
|
@@ -1192,10 +1192,10 @@
|
|
|
1192
1192
|
"relationships.yaml": "97da2db334e3c8cd4f634ee8d12fe426aabc0f5c37508d02450ce8beca100f04",
|
|
1193
1193
|
"src/agent.ts": "65e4579adb5a54977f557f8c783aba0e2a2a1d5cf3cea9f017b67ffe598b2f65",
|
|
1194
1194
|
"src/board-ui.ts": "16a843705552f8f18e0e328e90e4a2a9148d771ca69f2708fe4a91302947dfad",
|
|
1195
|
-
"src/board.ts": "
|
|
1195
|
+
"src/board.ts": "115c6cff12fb2b20e6471013161aef020a65331ecb97fa323e9b08f55053149f",
|
|
1196
1196
|
"src/ci.ts": "67e54d2cbfc44a9e42837d5e750af526d6255c539d3c452a4d5fca3226c0d9ac",
|
|
1197
1197
|
"src/cli.ts": "e45f2666334503a91eb65b1b5ee4dd8acab6b99f62d333e2ee2b65963db6358b",
|
|
1198
|
-
"src/delivery.ts": "
|
|
1198
|
+
"src/delivery.ts": "9a4500ae949faeef7bcfda114b7b09d4ac2b2cbb1218e68016829e0b6892847b",
|
|
1199
1199
|
"src/docs-capability.ts": "115cf27049a5cf19c133bac5ec237072be6f969a59a2ef6767e054108a21dc5c",
|
|
1200
1200
|
"src/enforce.ts": "41a6f8c058a2a034996c817f224191403cc66ccc4ed4e04bbefc430014c783d8",
|
|
1201
1201
|
"src/hooks.ts": "9779eb48f9d0cb6234c33f6616f98dde6583d2ee03ac3c19f3ace2d156af26d0",
|
package/package.json
CHANGED
package/src/board.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
3
4
|
import { readFile } from "node:fs/promises";
|
|
4
5
|
import { join } from "node:path";
|
|
5
6
|
import { runUi } from "./board-ui";
|
|
@@ -107,17 +108,26 @@ export async function read(url: string, topics: string[], since = "all"): Promis
|
|
|
107
108
|
* It polls rather than streams: a live stream can drop a message published while it connects. ntfy reads `since` (a
|
|
108
109
|
* message id) as that message's time on every topic, so one cursor serves several topics. Returns null after
|
|
109
110
|
* `timeoutSeconds`, so an agent's tool call stays bounded. */
|
|
110
|
-
export async function waitFor(url: string, topics: string[], agent: string, since = "all", timeoutSeconds = 0, skip: string[] = [], intervalMs = 500): Promise<Message | null> {
|
|
111
|
+
export async function waitFor(url: string, topics: string[], agent: string, since = "all", timeoutSeconds = 0, skip: string[] = [], intervalMs = 500, seen: (id: string) => void = () => {}): Promise<Message | null> {
|
|
111
112
|
const deadline = timeoutSeconds > 0 ? Date.now() + timeoutSeconds * 1000 : Infinity;
|
|
112
113
|
for (;;) {
|
|
113
114
|
let messages: Message[] = [];
|
|
114
115
|
try { messages = await read(url, topics, since); } catch { /* the board is briefly unavailable: keep waiting */ }
|
|
115
|
-
for (const message of messages) { since = message.id; if (addressedTo(message, agent) && !skip.includes(message.header.kind ?? "")) return message; }
|
|
116
|
+
for (const message of messages) { since = message.id; seen(since); if (addressedTo(message, agent) && !skip.includes(message.header.kind ?? "")) return message; }
|
|
116
117
|
if (Date.now() >= deadline) return null;
|
|
117
118
|
await Bun.sleep(intervalMs);
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
/** Where `wait` keeps its place, per identity and topic set, so restarting a watcher is always the same command.
|
|
123
|
+
* Best effort: where the file cannot be read or written, wait behaves as if it had no saved place. */
|
|
124
|
+
function cursorFile(agent: string, topics: string[], env: Record<string, string | undefined>): string {
|
|
125
|
+
const key = `${agent}__${[...topics].sort().join("+")}`.replace(/[^A-Za-z0-9@+_.-]/g, "_");
|
|
126
|
+
return join(env.ATDD_BOARD_STATE || join(homedir(), ".atdd-board", "cursors"), key);
|
|
127
|
+
}
|
|
128
|
+
const loadCursor = (file: string) => { try { return readFileSync(file, "utf8").trim() || undefined; } catch { return undefined; } };
|
|
129
|
+
const saveCursor = (file: string, id: string) => { try { mkdirSync(join(file, ".."), { recursive: true }); writeFileSync(file, `${id}\n`); } catch { /* best effort */ } };
|
|
130
|
+
|
|
121
131
|
export function format(message: Message, withTopic = false): string {
|
|
122
132
|
const time = new Date(message.time * 1000).toTimeString().slice(0, 8), h = message.header;
|
|
123
133
|
const route = `${h.from ?? "?"} -> ${[h.to ?? []].flat().join(", ") || "?"}`, tags = [h.kind, h.stage].filter(Boolean).join(" · ");
|
|
@@ -141,8 +151,10 @@ export async function chat(args: string[], env: Record<string, string | undefine
|
|
|
141
151
|
const me = agent();
|
|
142
152
|
topics.forEach(topic => checkTopic(topic, env));
|
|
143
153
|
if (command === "wait") {
|
|
144
|
-
|
|
145
|
-
|
|
154
|
+
// Without --since, continue from where this identity last stopped on these topics; either way, remember the place.
|
|
155
|
+
const file = cursorFile(me, topics, env);
|
|
156
|
+
const message = await waitFor(url, topics, me, flag("since") || loadCursor(file) || "all", Number(flag("timeout") ?? 0), list(flag("skip")), 500, id => saveCursor(file, id));
|
|
157
|
+
if (!message) { console.error(`no message for ${me} on ${topics.join(", ")} yet; run the same wait again to continue`); return 2; }
|
|
146
158
|
console.log(format(message, topics.length > 1));
|
|
147
159
|
return 0;
|
|
148
160
|
}
|
package/src/delivery.ts
CHANGED
|
@@ -41,11 +41,11 @@ export type DeliveryPolicy = {
|
|
|
41
41
|
/** The default operating model: two reviews, the plan and the whole change; the stages between them are written and
|
|
42
42
|
* held by their deterministic gates. Lists are preference orders; a later model is used only as a recorded fallback. */
|
|
43
43
|
const DEFAULT_STAGES: Partial<Record<Stage, { writer?: string[]; reviewer?: string[] }>> = {
|
|
44
|
-
plan: { writer: ["codex", "claude-opus"], reviewer: ["glm", "claude-opus", "codex"] },
|
|
45
|
-
red: { writer: ["glm", "claude-sonnet", "claude-opus", "codex"] },
|
|
46
|
-
green: { writer: ["glm", "claude-sonnet", "claude-opus", "codex"] },
|
|
47
|
-
refactor: { writer: ["glm", "claude-sonnet", "claude-opus", "codex"] },
|
|
48
|
-
final: { reviewer: ["codex", "glm", "claude-opus"] },
|
|
44
|
+
plan: { writer: ["codex", "claude-opus", "kimi"], reviewer: ["glm", "claude-opus", "codex", "kimi"] },
|
|
45
|
+
red: { writer: ["glm", "claude-sonnet", "deepseek-flash", "claude-opus", "codex", "kimi"] },
|
|
46
|
+
green: { writer: ["glm", "claude-sonnet", "deepseek-flash", "claude-opus", "codex", "kimi"] },
|
|
47
|
+
refactor: { writer: ["glm", "claude-sonnet", "deepseek-flash", "claude-opus", "codex", "kimi"] },
|
|
48
|
+
final: { reviewer: ["codex", "glm", "claude-opus", "kimi"] },
|
|
49
49
|
};
|
|
50
50
|
/** The stage a record or config name refers to: a current name, or a legacy review stage's. */
|
|
51
51
|
export const stageOf = (name: string): Stage | undefined => (STAGES as readonly string[]).includes(name) ? name as Stage : LEGACY_STAGES[name];
|