@haiyangbg/buildbeat 4.0.0 → 4.2.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 CHANGED
@@ -2,10 +2,25 @@
2
2
 
3
3
  > 本项目吃自己的狗粮(红线④:必更 CHANGELOG)。格式循 Keep a Changelog,倒序。
4
4
 
5
- ## Unreleased
5
+ ## v4.2.0 — 2026-10-07(次版本:合并决定点能接修复;合并批准绑定候选)
6
+
7
+ - Bind merge approval replies to `--candidate <sha>` and forward the binding through the CLI; reject old or unbound replies after repair. Support both repair paths when the current candidate is still the base commit. Isolate repair regression fixtures from host Git configuration, signing and hooks. The owner confirmed the public name `--candidate <sha>` and runtime 4.2 as the next minor on 2026-10-07; merge-decision `run --adopt`, `decide --action fix` and candidate binding require runtime 4.2 or later.
8
+ - Allow manual repair adoption at the merge decision: require clean actual HEAD, a new descendant of the candidate and changes within `allowedPaths`; resume at verify, review incrementally and return to the merge decision while preserving evidence and review accounting.
9
+ - Add `decide --action fix --reason <text>` at the merge decision when a fixer is configured. Record an accepted P1 with the human's reason, feed it to the fixer and continue through verify/review without spending a review round for the decision or changing budgets. Status, inbox and notifications offer manual repair and the configured fixer path. SUCCEEDED and legacy 3.x Runs retain their existing boundaries.
10
+
11
+ ## v4.1.0 — 2026-10-07(次版本:跨仓总览;提示里的命令与路径都能照做)
12
+
13
+ > **发布状态**:`@haiyangbg/buildbeat@4.1.0` 已于 2026-10-07 从 `main`(release PR #74,merge commit `9259ac9`,tag `v4.1.0`)经 OIDC Trusted Publishing 发布到 dist-tag **`latest`**(run 37590027989,publish 与 verify 双 job 一次 success;所有者授权「推送并发 4.1.0」)。独立回读(直连 npmjs.org):`latest` = 4.1.0、`dist.integrity` 与本地用 Node 24 + npm 11.19.0 打的候选逐字一致、注册表 tarball 解压后与候选逐字节相同、SLSA v1 provenance、隔离安装 `--version` = 4.1.0、裸调用零写入、用法含 `--all-repos`、`npm audit signatures` 通过;GitHub Release v4.1.0 标 Latest;本机两份全局安装均为 4.1.0;证据见 [`docs/releases/V4.1.0-RELEASE-EVIDENCE-2026-10-07.md`](docs/releases/V4.1.0-RELEASE-EVIDENCE-2026-10-07.md)。`next` 仍为 4.0.0。
14
+
15
+ - Add read-only `status --all-repos`: discover the main repository, configured target checkouts and immediate child repositories, deduplicate real paths, show pending decisions first and count settled work. JSON retains all work; `--work` filters across repositories, while `--run` requires single-repository status.
16
+ - Fix main-repository status/overview reporting target-repository work as ready to start: read state, findings and decisions from the target, retain the main run configuration for commands, and quote paths relative to the caller. Missing/invalid targets warn without hiding other work. Ship `src/v2/runtime/overview-repos.js` and update the package-file inventory for this runtime helper.
17
+ - Hints give commands that work as written. A merged work whose run configs were all read and none has a `release:` section is told there is nothing left to do, instead of being sent to `buildbeat release`, which fails without that section and left the work unable to close; with a `release:` section the hint names the config that has it. A waiting run with no reply commands points to `status --work`, and `check` names `status` instead of the 3.x `inbox`. The run config template comment (and its copies in the Skill, both quickstarts and the example) names `check` / `run` / `--new`, and the quickstart says where the npm package installs the templates. Found by a first run of a fresh project on 4.0.0 with real codex workers.
18
+ - `status --all-repos` prints English labels like the rest of the CLI (`pending decisions · <repo>`, `repo <repo>:`, `<repo>: settled N (closed a · cancelled b · merged, no release step c)`, `repo:` / `runs in:` under a work), and single-repository status again shows `NO_INTENT` for a work directory without a work description even when it has runs, as 4.0.0 did; a work known only from runtime ledgers still shows its run.
6
19
 
7
20
  ## v4.0.0 — 2026-10-04(主版本:聚焦可恢复交付,精简产品范围与操作入口)
8
21
 
22
+ > **发布状态**:`@haiyangbg/buildbeat@4.0.0` 已于 2026-10-05 从 `main`(PR #70、#71 内容,release PR #72,merge commit `c15fbe0`,tag `v4.0.0`)经 OIDC Trusted Publishing 发布到 dist-tag **`latest`**(run 37262387333,publish 与 verify 双 job 一次 success;所有者授权「合并并发 4.0」)。独立回读(直连 npmjs.org):`latest` = 4.0.0、`dist.integrity` 与本地用 Node 24 + npm 11.19.0 打的候选逐字一致、SLSA v1 provenance、隔离安装 `--version` = 4.0.0、裸调用零写入、用法含 `release` 与 `decide --action close`、`npm audit signatures` 通过;GitHub Release v4.0.0 标 Latest;本机两份全局安装均为 4.0.0;证据见 [`docs/releases/V4.0.0-RELEASE-EVIDENCE-2026-10-05.md`](docs/releases/V4.0.0-RELEASE-EVIDENCE-2026-10-05.md)。同日所有者本人经 npm 两步验证把 dist-tag `next` 从 3.3.1 挪到 4.0.0;直连 npmjs.org 回读 `next` = `latest` = 4.0.0。
23
+
9
24
  - Owner decisions before 4.0 (WORK-4.0-INTENT-FOLLOWUPS): `requireScreenshot: true` makes verify write PNG screenshots of the real render to `BUILDBEAT_SCREENSHOT_DIR`, records them as candidate-bound evidence after checking each decodes, fails a verify that leaves none, requires them in the merge check and lists them for the reviewer, the decision card and notifications; `buildbeat release` runs the project's read-only readback after a merged release and records it in `delivery/work/<ID>/releases.jsonl`, and `decide --action close` closes the window only on a passing readback; a one-page guide states what people and tools without the runtime may read and write; the governance-template rationale is corrected.
10
25
 
11
26
  - Focus the product on portable work context, the recoverable delivery loop, and evidence-backed human decisions.
package/SKILL.md CHANGED
@@ -11,22 +11,25 @@ description: BuildBeat 用项目文件接续上下文,在隔离工作树内自
11
11
 
12
12
  先跑 `buildbeat --version`,按结果选命令:
13
13
 
14
- - 4.x:按下文操作。
14
+ - 4.x:按下文操作,但合并决定点的两种修复入口有下述版本限制。
15
15
  - 3.x:下文的 `run`、`status --work`、`decide`、`check`、`history` 和 `work.md` 在 3.x 不存在,沿用项目现有的 3.x 写法:`intent.md` + `plan.md`,分别 `accept --artifact intent` 与 `accept --artifact plan`;`start --config <config> --attempt new` 开工,`resume --config <config> [--run <RUN>] [--adopt <sha> --by <name>]` 续跑;`overview`、`inbox`、`status --run <RUN>` 看进度;`approve --transition <t>` / `reject --reason <why>` 决定;`findings adjudicate` 裁决问题;`doctor` 检查配置;run-config 保留 `workflow:` 与 `riskPreset:`。
16
16
  - 3.x 的活动 Run 只能用 3.x 运行时完成或取消。升级到 4.x 前先问用户,并按[迁移说明](docs/MIGRATION.md)处理。
17
17
  - 没有运行时:按[不装运行时也能参与](docs/v2/guide/12-without-runtime.md)只读状态、写 `work.md` 和规定格式的决定行;不得代批 Run,不得声称验证、审查或回读已通过。
18
18
 
19
+ 合并决定点的 `run --adopt`、`decide --action fix` 与批准命令的 `--candidate` 候选绑定要求运行时 4.2 或更高版本。
20
+
19
21
  ## 会话操作
20
22
 
21
23
  | 用户意图 | 操作 |
22
24
  |---|---|
23
- | 接手、查看进度、有什么待批 | `buildbeat status --repo .`;指定 `--work` 或 `--run` 查看细节 |
25
+ | 接手、查看进度、有什么待批 | `buildbeat status --repo .`;主仓的 run 配置中 `repo:` 指向其他仓时,查看全部代码仓加 `--all-repos`(运行时 4.1 起;可加 `--work`、`--json`);单仓指定 `--work` 或 `--run` 查看细节 |
24
26
  | 准备一项工作 | 写 `delivery/work/<ID>/work.md`,包含目标、范围、验收、实施计划;复制 run-config 样板并填真实命令 |
25
27
  | 接受工作说明 | `buildbeat accept --repo . --work <ID> --by <owner>`;用户已有明确授权时直接记录,不重复问同一个决定 |
26
28
  | 开工、继续 | `buildbeat run --config <config>`;首次编号,已有运行则恢复;明确新一轮时加 `--new` |
27
- | 批准、拒绝 | 读取状态卡,再 `buildbeat decide --repo . --run <RUN> --action approve|reject --transition <t> --by <owner>`;非终态决定后续跑 |
29
+ | 批准、拒绝 | 读取状态卡并复制批准或拒绝命令;合并批准为 `buildbeat decide --repo . --run <RUN> --action approve --transition enter-wait-merge --candidate <完整 SHA> --by <owner>`,使用卡片候选;修复后旧候选或未绑定候选的批准失效;非终态决定后续跑 |
30
+ | 合并决定退回修复 | `buildbeat decide --repo . --run <RUN> --action fix --reason <要修什么> --by <owner>`;仅合并决定点且有 fixer 时可用,再 `buildbeat run --config <config> --run <RUN>` 续跑;原因记为已接受 P1,重验、再审,review 预算照常累计 |
28
31
  | 问题接受、驳回 | `buildbeat decide --repo . --work <ID> --action accept|dismiss --fingerprint <fp> --by <owner> --note <reason>` |
29
- | 已手修并提交 | `buildbeat run --config <config> --run <RUN> --adopt <sha> --by <name>`;检查干净工作树与实际 HEAD 后从 verify 继续 |
32
+ | 已手修并提交 | `buildbeat run --config <config> --run <RUN> --adopt <sha> --by <name>`;检查干净工作树与实际 HEAD 后从 verify 继续;合并决定点也可用,此时须为当前候选的新后代且改动不越界 |
30
33
  | 配置或环境有问题 | `buildbeat check --config <config>`;显式 `--step <step>` 会在主检出执行 worker,只有用户任务需要才调用,结果不是正式证据 |
31
34
  | 上线后回读、关窗 | 人完成上线后 `buildbeat release --config <config> [--note <text>]`;最近一次回读通过再 `buildbeat decide --repo . --work <ID> --action close --result <text> --by <owner>`。回读失败不能关窗 |
32
35
  | 结束一轮 | `buildbeat stop --repo . --run <RUN> --reason <reason>`;活动驱动持锁时先处理进程,不能把 stop 当作进程杀手 |
@@ -54,10 +57,10 @@ description: BuildBeat 用项目文件接续上下文,在隔离工作树内自
54
57
  ```yaml
55
58
  # BuildBeat v2 run 配置样板。拷到 delivery/work/<WORK-ID>/run-config.yaml 后改 work / run / allowedPaths / workers。
56
59
  # 路径相对本文件解析。严格 YAML 子集:只有块列表与块映射(列表项可与键同缩进),行内只允许空的 [] / {},无锚点,注释必须独占一行。
57
- # 起跑前:buildbeat doctor --config <本文件>;起跑:buildbeat start --config <本文件> --attempt new
60
+ # 起跑前:buildbeat check --config <本文件>;起跑:buildbeat run --config <本文件>
58
61
  repo: ../../..
59
62
  work: WORK-X
60
- # 家族名;--attempt new 自动编成 RUN-X-01/02…
63
+ # 家族名;run 自动编成 RUN-X-01/02…,--new 开新一轮
61
64
  run: RUN-X
62
65
  # builder / fixer 只能改这些目录;越界改动不成为候选
63
66
  allowedPaths:
@@ -1,6 +1,6 @@
1
1
  # BuildBeat capability matrix / 能力矩阵
2
2
 
3
- Current release: 4.0.0. One product path: Skill → local runtime →
3
+ Current release: 4.2.0. One product path: Skill → local runtime →
4
4
  project records. The Claude Code plugin distributes the Skill and references;
5
5
  install the runtime separately. Files remain readable without the runtime, but
6
6
  manual work has no automatic loop, isolation, budget or approval enforcement;
@@ -13,9 +13,9 @@ may be written by hand.
13
13
  | Delivery loop | Fixed build/verify/review/fix flow | External CLI tools do the actual work |
14
14
  | Recovery | Run/step ledger, resume, manual candidate adoption, stale locks | Active runtime is not transferred by Git clone |
15
15
  | Trusted result | Real command evidence, current candidate, read-only review | Script tests do not prove real AI business capability |
16
- | Decisions | Acceptance, exact approval, frozen checks, budget/convergence stops | No automatic merge, push or deployment |
16
+ | Decisions | Acceptance, exact approval bound to the candidate, frozen checks, budget/convergence stops, repairs at the merge decision (adopt a hand fix or send it back to the fixer) | No automatic merge, push or deployment |
17
17
  | Efficiency | Verify cache, review range hints, parallel Work isolation | External test state must be isolated and stable |
18
- | Feedback | Unified status/cost/decisions, liveness, webhook/DingTalk | Notifications do not approve |
18
+ | Feedback | Unified status/cost/decisions, also across a main repository and its code repositories (`--all-repos`), liveness, webhook/DingTalk | Notifications do not approve |
19
19
  | UI evidence | `requireScreenshot`: screenshots from verify, bound to the candidate and required at merge | The project's verify renders and captures |
20
20
  | Release closeout | `release` records the project's readback in the Work; `decide --action close` on a passing readback | No deployment; observation stays with project monitoring |
21
21
  | Maintenance | Check, history/replay, safe gc | Not a production monitoring service |
package/docs/RELEASING.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This runbook governs BuildBeat's public npm distribution. The canonical package is `@haiyangbg/buildbeat` in `HaiYangBG1/BuildBeat`; the only executable is `buildbeat`. No other package name or executable alias receives publications.
4
4
 
5
- Release evidence at source package version `@haiyangbg/buildbeat@4.0.0` (release candidate awaiting publication; the dated verified distribution below remains current until its readback); latest independently verified BuildBeat npm distribution `@haiyangbg/buildbeat@3.3.1` (dist-tags `latest` and `next`; the owner moved `next` from `3.3.0` to `3.3.1` with npm 2FA on 2026-10-02), anchored by annotated tag `v3.3.1` at commit `cc7a6db`, workflow run [36982501415](https://github.com/HaiYangBG1/BuildBeat/actions/runs/36982501415), and archived in [`V3.3.1-RELEASE-EVIDENCE-2026-10-02.md`](releases/V3.3.1-RELEASE-EVIDENCE-2026-10-02.md). The 3.3.0 chain (`latest` on 2026-10-02 until 3.3.1 took over the same day; the owner moved `next` from `3.2.1` to `3.3.0` with npm 2FA that day) stays archived in [`V3.3.0-RELEASE-EVIDENCE-2026-10-02.md`](releases/V3.3.0-RELEASE-EVIDENCE-2026-10-02.md). The 3.2.1 chain (`latest` from 2026-09-26 until 3.3.0 took over on 2026-10-02; the owner moved `next` from `3.0.1` to `3.2.1` with npm 2FA on 2026-09-26) stays archived in [`V3.2.1-RELEASE-EVIDENCE-2026-09-26.md`](releases/V3.2.1-RELEASE-EVIDENCE-2026-09-26.md). The 3.2.0 chain (`latest` on 2026-09-26 until 3.2.1 took over the same day) stays archived in [`V3.2.0-RELEASE-EVIDENCE-2026-09-26.md`](releases/V3.2.0-RELEASE-EVIDENCE-2026-09-26.md). The 3.1.0 chain (`latest` on 2026-09-26 until 3.2.0 took over the same day) stays archived in [`V3.1.0-RELEASE-EVIDENCE-2026-09-26.md`](releases/V3.1.0-RELEASE-EVIDENCE-2026-09-26.md). The 3.0.1 chain (`latest` from 2026-09-09 until 3.1.0 took over on 2026-09-26) stays archived in [`V3.0.1-RELEASE-EVIDENCE-2026-09-09.md`](releases/V3.0.1-RELEASE-EVIDENCE-2026-09-09.md). The 3.0.0 chain (`latest` from 2026-09-09 until 3.0.1 took over the same day; the first version without the removed generation) stays archived in [`V3.0.0-RELEASE-EVIDENCE-2026-09-09.md`](releases/V3.0.0-RELEASE-EVIDENCE-2026-09-09.md). The 2.0.2 chain (`latest` from 2026-09-09 until 3.0.0 took over the same day; the last version carrying the removed generation) stays archived in [`V2.0.2-RELEASE-EVIDENCE-2026-09-09.md`](releases/V2.0.2-RELEASE-EVIDENCE-2026-09-09.md). The 2.0.1 chain (`latest` from 2026-09-06 until 2.0.2 took over on 2026-09-09) stays archived in [`V2.0.1-RELEASE-EVIDENCE-2026-09-06.md`](releases/V2.0.1-RELEASE-EVIDENCE-2026-09-06.md). The 2.0.0 chain (`latest` from 2026-09-05 until 2.0.1 took over on 2026-09-06) stays archived in [`V2.0.0-RELEASE-EVIDENCE-2026-09-05.md`](releases/V2.0.0-RELEASE-EVIDENCE-2026-09-05.md). The beta.5 chain stays archived in [`V2.0.0-BETA.5-RELEASE-EVIDENCE-2026-09-05.md`](releases/V2.0.0-BETA.5-RELEASE-EVIDENCE-2026-09-05.md). The beta.4 chain stays archived in [`V2.0.0-BETA.4-RELEASE-EVIDENCE-2026-09-03.md`](releases/V2.0.0-BETA.4-RELEASE-EVIDENCE-2026-09-03.md); the beta.3 chain in [`V2.0.0-BETA.3-RELEASE-EVIDENCE-2026-09-01.md`](releases/V2.0.0-BETA.3-RELEASE-EVIDENCE-2026-09-01.md). The beta.2 chain stays archived in [`V2.0.0-BETA.2-RELEASE-EVIDENCE-2026-08-28.md`](releases/V2.0.0-BETA.2-RELEASE-EVIDENCE-2026-08-28.md); the beta.1 chain stays archived in [`V2.0.0-BETA.1-RELEASE-EVIDENCE-2026-08-28.md`](releases/V2.0.0-BETA.1-RELEASE-EVIDENCE-2026-08-28.md). Earlier distributions and the retired legacy package name are archived in the dated evidence files under `docs/releases/`.
5
+ Release evidence at source package version `@haiyangbg/buildbeat@4.2.0`; latest independently verified BuildBeat npm distribution `@haiyangbg/buildbeat@4.1.0` (dist-tag `latest`; `next` stays on `4.0.0` until the owner moves it with npm 2FA), anchored by annotated tag `v4.1.0` at commit `9259ac9`, workflow run [37590027989](https://github.com/HaiYangBG1/BuildBeat/actions/runs/37590027989), and archived in [`V4.1.0-RELEASE-EVIDENCE-2026-10-07.md`](releases/V4.1.0-RELEASE-EVIDENCE-2026-10-07.md). The 4.0.0 chain (`latest` from 2026-10-05 until 4.1.0 took over on 2026-10-07; the owner moved `next` from `3.3.1` to `4.0.0` with npm 2FA on 2026-10-05) stays archived in [`V4.0.0-RELEASE-EVIDENCE-2026-10-05.md`](releases/V4.0.0-RELEASE-EVIDENCE-2026-10-05.md). The 3.3.1 chain (`latest` from 2026-10-02 until 4.0.0 took over on 2026-10-05; the owner moved `next` from `3.3.0` to `3.3.1` with npm 2FA on 2026-10-02) stays archived in [`V3.3.1-RELEASE-EVIDENCE-2026-10-02.md`](releases/V3.3.1-RELEASE-EVIDENCE-2026-10-02.md). The 3.3.0 chain (`latest` on 2026-10-02 until 3.3.1 took over the same day; the owner moved `next` from `3.2.1` to `3.3.0` with npm 2FA that day) stays archived in [`V3.3.0-RELEASE-EVIDENCE-2026-10-02.md`](releases/V3.3.0-RELEASE-EVIDENCE-2026-10-02.md). The 3.2.1 chain (`latest` from 2026-09-26 until 3.3.0 took over on 2026-10-02; the owner moved `next` from `3.0.1` to `3.2.1` with npm 2FA on 2026-09-26) stays archived in [`V3.2.1-RELEASE-EVIDENCE-2026-09-26.md`](releases/V3.2.1-RELEASE-EVIDENCE-2026-09-26.md). The 3.2.0 chain (`latest` on 2026-09-26 until 3.2.1 took over the same day) stays archived in [`V3.2.0-RELEASE-EVIDENCE-2026-09-26.md`](releases/V3.2.0-RELEASE-EVIDENCE-2026-09-26.md). The 3.1.0 chain (`latest` on 2026-09-26 until 3.2.0 took over the same day) stays archived in [`V3.1.0-RELEASE-EVIDENCE-2026-09-26.md`](releases/V3.1.0-RELEASE-EVIDENCE-2026-09-26.md). The 3.0.1 chain (`latest` from 2026-09-09 until 3.1.0 took over on 2026-09-26) stays archived in [`V3.0.1-RELEASE-EVIDENCE-2026-09-09.md`](releases/V3.0.1-RELEASE-EVIDENCE-2026-09-09.md). The 3.0.0 chain (`latest` from 2026-09-09 until 3.0.1 took over the same day; the first version without the removed generation) stays archived in [`V3.0.0-RELEASE-EVIDENCE-2026-09-09.md`](releases/V3.0.0-RELEASE-EVIDENCE-2026-09-09.md). The 2.0.2 chain (`latest` from 2026-09-09 until 3.0.0 took over the same day; the last version carrying the removed generation) stays archived in [`V2.0.2-RELEASE-EVIDENCE-2026-09-09.md`](releases/V2.0.2-RELEASE-EVIDENCE-2026-09-09.md). The 2.0.1 chain (`latest` from 2026-09-06 until 2.0.2 took over on 2026-09-09) stays archived in [`V2.0.1-RELEASE-EVIDENCE-2026-09-06.md`](releases/V2.0.1-RELEASE-EVIDENCE-2026-09-06.md). The 2.0.0 chain (`latest` from 2026-09-05 until 2.0.1 took over on 2026-09-06) stays archived in [`V2.0.0-RELEASE-EVIDENCE-2026-09-05.md`](releases/V2.0.0-RELEASE-EVIDENCE-2026-09-05.md). The beta.5 chain stays archived in [`V2.0.0-BETA.5-RELEASE-EVIDENCE-2026-09-05.md`](releases/V2.0.0-BETA.5-RELEASE-EVIDENCE-2026-09-05.md). The beta.4 chain stays archived in [`V2.0.0-BETA.4-RELEASE-EVIDENCE-2026-09-03.md`](releases/V2.0.0-BETA.4-RELEASE-EVIDENCE-2026-09-03.md); the beta.3 chain in [`V2.0.0-BETA.3-RELEASE-EVIDENCE-2026-09-01.md`](releases/V2.0.0-BETA.3-RELEASE-EVIDENCE-2026-09-01.md). The beta.2 chain stays archived in [`V2.0.0-BETA.2-RELEASE-EVIDENCE-2026-08-28.md`](releases/V2.0.0-BETA.2-RELEASE-EVIDENCE-2026-08-28.md); the beta.1 chain stays archived in [`V2.0.0-BETA.1-RELEASE-EVIDENCE-2026-08-28.md`](releases/V2.0.0-BETA.1-RELEASE-EVIDENCE-2026-08-28.md). Earlier distributions and the retired legacy package name are archived in the dated evidence files under `docs/releases/`.
6
6
 
7
7
  ## Channels and branches
8
8
 
@@ -13,3 +13,5 @@
13
13
  | Switch sessions | Save missing facts and retain active runtime files; continue from project records |
14
14
 
15
15
  The loop ends at the merge decision. Merge, push, deployment and publication each require their corresponding authorization.
16
+
17
+ For a main repository with code repositories, inspect them together with `buildbeat status --repo . --all-repos`. See [Session handoff](11-session-handoff.en.md) for discovery, filters and settled counts.
@@ -13,3 +13,5 @@
13
13
  | 换会话 | 保存未落盘事实,保留活动运行现场;新会话按项目记录接续 |
14
14
 
15
15
  工作流默认停在合并决定。合并、推送、部署和发布分别需要对应授权。
16
+
17
+ 主仓有多个代码仓时,用 `buildbeat status --repo . --all-repos` 一起查看;发现范围、筛选与已了结计数见[跨会话接续](11-session-handoff.md)。
@@ -3,7 +3,7 @@
3
3
  Install the runtime: `npm install --global @haiyangbg/buildbeat@latest` (Node ≥ 20). Before upgrading from 3.x, finish or cancel runs still in progress with their original 3.x runtime, see [Migration](../../MIGRATION.md); never replace a runtime underneath an active run.
4
4
 
5
5
  1. Check Node ≥20, Git, Bash and an authenticated AI tool. Keep project-owned instructions.
6
- 2. Install templates/v2/AGENTS.md and CLAUDE.md according to the tool's loading rules; copy runtime exclusions from gitignore.template.
6
+ 2. The templates ship with the npm package under `$(npm root -g)/@haiyangbg/buildbeat/templates/`. Install `v2/AGENTS.md` and `v2/CLAUDE.md` according to the tool's loading rules; copy runtime exclusions from `gitignore.template` (in `templates/`, not in `v2/`).
7
7
  3. Create delivery/work/WORK-X/work.md using the goal, scope, acceptance and implementation sections in work.example.md.
8
8
  4. Copy templates/v2/envelope/ to delivery/envelope/ and configure real tool and verification commands. New work needs no workflow.yaml copy.
9
9
  5. After the user accepts the work, use the commands below. Detach long runs from short host timeouts and handle the decision shown by status.
@@ -18,10 +18,10 @@ buildbeat status --repo . --work WORK-X
18
18
  ```yaml
19
19
  # BuildBeat v2 run 配置样板。拷到 delivery/work/<WORK-ID>/run-config.yaml 后改 work / run / allowedPaths / workers。
20
20
  # 路径相对本文件解析。严格 YAML 子集:只有块列表与块映射(列表项可与键同缩进),行内只允许空的 [] / {},无锚点,注释必须独占一行。
21
- # 起跑前:buildbeat doctor --config <本文件>;起跑:buildbeat start --config <本文件> --attempt new
21
+ # 起跑前:buildbeat check --config <本文件>;起跑:buildbeat run --config <本文件>
22
22
  repo: ../../..
23
23
  work: WORK-X
24
- # 家族名;--attempt new 自动编成 RUN-X-01/02…
24
+ # 家族名;run 自动编成 RUN-X-01/02…,--new 开新一轮
25
25
  run: RUN-X
26
26
  # builder / fixer 只能改这些目录;越界改动不成为候选
27
27
  allowedPaths:
@@ -92,3 +92,5 @@ workers:
92
92
  [Migration](../../MIGRATION.md) · [Worker contract](05-worker-contract.en.md) · [Recovery](10-recovery.en.md)
93
93
 
94
94
  Before starting, commit work.md and worker scripts into the selected base so the isolated checkout sees the accepted scope. Acceptance records may be committed afterward.
95
+
96
+ For a main repository with code repositories, use `buildbeat status --repo . --all-repos` for pending decisions and open work across repositories; add `--work <ID>` or `--json` as needed. See [Session handoff](11-session-handoff.en.md).
@@ -3,7 +3,7 @@
3
3
  安装运行时:`npm install --global @haiyangbg/buildbeat@latest`(Node ≥ 20)。从 3.x 升级前,先用原来的 3.x 运行时完成或取消仍在进行的 Run,见[迁移说明](../../MIGRATION.md);不要在 Run 进行中替换运行时。
4
4
 
5
5
  1. 检查 Node ≥20、Git、Bash 和已鉴权的 AI 工具;保留项目自己的规范。
6
- 2. 将 templates/v2/AGENTS.md、CLAUDE.md 按工具装载方式放入项目;复制 gitignore.template 的运行时排除项。
6
+ 2. 模板随 npm 包安装在 `$(npm root -g)/@haiyangbg/buildbeat/templates/`。将其中 `v2/AGENTS.md`、`v2/CLAUDE.md` 按工具装载方式放入项目;从 `gitignore.template`(在 `templates/` 下,不在 `v2/` 里)复制运行时排除项。
7
7
  3. 建 delivery/work/WORK-X/work.md,使用 work.example.md 的目标、范围、验收、实施计划结构。
8
8
  4. 把 templates/v2/envelope/ 复制为 delivery/envelope/;配置真实工具和验证命令。新工作不用复制 workflow.yaml。
9
9
  5. 用户接受工作说明后运行下面命令。长运行使用宿主支持的脱离方式启动;根据 status 的待决定对象处理例外。
@@ -18,10 +18,10 @@ buildbeat status --repo . --work WORK-X
18
18
  ```yaml
19
19
  # BuildBeat v2 run 配置样板。拷到 delivery/work/<WORK-ID>/run-config.yaml 后改 work / run / allowedPaths / workers。
20
20
  # 路径相对本文件解析。严格 YAML 子集:只有块列表与块映射(列表项可与键同缩进),行内只允许空的 [] / {},无锚点,注释必须独占一行。
21
- # 起跑前:buildbeat doctor --config <本文件>;起跑:buildbeat start --config <本文件> --attempt new
21
+ # 起跑前:buildbeat check --config <本文件>;起跑:buildbeat run --config <本文件>
22
22
  repo: ../../..
23
23
  work: WORK-X
24
- # 家族名;--attempt new 自动编成 RUN-X-01/02…
24
+ # 家族名;run 自动编成 RUN-X-01/02…,--new 开新一轮
25
25
  run: RUN-X
26
26
  # builder / fixer 只能改这些目录;越界改动不成为候选
27
27
  allowedPaths:
@@ -92,3 +92,5 @@ workers:
92
92
  [Migration](../../MIGRATION.md) · [Worker contract](05-worker-contract.md) · [Recovery](10-recovery.md)
93
93
 
94
94
  启动前将 work.md 和 worker 脚本提交到所选 base,确保隔离工作树能读到同一份已确认范围;接受记录可以随后提交。
95
+
96
+ 主仓管理多个代码仓时,用 `buildbeat status --repo . --all-repos` 查看跨仓待批与未了结工作;可加 `--work <ID>` 或 `--json`。详见[跨会话接续](11-session-handoff.md)。
@@ -6,10 +6,21 @@ New work has one work.md. accept binds its current digest but does not start exe
6
6
 
7
7
  Status presents the transition, candidate, plan digest, evidence, reasons and next action. decide --action approve|reject must name the run and transition. Approval re-reads the candidate and frozen safeguards; changed subjects, dirty trees, missing evidence or unacceptable reviews cannot be stamped. Continue nonterminal decisions with run. Final approval means merge-ready only.
8
8
 
9
+ At the merge decision, `run --adopt`, `decide --action fix` and the approval command’s `--candidate` binding require runtime 4.2 or later.
10
+
11
+ When a problem is found at the merge decision (`enter-wait-merge`), repair it within the same Run and verify and review again:
12
+
13
+ - Manual repair: commit in the Run worktree, then execute `buildbeat run --config <config> --run <RUN> --adopt <sha> --by <name>`. The worktree must be clean, the SHA must be its actual HEAD and a new descendant of the current candidate, and changes must stay within `allowedPaths`. Resume at verify, review incrementally from the previous candidate, then wait for another merge decision.
14
+ - Return to the fixer: execute `buildbeat decide --repo <repo> --run <RUN> --action fix --reason <what to repair> --by <name>`, then continue with `buildbeat run --config <config> --run <RUN>`. This is available only at the merge decision with a configured fixer; otherwise commit a manual repair and adopt it. The reason becomes an accepted P1 in the Work findings account and is passed in full to the fixer.
15
+
16
+ After either repair path returns to the merge decision, read `buildbeat status --repo <repo> --run <RUN>` and copy its refreshed approval command: `buildbeat decide --repo <repo> --run <RUN> --action approve --transition enter-wait-merge --candidate <full SHA from the card> --by <name>`. The candidate is filled in by status, inbox and notifications. Approval with the old candidate, or without a candidate after repair, fails with `approval stale`; read the new card before deciding.
17
+
18
+ The repair decision answers the old request and preserves historical evidence; the merge check uses only evidence for the current candidate. Returning to fix does not spend a review round, grant budget or refund it. Subsequent reviews count normally. SUCCEEDED Runs cannot be reopened; active 3.x Runs still require their original runtime.
19
+
9
20
  Use decide --action accept|dismiss --work ... --fingerprint ... for findings. Exact-fingerprint dismissals persist; similarity detects nonconverging reviews and never substitutes for adjudication.
10
21
 
11
22
  Normal repairs continue automatically. Repeated issues, increased blockers, infrastructure failures and exhausted budgets create exception decisions. One budget grant covers its repair/reverification/review plan and can be replayed. The driving session cannot approve on behalf of the user without authorization.
12
23
 
13
- Merging and releasing stay human actions. After a release, run `buildbeat release --config <config> [--note <text>]`: it checks that the Work's succeeded candidate is contained in `--ref` (default: the current HEAD), runs the read-only readback command from the run config's `release:` section in the main checkout (same shape, environment allowlist and redaction as a worker), and appends the outcome, exit code, commit, output digest and a redacted tail to `delivery/work/<ID>/releases.jsonl`. Once the latest readback has passed, close the window with `decide --repo . --work <ID> --action close --result <text>`, which records a close-work decision bound to that readback; a failed readback cannot close it.
24
+ Merging and releasing stay human actions. After a release, run `buildbeat release --config <config> [--note <text>]`: it checks that the Work's succeeded candidate is contained in `--ref` (default: the current HEAD), runs the read-only readback command from the run config's `release:` section in the main checkout (same shape, environment allowlist and redaction as a worker), and appends the outcome, exit code, commit, output digest and a redacted tail to `delivery/work/<ID>/releases.jsonl`. Once the latest readback has passed, close the window with `decide --repo . --work <ID> --action close --result <text>`, which records a close-work decision bound to that readback; a failed readback cannot close it. A Work whose run configs have no `release:` section ends at the merge, and status says nothing is left to do.
14
25
 
15
26
  Notifications report waiting, termination and stalls; they do not accept approvals. Webhook/DingTalk URLs come only from environment variables, and delivery failure does not affect the run. The watcher is an internal feedback mechanism.
@@ -6,10 +6,21 @@
6
6
 
7
7
  status 的决定卡包含 transition、候选、计划摘要、证据、原因和下一步。decide --action approve|reject 必须指向该 Run 与转换。批准前重新回读候选与冻结校验;变化、脏树、缺证据或不合格审查不能盖章。非终态批准后用 run 续跑,最终批准只表示具备合并条件。
8
8
 
9
+ 合并决定点的 `run --adopt`、`decide --action fix` 与批准命令的 `--candidate` 候选绑定要求运行时 4.2 或更高版本。
10
+
11
+ 合并决定点(`enter-wait-merge`)发现问题时,可以在本 Run 中修复并重新验证、审查:
12
+
13
+ - 手工修复:在 Run 工作树提交后执行 `buildbeat run --config <config> --run <RUN> --adopt <sha> --by <name>`。工作树必须干净,SHA 必须是实际 HEAD,并且是当前候选的新后代;改动仍受 `allowedPaths` 约束。从 verify 继续,review 使用上一候选的增量范围,最后再次等待合并决定。
14
+ - 退回 fixer:执行 `buildbeat decide --repo <repo> --run <RUN> --action fix --reason <要修什么> --by <name>`,再用 `buildbeat run --config <config> --run <RUN>` 续跑。仅合并决定点可用,且配置必须有 fixer;否则先手修再接纳。原因记为本 Work 已接受的 P1 问题,fixer 收到完整原因。
15
+
16
+ 任一修复路径回到合并决定后,先读 `buildbeat status --repo <repo> --run <RUN>`,复制刷新后的批准命令:`buildbeat decide --repo <repo> --run <RUN> --action approve --transition enter-wait-merge --candidate <卡片中的完整 SHA> --by <name>`。状态卡、待批列表和通知会填好候选;旧候选命令或修复后未携带候选的命令会报 `approval stale`,需要重新读取卡片再决定。
17
+
18
+ 修复决定回答旧请求,保留历史证据;合并检查只使用当前候选的证据。退回本身不计 review、不扩额也不退款,后续 review 照常累计。已批准的 SUCCEEDED Run 不重新打开;3.x 活动 Run 仍由原运行时处理。
19
+
9
20
  审查问题通过 decide --action accept|dismiss --work ... --fingerprint ... 裁决。精确指纹的驳回持续生效;换说法的相似性只用于停止不收敛循环,不代替人的裁决。
10
21
 
11
22
  正常修复自动继续;重复问题、阻断数增加、环境故障或预算耗尽才形成例外决定。预算批准一次放行对应修复、重验和再审;扩额记录可以重放,驾驶会话不能自行冒充用户批准。
12
23
 
13
- 合并和上线仍由人完成。上线后运行 `buildbeat release --config <config> [--note <text>]`:它确认该 Work 已成功 Run 的候选包含在 `--ref`(默认当前 HEAD)中,在主检出执行 run-config `release:` 段配置的只读回读命令(形状、环境白名单与脱敏同 worker),把结果、退出码、提交、输出摘要和末尾若干行追加到 `delivery/work/<ID>/releases.jsonl`。最近一次回读通过后,用 `decide --repo . --work <ID> --action close --result <text>` 关窗,写入绑定该回读的 close-work 决定;回读失败时不能关窗。
24
+ 合并和上线仍由人完成。上线后运行 `buildbeat release --config <config> [--note <text>]`:它确认该 Work 已成功 Run 的候选包含在 `--ref`(默认当前 HEAD)中,在主检出执行 run-config `release:` 段配置的只读回读命令(形状、环境白名单与脱敏同 worker),把结果、退出码、提交、输出摘要和末尾若干行追加到 `delivery/work/<ID>/releases.jsonl`。最近一次回读通过后,用 `decide --repo . --work <ID> --action close --result <text>` 关窗,写入绑定该回读的 close-work 决定;回读失败时不能关窗。run 配置都没有 `release:` 段的 Work 到合并为止,status 提示没有剩余必做步骤。
14
25
 
15
26
  通知只报告待批、结束或疑似卡顿,不能接收审批;webhook/钉钉 URL 仅从环境变量读取,发送失败不影响 Run。看门进程是运行反馈的内部实现。
@@ -2,8 +2,12 @@
2
2
 
3
3
  [简体中文](10-recovery.md)
4
4
 
5
+ At the merge decision, `run --adopt`, `decide --action fix` and the approval command’s `--candidate` binding require runtime 4.2 or later.
6
+
5
7
  - run resumes the unique nonterminal run in its family; choose --run when ambiguous. Archived work needs explicit --new to start another attempt.
6
8
  - An interrupted step reruns itself from the ledger. Handle a dirty worktree first. After committing a manual fix, --adopt checks the actual HEAD and continues at verify.
9
+ - The merge decision also accepts a manual repair: the new HEAD must descend from the candidate and stay in scope. Alternatively use `decide --action fix --repo <repo> --run <RUN> --reason <what to repair> --by <name>` with a configured fixer, then `run --config <config> --run <RUN>`. Both paths verify and review again before another merge decision, preserving review budgets and historical evidence.
10
+ - After repair, read the refreshed status card and copy its approval command with `--candidate <full SHA>`; old or unbound approvals fail with `approval stale`.
7
11
  - Reclaim a lock only when its owner is on this host and provably dead. Keep live, foreign-host and unidentified locks. stop is not a process-kill command.
8
12
  - Timeouts, crashes, malformed envelopes and exit 75 are infra: no fixer and no failure-budget charge. Decide and continue once the environment recovers.
9
13
  - A corrupt ledger exposes only its valid prefix and refuses appends. history --verify replays and validates without silently repairing it. Preserve the scene and candidate.
@@ -2,8 +2,12 @@
2
2
 
3
3
  [English](10-recovery.en.md)
4
4
 
5
+ 合并决定点的 `run --adopt`、`decide --action fix` 与批准命令的 `--candidate` 候选绑定要求运行时 4.2 或更高版本。
6
+
5
7
  - run 自动续接已有家族的唯一未终态 Run;多个候选时必须 --run 指定。历史工作需明确 --new 才会重新启动。
6
8
  - 中断步按台账重跑自身;脏工作树先处理。手修并提交后用 --adopt 指定实际 HEAD,从 verify 继续。
9
+ - 合并决定点也可接纳手修:要求新 HEAD 是当前候选后代且改动不越界;也可 `decide --action fix --repo <repo> --run <RUN> --reason <要修什么> --by <name>` 退回已配置的 fixer,再 `run --config <config> --run <RUN>`。两者都会重验、再审并重新等待合并决定,review 预算与历史证据保留。
10
+ - 修复后重新读取状态卡,复制带 `--candidate <完整 SHA>` 的批准命令;旧候选或未绑定候选的批准会报 `approval stale`。
7
11
  - 只有同机且持有者进程已不存在的锁自动回收。活动、异机、缺持有者信息的锁保留;stop 不是杀进程命令。
8
12
  - 超时、崩溃、无效信封、exit 75 属于 infra,不派 fixer、不扣失败预算。环境恢复后按待决策对象处理并续跑。
9
13
  - 台账损坏只读取合法前缀,拒绝追加;history --verify 校验回放,不会静默修复。保留现场与候选后决定恢复方式。
@@ -6,6 +6,12 @@ Before closing a session, save the goal, decisions, unverified scope and next st
6
6
 
7
7
  A new session reads AGENTS.md, work.md (or legacy intent/plan), decisions and status. Verify available facts and reuse still-valid authorization. A new conversation is not permission to start a duplicate run.
8
8
 
9
+ For a main repository managing several code repositories, start with `buildbeat status --repo . --all-repos`. This reads the main checkout, targets named by `repo:` in its `delivery/work/` run configurations, and immediate child Git checkouts containing `delivery/work/`. Discovery deduplicates real paths and never recurses. Missing or invalid targets and unreadable configurations produce warnings without hiding other repositories.
10
+
11
+ Pending decisions come first, followed by open work grouped by repository. CLOSED, CANCELLED and merged work whose configuration has no `release:` are counted only. Merged work with `release:` remains visible until closed. `--json` returns `{repos: [{repo, works, settled}], pending, warnings}` including settled work. `--work <ID>` filters the merged results; `--run` cannot be combined with `--all-repos`.
12
+
13
+ When a configured target has the matching work directory or a runtime ledger, its state, findings and decisions are authoritative and the work appears only under that repository. Suggested commands retain the main repository's configuration and use paths relative to the caller; fill in person/reason placeholders before executing. Work without target records remains under the main repository with its runtime target shown. Single-repository `status` also reads this one level of target state, preventing duplicate-start suggestions.
14
+
9
15
  Changing tools requires command, authentication and output-contract configuration. Records remain reusable; execution capability needs validation. Handoffs between people require candidate, records, access and a named next owner/action.
10
16
 
11
17
  Git transfers long-lived facts between machines. Active processes, runtime files, worktrees and host resources do not migrate with clone. Inspect the original environment before choosing how to continue.
@@ -6,6 +6,12 @@
6
6
 
7
7
  新会话读取项目 AGENTS.md、Work 的 work.md(或旧 intent/plan)、决定与 status。能从记录查到的事实自行核对,沿用仍有效的授权;不把开新会话等同于重开 Run。
8
8
 
9
+ 主仓管理多个代码仓时,先运行 `buildbeat status --repo . --all-repos`。它只读查看主仓、主仓 `delivery/work/` 下 run 配置的 `repo:` 目标仓,以及主仓下一层含 `delivery/work/` 的 Git 仓,按真实路径去重,不递归。不存在或无效的目标、读不了的配置会给出警告,其余仓继续显示。
10
+
11
+ 待人决定先列出,随后按仓显示未了结工作;CLOSED、CANCELLED 和已合并且配置没有 `release:` 的工作只计数。已合并但配有 `release:`、尚未关窗的工作仍显示。`--json` 返回 `{repos: [{repo, works, settled}], pending, warnings}`,包含已了结工作;`--work <ID>` 筛选各仓合并后的这项工作,`--run` 不能与 `--all-repos` 同用。
12
+
13
+ 主仓配置指向的代码仓已有同名工作目录或运行台账时,状态、审查问题和决定取自代码仓,只在代码仓下显示;提示命令保留主仓配置,仓路径和配置路径相对当前目录,可填入人名、理由等占位内容后执行。目标仓还没有记录时,工作留在主仓并注明运行目标。不加 `--all-repos` 的主仓 `status` 也会读取这一层目标仓的真实状态,避免提示重复开跑。
14
+
9
15
  换工具时重新配置命令、鉴权和输出合同;接手事实可以复用,具体工具执行能力需要验证。换人时同步候选、记录和权限,说明当前推进人及下一步。
10
16
 
11
17
  换机器只能通过 Git 同步长期事实。活动进程、运行时文件、工作树和宿主资源不会随 clone 迁移,先核对原环境再选择接手方式。
@@ -1,5 +1,5 @@
1
1
  # 简账 · WORK-EXPORT-DATE-FILTER 的 run 配置。路径相对本文件解析。
2
- # 起跑前:buildbeat doctor --config <本文件>;起跑:buildbeat start --config <本文件> --attempt new
2
+ # 起跑前:buildbeat check --config <本文件>;起跑:buildbeat run --config <本文件>
3
3
  repo: ../../..
4
4
  work: WORK-EXPORT-DATE-FILTER
5
5
  run: RUN-EXPORT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haiyangbg/buildbeat",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "description": "BuildBeat: Git-based AI delivery across models, tools, sessions, and people, with context in project files, build-test-review-fix loops, and human decisions.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/v2/cli/run.js CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  import { execFileSync, spawn, spawnSync } from "node:child_process";
13
13
  import { createHash } from "node:crypto";
14
- import { existsSync, readdirSync, readFileSync } from "node:fs";
14
+ import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
15
15
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
16
16
  import { fileURLToPath } from "node:url";
17
17
 
@@ -32,6 +32,7 @@ import {
32
32
  approveRun,
33
33
  listInbox,
34
34
  rejectRun,
35
+ requestFix,
35
36
  } from "../runtime/decisions.js";
36
37
  import { checkRequires } from "../runtime/env-contract.js";
37
38
  import {
@@ -45,10 +46,12 @@ import { applyGc, planGc } from "../runtime/gc.js";
45
46
  import {
46
47
  artifactStatus,
47
48
  computeOverview,
49
+ computeRepositoryOverview,
48
50
  readJsonl,
49
51
  renderOverview,
50
52
  runsFor,
51
53
  } from "../runtime/overview.js";
54
+ import { configTargets, workConfigs, pathLabel, shellArg } from "../runtime/overview-repos.js";
52
55
  import { closeWork, runReadback } from "../runtime/release.js";
53
56
  import {
54
57
  DEFAULT_STALL_AFTER_MS,
@@ -94,8 +97,9 @@ Usage:
94
97
  buildbeat --version
95
98
  buildbeat accept --repo <path> --work <WORK-ID> [--by <name>]
96
99
  buildbeat run --config <run-config.yaml> [--run <RUN-ID>] [--new] [--adopt <sha> --by <name>]
97
- buildbeat status --repo <path> [--work <WORK-ID> | --run <RUN-ID>] [--json]
98
- buildbeat decide --repo <path> --run <RUN-ID> --action approve|reject --transition <t> [--by <name>] [--reason <text>]
100
+ buildbeat status --repo <path> [--work <WORK-ID> | --run <RUN-ID>] [--all-repos] [--json]
101
+ buildbeat decide --repo <path> --run <RUN-ID> --action approve|reject --transition <t> [--candidate <sha>] [--by <name>] [--reason <text>]
102
+ buildbeat decide --repo <path> --run <RUN-ID> --action fix --reason <text> [--by <name>]
99
103
  buildbeat decide --repo <path> --work <WORK-ID> --action accept|dismiss --fingerprint <fp> [--by <name>] [--note <text>]
100
104
  buildbeat release --config <run-config.yaml> [--ref <ref>] [--note <text>]
101
105
  buildbeat decide --repo <path> --work <WORK-ID> --action close --result <text> [--by <name>]
@@ -110,7 +114,7 @@ check --step executes that worker in the main checkout, outside the evidence led
110
114
 
111
115
  // Switches may stand alone (`--json`) or take an explicit true/false
112
116
  // (`--json true`, the older spelling); every other flag needs a value.
113
- const SWITCHES = new Set(["json", "apply", "force", "once", "new", "verify"]);
117
+ const SWITCHES = new Set(["json", "apply", "force", "once", "new", "verify", "all-repos"]);
114
118
 
115
119
  function parseFlags(argv) {
116
120
  const flags = {};
@@ -177,8 +181,8 @@ function repoLabelFor(repoRoot, typed) {
177
181
  return rel;
178
182
  }
179
183
 
180
- function printNextReply(repoLabel, state) {
181
- const lines = nextReply({ repoLabel, state });
184
+ function printNextReply(repoLabel, state, repoRoot) {
185
+ const lines = nextReply({ repoLabel, state, repoRoot });
182
186
  if (lines.length === 0) {
183
187
  return;
184
188
  }
@@ -287,7 +291,7 @@ function printState(state, ledger, view = {}) {
287
291
  console.log(` reason: ${reason}`);
288
292
  }
289
293
  if (view.repoLabel) {
290
- printNextReply(view.repoLabel, state);
294
+ printNextReply(view.repoLabel, state, view.repoRoot);
291
295
  }
292
296
  }
293
297
  if (state.terminal) {
@@ -588,7 +592,7 @@ async function notifyForState(repoRoot, repoLabel, state) {
588
592
  }
589
593
  const results = await dispatchNotification(
590
594
  config,
591
- buildNotification(kind, { repoLabel, state }),
595
+ buildNotification(kind, { repoLabel, state, repoRoot }),
592
596
  { repoRoot },
593
597
  );
594
598
  for (const row of results) {
@@ -913,6 +917,7 @@ async function commandResume(
913
917
  sha: flags.adopt,
914
918
  by: flags.by ?? "human",
915
919
  resumeAt,
920
+ allowedPaths: options.allowedPaths,
916
921
  });
917
922
  console.log(
918
923
  `adopted ${adopted.adopted} as candidate (${adopted.decisionRef}, answers ${adopted.transition}); resuming at ${adopted.resumeAt}`,
@@ -971,6 +976,7 @@ function commandInbox(flags, rows = null) {
971
976
  );
972
977
  let lastWork = null;
973
978
  for (const row of sorted) {
979
+ const rowRepo = row.repo ?? repoRoot;
974
980
  if (row.corrupted) {
975
981
  console.log(
976
982
  `${row.run}: LEDGER CORRUPTED after seq=${row.corrupted.afterSeq} (${row.corrupted.reason})`,
@@ -981,7 +987,7 @@ function commandInbox(flags, rows = null) {
981
987
  console.log(`work ${row.work}:`);
982
988
  lastWork = row.work;
983
989
  }
984
- const ledger = EventLedger.open(ledgerPathFor(repoRoot, row.run));
990
+ const ledger = EventLedger.open(ledgerPathFor(rowRepo, row.run));
985
991
  const requested = [...ledger.events]
986
992
  .reverse()
987
993
  .find((event) => event.type === "HUMAN_REQUESTED");
@@ -1004,10 +1010,11 @@ function commandInbox(flags, rows = null) {
1004
1010
  console.log(` reason: ${reason}`);
1005
1011
  }
1006
1012
  for (const line of nextReply({
1007
- repoLabel: repoLabelFor(repoRoot, flags.repo),
1013
+ repoLabel: row.repo ? shellArg(pathLabel(process.cwd(), rowRepo)) : repoLabelFor(repoRoot, flags.repo),
1008
1014
  state: ledger.state,
1015
+ repoRoot: rowRepo,
1009
1016
  })) {
1010
- console.log(` next: ${line}`);
1017
+ console.log(` next: ${row.config ? line.replaceAll("--config <run-config.yaml>", `--config ${shellArg(pathLabel(process.cwd(), row.config))}`) : line}`);
1011
1018
  }
1012
1019
  }
1013
1020
  }
@@ -1030,6 +1037,7 @@ function commandApprove(flags) {
1030
1037
  const result = approveRun(resolve(flags.repo), flags.run, {
1031
1038
  by: flags.by ?? "human",
1032
1039
  transition: flags.transition,
1040
+ candidate: flags.candidate,
1033
1041
  policies,
1034
1042
  });
1035
1043
  if (!result.approved) {
@@ -1257,7 +1265,7 @@ function commandDoctor(flags) {
1257
1265
  console.log(`notify: PROBLEM ${notifyError}`);
1258
1266
  } else if (!notify) {
1259
1267
  console.log(
1260
- `notify: none (${NOTIFY_CONFIG} absent; a waiting run reaches nobody until someone runs inbox)`,
1268
+ `notify: none (${NOTIFY_CONFIG} absent; a waiting run reaches nobody until someone runs status)`,
1261
1269
  );
1262
1270
  } else {
1263
1271
  console.log("notify channels:");
@@ -1455,30 +1463,57 @@ function commandMetrics(flags) {
1455
1463
  function commandStatus(flags) {
1456
1464
  if (!flags.repo) throw new Error("status requires --repo");
1457
1465
  const repoRoot = resolve(flags.repo);
1466
+ if (flags["all-repos"] === "true" && flags.run)
1467
+ throw new Error("status: --all-repos cannot be used with --run; use --work instead");
1458
1468
  if (flags.run && flags.work)
1459
1469
  throw new Error("status: choose --run or --work");
1460
1470
  if (!flags.run) {
1461
- const rows = computeOverview(repoRoot, {
1462
- work: flags.work ?? null,
1463
- repoLabel: repoLabelFor(repoRoot, flags.repo),
1464
- });
1465
- const pending = pendingFor(repoRoot, flags.work);
1466
- if (flags.json === "true")
1467
- console.log(
1468
- JSON.stringify(
1469
- {
1470
- works: rows,
1471
- pending,
1472
- metrics: flags.work ? null : computeMetrics(repoRoot),
1473
- },
1474
- null,
1475
- 2,
1476
- ),
1477
- );
1478
- else {
1479
- console.log(renderOverview(rows));
1480
- if (pending.length) commandInbox(flags, pending);
1481
- if (!flags.work) console.log(renderMetrics(computeMetrics(repoRoot)));
1471
+ const allRepos = flags["all-repos"] === "true";
1472
+ const result = computeRepositoryOverview(repoRoot, { work: flags.work ?? null, allRepos, localRepoLabel: repoLabelFor(repoRoot, flags.repo) });
1473
+ const rows = result.repos.flatMap((group) => group.works);
1474
+ if (flags.json === "true") {
1475
+ console.log(JSON.stringify(allRepos ? result : {
1476
+ works: rows.map((row) => {
1477
+ if (row.repo !== result.repos[0].repo || row.targetRepo) return row;
1478
+ const { config, hasRelease, repo, repoLabel, settled, ...local } = row;
1479
+ return local;
1480
+ }),
1481
+ pending: result.pending.map((row) => {
1482
+ if (row.repo !== result.repos[0].repo) return row;
1483
+ const { repo, config, ...local } = row;
1484
+ return local;
1485
+ }),
1486
+ metrics: flags.work ? null : computeMetrics(repoRoot),
1487
+ ...(result.warnings.length ? { warnings: result.warnings } : {}),
1488
+ }, null, 2));
1489
+ } else {
1490
+ for (const warning of result.warnings) console.log(`warning: ${warning}`);
1491
+ if (allRepos) {
1492
+ for (const group of result.repos) {
1493
+ const pending = result.pending.filter((row) => row.repo === group.repo);
1494
+ if (pending.length) {
1495
+ console.log(`pending decisions · ${pathLabel(process.cwd(), group.repo)}`);
1496
+ commandInbox(flags, pending);
1497
+ }
1498
+ }
1499
+ for (const group of result.repos) {
1500
+ const open = group.works.filter((row) => !row.settled);
1501
+ if (open.length) {
1502
+ console.log(`repo ${pathLabel(process.cwd(), group.repo)}:`);
1503
+ console.log(renderOverview(open));
1504
+ }
1505
+ }
1506
+ for (const group of result.repos) {
1507
+ const count = group.settled;
1508
+ console.log(`${pathLabel(process.cwd(), group.repo)}: settled ${count.total} (closed ${count.closed} · cancelled ${count.cancelled} · merged, no release step ${count.merged})`);
1509
+ }
1510
+ } else {
1511
+ // Keep the single-repo surface while forwarding work facts and waits.
1512
+ console.log(renderOverview(rows.map((row) => row.repo === result.repos[0].repo
1513
+ ? { ...row, repo: undefined } : row)));
1514
+ if (result.pending.length) commandInbox(flags, result.pending);
1515
+ if (!flags.work) console.log(renderMetrics(computeMetrics(repoRoot)));
1516
+ }
1482
1517
  }
1483
1518
  return;
1484
1519
  }
@@ -1598,6 +1633,28 @@ function commandDecide(flags) {
1598
1633
  }
1599
1634
  if (flags.action === "approve") return commandApprove(flags);
1600
1635
  if (flags.action === "reject") return commandReject(flags);
1636
+ if (flags.action === "fix") {
1637
+ if (!flags.repo || !flags.run) throw new Error("fix requires --repo and --run");
1638
+ const repoRoot = resolve(flags.repo);
1639
+ const prior = EventLedger.open(ledgerPathFor(repoRoot, flags.run));
1640
+ let hasFixer;
1641
+ // Older 4.x ledgers did not freeze repair capabilities. Discover a
1642
+ // single matching configuration; never guess between several configs.
1643
+ if (prior.state.run?.deliveryChecks && !prior.state.run.repair) {
1644
+ const configs = configTargets(workConfigs(repoRoot, prior.state.run.work), []);
1645
+ const candidates = configs.filter((item) => item.repo === realpathSync(repoRoot)).map((item) =>
1646
+ loadRunConfig({ config: item.path }, "resume")).filter((options) =>
1647
+ inRunFamily(options.runId, flags.run));
1648
+ if (candidates.length !== 1) throw new Error("cannot identify the run configuration; use manual repair and run --adopt");
1649
+ assertRunConfiguration(prior.state, candidates[0]);
1650
+ hasFixer = Boolean(candidates[0].adapters.fixer);
1651
+ }
1652
+ const result = requestFix(repoRoot, flags.run, {
1653
+ by: flags.by ?? "human", reason: flags.reason, hasFixer,
1654
+ });
1655
+ console.log(`repair requested as ${result.decisionRef}; continue with: buildbeat run --config <run-config.yaml> --run ${flags.run}`);
1656
+ return;
1657
+ }
1601
1658
  if (["accept", "dismiss"].includes(flags.action) && flags.fingerprint) {
1602
1659
  if (!flags.repo || !flags.work)
1603
1660
  throw new Error("finding decisions require --repo and --work");
@@ -1611,7 +1668,7 @@ function commandDecide(flags) {
1611
1668
  return;
1612
1669
  }
1613
1670
  throw new Error(
1614
- "decide requires --action approve|reject, accept|dismiss with --work and --fingerprint, or close with --work and --result",
1671
+ "decide requires --action approve|reject|fix, accept|dismiss with --work and --fingerprint, or close with --work and --result",
1615
1672
  );
1616
1673
  }
1617
1674
 
@@ -48,7 +48,7 @@ export const EVIDENCE_STATUSES = ["passed", "failed", "unverified"];
48
48
 
49
49
  export const EVIDENCE_GRADES = ["L0", "L1", "L2", "L3", "L4"];
50
50
 
51
- export const DECISION_VALUES = ["approved", "rejected"];
51
+ export const DECISION_VALUES = ["approved", "rejected", "fix"];
52
52
 
53
53
  // observe v0 per docs/v2/RFC-0003-workflow-policy.md §8 (frozen shape):
54
54
  // bands are exactly three fixed levels; triage actions are a closed set.
@@ -71,6 +71,7 @@ export function applyEvent(state, event) {
71
71
  planDigest: data.planDigest ?? "UNVERIFIED",
72
72
  intentDigest: data.intentDigest ?? "UNVERIFIED",
73
73
  ...(data.deliveryChecks ? { deliveryChecks: data.deliveryChecks } : {}),
74
+ ...(data.repair ? { repair: data.repair } : {}),
74
75
  };
75
76
  break;
76
77
  }
@@ -221,7 +222,7 @@ export function applyEvent(state, event) {
221
222
  subject: data.subject,
222
223
  decisionRef: data.decisionRef,
223
224
  });
224
- if (data.decision === "approved") {
225
+ if (data.decision === "approved" || data.decision === "fix") {
225
226
  next.approvals.push({
226
227
  decisionRef: data.decisionRef,
227
228
  transition: data.transition,