@haiyangbg/buildbeat 2.0.0-beta.3 → 2.0.0-beta.5
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 +47 -7
- package/SKILL.md +86 -2
- package/docs/CLI-PILOT-2026-08-23.md +1 -1
- package/docs/CLI.md +1 -1
- package/docs/EXECUTION-PLAN.md +2 -2
- package/docs/PHASE2-PILOT-PREFLIGHT-2026-08-25.md +2 -2
- package/docs/PHASE4-V1.20-PILOT-2026-08-25.md +2 -2
- package/docs/RELEASING.md +1 -1
- package/docs/V2-D2-DECISION-CARD.md +2 -2
- package/docs/V2-DECISIONS.md +2 -2
- package/docs/V2-ITERATION-01.md +13 -13
- package/docs/V2-ITERATION-06.md +2 -2
- package/docs/V2-ITERATION-08.md +62 -0
- package/docs/V2-PLAN.md +6 -6
- package/docs/V2-PROPOSAL.md +2 -2
- package/docs/V2.0.0-BETA.1-RELEASE-EVIDENCE-2026-08-28.md +1 -1
- package/docs/V2.0.0-BETA.2-RELEASE-EVIDENCE-2026-08-28.md +1 -1
- package/docs/V2.0.0-BETA.3-RELEASE-EVIDENCE-2026-09-01.md +8 -0
- package/docs/V2.0.0-BETA.4-RELEASE-EVIDENCE-2026-09-03.md +9 -0
- package/docs/v2/M4-EXTERNAL-PILOT-2026-08-28.md +11 -11
- package/docs/v2/{M4-CHICKAI-PILOT-2026-08-28.md → M4-PILOT-APP-2026-08-28.md} +4 -4
- package/docs/v2/M4-SELFHOST-2026-08-28.md +1 -1
- package/docs/v2/RFC-0001-product-definition.md +2 -2
- package/docs/v2/SPEC-0001-events-v1.md +7 -6
- package/docs/v2/guide/00-how-to-talk.md +57 -0
- package/docs/v2/guide/01-quickstart.md +4 -0
- package/docs/v2/guide/02-workflow-guide.md +48 -1
- package/docs/v2/guide/04-adapter-guide.md +4 -0
- package/docs/v2/guide/05-worker-contract.md +10 -0
- package/docs/v2/guide/06-evidence-guide.md +4 -0
- package/docs/v2/guide/07-approval-guide.md +47 -0
- package/docs/v2/guide/10-recovery.md +25 -3
- package/docs/v2/guide/README.md +3 -0
- package/example/.buildbeat/manifest.json +1 -1
- package/lessons.md +37 -0
- package/package.json +1 -1
- package/src/v2/adapters/mock.js +9 -2
- package/src/v2/adapters/shell.js +87 -14
- package/src/v2/cli/run.js +607 -30
- package/src/v2/domain/event-registry.js +1 -0
- package/src/v2/engine/reducer.js +29 -1
- package/src/v2/engine/workflow.js +8 -1
- package/src/v2/evidence/collector.js +14 -3
- package/src/v2/presets/release-readback.yaml +36 -0
- package/src/v2/presets/risk/release.yaml +21 -0
- package/src/v2/runtime/cache.js +124 -0
- package/src/v2/runtime/decisions.js +80 -0
- package/src/v2/runtime/env-contract.js +35 -1
- package/src/v2/runtime/envelope.js +183 -0
- package/src/v2/runtime/gc.js +182 -0
- package/src/v2/runtime/liveness.js +193 -0
- package/src/v2/runtime/metrics.js +8 -0
- package/src/v2/runtime/notify.js +223 -0
- package/src/v2/runtime/orchestrator.js +267 -19
- package/src/v2/runtime/overview.js +301 -0
- package/src/v2/runtime/run-record.js +3 -0
- package/src/v2/runtime/work-cost.js +147 -0
- package/src/v2/workspace/workspace-manager.js +14 -1
- package/templates/contracts/PROTOCOL.md +4 -0
- package/templates/gitignore.template +5 -0
- package/templates/scripts/bus-check.sh +37 -12
- package/templates/v2/AGENTS.md +73 -0
- package/templates/v2//346/214/207/346/214/245/345/217/260.md +36 -0
package/docs/V2-PROPOSAL.md
CHANGED
|
@@ -254,7 +254,7 @@ gates:
|
|
|
254
254
|
| WP1.3 | human Gate 异步审批:`.pending` 工件 + `buildbeat approve` + `inbox` | 审批落 decisions.md,闭环等文件恢复流转 |
|
|
255
255
|
| WP1.4 | `buildbeat watch`:监听工件事件自动触发下一步 | 真实项目从 intent.md 提交到 merge 候选,人仅两次介入 |
|
|
256
256
|
| WP1.5 | `buildbeat status` 派生视图 v0 | 与手工盘点一致,无手写状态文件 |
|
|
257
|
-
| WP1.6 |
|
|
257
|
+
| WP1.6 | 真实项目试点(建议从一个真实企业工作区里选一个单仓小项目) | 试点记录入库(沿用 v1 的 PILOT 文档传统) |
|
|
258
258
|
|
|
259
259
|
**止损**:试点显示 loop 开销 > 收益(solo 小项目场景),则 Runner 降级为"半自动"——只做 `run --stage` 单步 + inbox,watch 缓建;协议层成果不受影响。
|
|
260
260
|
|
|
@@ -312,7 +312,7 @@ gates:
|
|
|
312
312
|
| D2 | Runner 载体 | **扩展现有 `@haiyangbg/buildbeat` CLI**(复用 Node 地基与发布链) | 新仓另起:边界干净但分裂维护面 |
|
|
313
313
|
| D3 | 首个 agent adapter | **按你日常主力工具定**(cursor-agent 或 claude -p) | 双 adapter 齐发验证接口普适性,成本 +30% |
|
|
314
314
|
| D4 | v1 手写状态废除节奏 | **v2 新项目直接无手写状态**;v1 项目迁移时一步到位 | 过渡期双轨(手写+派生并存)——强烈不建议,等于自造 SSOT 腐烂 |
|
|
315
|
-
| D5 | Phase 1 试点项目 |
|
|
315
|
+
| D5 | Phase 1 试点项目 | 一个真实企业工作区内选一个单仓、有测试、迭代活跃的小项目 | 用 BuildBeat 仓库自举:戏剧性强但元问题(用未验证的引擎改引擎)风险高 |
|
|
316
316
|
|
|
317
317
|
---
|
|
318
318
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# v2.0.0-beta.1 发布证据(2026-08-28)
|
|
2
2
|
|
|
3
|
-
> 授权:所有者
|
|
3
|
+
> 授权:所有者 于 solobaton 会话逐步授权("发布吧,授权也一起" = npm publish + `v2` 分支推送);执行 = 受托会话。
|
|
4
4
|
> 渠道:**prerelease → dist-tag `next`**;`latest` 保持 `1.21.0` 不动(v1 CLI 与脚手架冻结随包分发)。
|
|
5
5
|
|
|
6
6
|
## 候选
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 授权:所有者会话内 "4. OK"(beta.2 发布 + 部署审批);流程与 [beta.1](V2.0.0-BETA.1-RELEASE-EVIDENCE-2026-08-28.md) 完全一致。
|
|
4
4
|
|
|
5
|
-
- 内容:单一内核修复——范围门中文路径误拦(git quotepath 转义;
|
|
5
|
+
- 内容:单一内核修复——范围门中文路径误拦(git quotepath 转义;meta 试点仓 迁移试点真实事故 `RUN-META-V2-01`),修复 + 中文路径永久回归(`3f39189`)。
|
|
6
6
|
- 候选:`d7a9ab9`(`v2` tip),tag `v2.0.0-beta.2`;本地 prepublishOnly 全链 `GATE_EXIT=0`(node 143/143);CI 两个 run(`3f39189` / `d7a9ab9`)conclusion=success。
|
|
7
7
|
- 发布:workflow run [33175013599](https://github.com/HaiYangBG1/BuildBeat/actions/runs/33175013599) 双 job success(publish + verify:exact integrity、dist-tag 路由、provenance、隔离安装、签名审计)。
|
|
8
8
|
- 本地独立回读:dist-tags `{bootstrap: 0.0.0, latest: 1.21.0, next: 2.0.0-beta.2}`(`latest` 未动);`dist.integrity = sha512-Lt90fCFnNCvnMJK/cs/lUrau4uESf5iDCmy9GFYD6sFdGSHbwlcOM4afBlziHTuoz3rVKpgw8b/+bP+bjarh6w==`;provenance `https://slsa.dev/provenance/v1`;所有者机器全局安装已切换为该官方工件(quotepath 修复在包内核验)。
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# v2.0.0-beta.3 发布证据(2026-09-01)
|
|
2
|
+
|
|
3
|
+
> 授权:所有者会话内「发」(beta.3 发布 + 部署审批);流程与 [beta.1](V2.0.0-BETA.1-RELEASE-EVIDENCE-2026-08-28.md)/[beta.2](V2.0.0-BETA.2-RELEASE-EVIDENCE-2026-08-28.md) 完全一致。
|
|
4
|
+
|
|
5
|
+
- 内容:三十轮部署战役(meta 试点仓 `WORK-PILOT-DEPLOY-01`,DEPLOY-01~30 + L4 之夜)的机制回灌——发现分诊门(`reviewTriage: required`)、裁决台账与锚定审查(`review-findings.jsonl` + `findings` 命令)、环境契约(`requires:`)、review 预算封顶 2 轮(预设原生)、`preflight` 预检通道、崩溃恢复改重跑中断步(`f6d59c5`+`0030e77`+`4b1b466`);另含战役期所有者会话的 4 个内核修复(预算守卫 `05e535c`、porcelain 列 `eb965b9`、证据引用相对化 `ef176d5`、打包启动器测试 `ab267bd`)与 Node<20 守卫(`a3c176a`)。
|
|
6
|
+
- 候选:`02d1f5a`(`v2` tip),tag `v2.0.0-beta.3`;本地全量门 node 164/164 + docs 检查全过;CI run(`4b1b466`)conclusion=success(`0030e77` 上的唯一红项为测试自身平台差异——GNU `true --version` 在 Linux 打印版本号——已以 `4b1b466` 修复)。
|
|
7
|
+
- 发布:workflow run [33460544343](https://github.com/HaiYangBG1/BuildBeat/actions/runs/33460544343) 双 job success(publish + verify:exact integrity、dist-tag 路由、provenance、隔离安装、签名审计);npm-publish 环境审批以所有者 gh 凭据执行。
|
|
8
|
+
- 本地独立回读(直连 registry.npmjs.org,不经镜像):dist-tags `{bootstrap: 0.0.0, latest: 1.21.0, next: 2.0.0-beta.3}`(`latest` 未动);`dist.integrity = sha512-K9H/nkK8YTuWwU3u/TcwTXpO/RnAYiChskWBlDyh2TG6DBRtibOk3KH4VxPQdkdOtcUGXm6N+MoEfNH8autw8w==`;`npm audit signatures` = registry signature + attestation 双 verified;隔离安装 smoke:`buildbeat-v2` usage 含 `preflight`/`findings` 新命令、`buildbeat --version` = 2.0.0-beta.3、`src/v2/runtime/findings.js`+`env-contract.js` 在包内。
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# v2.0.0-beta.4 发布证据(2026-09-03)
|
|
2
|
+
|
|
3
|
+
> 授权:所有者会话内「发布 beta.4 吧,授权也一起」(发布 + `npm-publish` 环境审批);流程与 [beta.3](V2.0.0-BETA.3-RELEASE-EVIDENCE-2026-09-01.md) 完全一致。
|
|
4
|
+
|
|
5
|
+
- 内容:迭代 08「等待要能找到人」——运行中可见性(实时输出、每步耗时/历史中位数、STALLED)、同 Work 新 Run 作废旧等待、`gc`、通知出站(`.buildbeat/notify.yaml` + `watch`)、`overview` Work 级总览、信封一等公民(`envelope:` / `--attempt new` / `redact:`)、verify 复用与增量审查、`release-readback` 上线回读车道、`requires:` 探针、可见命名进决策卡;Skill §0.5 驾驶手册 + `templates/v2/`;指南第 0 篇「怎么和会话说话」;仓库脱敏。明细见 [`CHANGELOG.md`](../CHANGELOG.md)、[`V2-ITERATION-08.md`](V2-ITERATION-08.md)。
|
|
6
|
+
- 候选:`6854b20`(`v2` tip),annotated tag `v2.0.0-beta.4`;本地候选门全过:`bash -n` / shellcheck / actionlint、`check:docs`(169 文件)、`test-scripts` 222 断言、plugin 7 断言、skill-only、pilot 15、node 182/182、`npm publish --dry-run`、gitleaks 无泄漏、`git diff --check`;CI run [33728721638](https://github.com/HaiYangBG1/BuildBeat/actions/runs/33728721638) success。发布前回读:registry 无 `2.0.0-beta.4`;tag ruleset `Protect release tags` = active、`refs/tags/v*`、仅 update/deletion、无 bypass。
|
|
7
|
+
- 发布:workflow run [33728863042](https://github.com/HaiYangBG1/BuildBeat/actions/runs/33728863042)(`workflow_dispatch` from `v2`,tag `v2.0.0-beta.4`)双 job success(`Publish v2.0.0-beta.4 to npm` + `Verify v2.0.0-beta.4 from npm`);`npm-publish` 环境审批按会话授权以所有者 gh 凭据落章,备注引用授权原文。
|
|
8
|
+
- 本地独立回读(直连 registry.npmjs.org,不经镜像):dist-tags `{bootstrap: 0.0.0, latest: 1.21.0, next: 2.0.0-beta.4}`(`latest` 未动);`dist.integrity = sha512-vjcJ4qzrBY1AFIiXuOpS/HvzaqfbtG3L5jmNxU1V8DNBydq3kOufjBBitvM067iP6O/kyThqcoekH4dWsBjb4Q==`;`dist.attestations.url` 存在;隔离前缀安装 `buildbeat --version = 2.0.0-beta.4`,`buildbeat-v2` usage 含 `overview` / `gc` / `watch`,`src/v2/runtime/` 含 `cache / envelope / gc / liveness / notify / overview`,包内含 `docs/v2/guide/00-how-to-talk.md`;`npm audit signatures` = 1 package has a verified attestation;`buildbeat doctor <空目录> --json` 返回有界 JSON(`cliVersion 2.0.0-beta.4`,`not-installed`),零写入。
|
|
9
|
+
- 未做:`latest` 仍指 v1.21.0(v2 仍是预发布);GitHub Release 页未建(与前几个 beta 一致,npm 面为准)。
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
# M4 外部试点证据:
|
|
1
|
+
# M4 外部试点证据:pilot-auth 数仓 CLI 标识重命名(RUN-PILOT-EXT-01)
|
|
2
2
|
|
|
3
3
|
> 日期:2026-08-28
|
|
4
|
-
>
|
|
5
|
-
> 任务:项目所有者点名的真实需求 `LXJ-AUTH-
|
|
4
|
+
> 项目:`<试点工作区>/pilot-backend`(真实业务单仓:Java 多模块 + Node portal 测试 + 真实远端)
|
|
5
|
+
> 任务:项目所有者点名的真实需求 `LXJ-AUTH-PILOT-EXT-01`——数仓 CLI 公有客户端标识 `client-b-old` 精确重命名为 `client-b`(meta 仓 `pm/decisions.md` 当日拍板行)
|
|
6
6
|
> 结论上限:本地候选 + 本地真实测试;不含生产切换(提案 §4 硬门未授权)。**Run 停在合并决定,等待项目所有者。**
|
|
7
7
|
|
|
8
8
|
## 1. 为什么这个试点有分量
|
|
9
9
|
|
|
10
|
-
同一需求今天早些时候已被**人工方式**做过一遍:候选散落在两个仓的未提交工作树里,与无关改动混杂,当日人工 L3 证据自记"没有 clean candidate hash,不满足 review-ready"。本 Run 从**已提交干净基线** `a99d2ad1`(仍是 `
|
|
10
|
+
同一需求今天早些时候已被**人工方式**做过一遍:候选散落在两个仓的未提交工作树里,与无关改动混杂,当日人工 L3 证据自记"没有 clean candidate hash,不满足 review-ready"。本 Run 从**已提交干净基线** `a99d2ad1`(仍是 `client-b-old`)出发,由 v2 Runner 驱动真实 Agent 独立重做,产出可审查的干净 candidate——这正是 M-1 卡点(人是节拍器、无干净候选)的正面对照。人工候选未被触碰,不倒算、不回放。
|
|
11
11
|
|
|
12
12
|
## 2. 流程事实(5.2 分钟全自动到合并决定)
|
|
13
13
|
|
|
14
14
|
| 事实 | 值 |
|
|
15
15
|
|---|---|
|
|
16
16
|
| Workers | **codex CLI 经 Shell Adapter**(厂商中立实证:Claude CLI 未登录不可用,换 codex 零运行时改动,裁决 #5) |
|
|
17
|
-
| builder | `codex exec -s workspace-write`(沙箱内只改文件;commit 由包装脚本机械执行);15 文件 +68/−33,**全部在 `
|
|
18
|
-
| verify | `test-jdk17.sh`(Surefire 全量)+ portal node 测试 + 验收 grep(`
|
|
17
|
+
| builder | `codex exec -s workspace-write`(沙箱内只改文件;commit 由包装脚本机械执行);15 文件 +68/−33,**全部在 `pilot-auth/` 内**(allowedPaths 强制) |
|
|
18
|
+
| verify | `test-jdk17.sh`(Surefire 全量)+ portal node 测试 + 验收 grep(`client-b-old` 零残留、`client-b` 在册)——一次全绿,退出码回读 |
|
|
19
19
|
| review | `codex exec -s read-only` fresh-context 只读审查,结构化信封 `{"status":"succeeded","findings":[]}` |
|
|
20
|
-
| candidate | `f97f122`(Git 回读固定,位于 `run/RUN-
|
|
21
|
-
| UI 证据 | portal 文档页 Chrome headless 真渲染截图(页面源码含 `
|
|
22
|
-
| 治理 | `standard` 预设:plan/intent digest 绑定接受(`A-WORK-
|
|
20
|
+
| candidate | `f97f122`(Git 回读固定,位于 `run/RUN-PILOT-EXT-01` 分支,未合并) |
|
|
21
|
+
| UI 证据 | portal 文档页 Chrome headless 真渲染截图(页面源码含 `client-b`、无 `client-b-old`),digest 登记为 screenshot 证据(seq 29);`ui-render-merge-gate` 要求批准前必须存在 |
|
|
22
|
+
| 治理 | `standard` 预设:plan/intent digest 绑定接受(`A-WORK-PILOT-EXT-01-1/2`,by haiyangbg)为 build 前置门;env 白名单(宿主凭据不达 codex 子进程);真实远端 上 worktree 推送保护生效 |
|
|
23
23
|
| 台账 | 29 事件链校验通过;metrics:自动到达 `WAITING_HUMAN` 100%、证据完整率 100%(3/3 步) |
|
|
24
24
|
| 成本 | codex token/费用本轮无采集口径,记 `UNVERIFIED` |
|
|
25
25
|
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
|
|
32
32
|
## 4. 合并决定(已批准)
|
|
33
33
|
|
|
34
|
-
项目所有者于 2026-08-28 批准:`D-RUN-
|
|
34
|
+
项目所有者于 2026-08-28 批准:`D-RUN-PILOT-EXT-01-1`(merge-evidence-floor 与 ui-render-merge-gate 在盖章瞬间均为 PASS)。Run 终态 `SUCCEEDED`,压实为 `delivery/work/WORK-PILOT-EXT-01/runs/RUN-PILOT-EXT-01/run-record.json`,最终台账 34 事件链校验通过;worktree 已清理,candidate `f97f122` 保留在 `run/RUN-PILOT-EXT-01` 分支可达。
|
|
35
35
|
|
|
36
36
|
批准仅表示 merge-ready;将候选并入工作分支、与既有人工候选合流、契约同步与生产切换(提案 §4 硬门)均为后续人工决定,本 Run 未执行任何一项。
|
|
37
37
|
|
|
38
38
|
## 4.1 生产切换(2026-08-28 当日晚,所有者逐步授权后完成)
|
|
39
39
|
|
|
40
|
-
candidate `f97f122` 经 cherry-pick 到生产血统(`
|
|
40
|
+
candidate `f97f122` 经 cherry-pick 到生产血统(`origin/master`,规避了本地分支上未批准的 registry 在途工作与已部署内网文档的双向分叉)→ 全量验证(Surefire 全套 + Portal 29/29 + 零残留)→ 按提案 §4 硬门完成生产切换:只读盘点(唯一 `client-b-old` 行 / 零 Nacos 覆盖 / 零真实消费方登录记录)→ **有界双行窗口**破解新旧健康门顺序死锁(先 INSERT `client-b` 镜像行 → 云效 Run #31 双批发布 SUCCESS → 软删旧行收口)→ L4 全绿(`client-b` 200 ×2、`client-b-old` 400 ×2、`/index` 200 全程无扰动)。证据:meta 仓 `pm/archive/登录二期/evidence/2026-08-28-LXJ-AUTH-PILOT-EXT-01-生产切换.md`。
|
|
41
41
|
|
|
42
42
|
## 5. 对 M4 退出指标的回填
|
|
43
43
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# M4 外部试点 2:
|
|
1
|
+
# M4 外部试点 2:pilot-app Bug 看板积压批处理(RUN-CHICK-0037 / 0018)
|
|
2
2
|
|
|
3
3
|
> 日期:2026-08-28
|
|
4
|
-
>
|
|
5
|
-
> 任务来源:项目所有者点名"异常看板积压了很多 bug,可以去处理一下"。积压读取自钉钉 AI 表格(
|
|
4
|
+
> 项目:`<试点工作区>/pilot-frontend`(Next.js 生产应用,基线 `9d571b3`)
|
|
5
|
+
> 任务来源:项目所有者点名"异常看板积压了很多 bug,可以去处理一下"。积压读取自钉钉 AI 表格(pilot-app base / Bug看板表,feedback 同步目标):39 条中 **13 未修复 + 2 待定**(P1×3 / P2×4 / P3×8)
|
|
6
6
|
> 结论上限:本地候选停在合并决定;不 merge、不 push、不发布;未回写钉钉看板状态。
|
|
7
7
|
|
|
8
8
|
## 1. 本批交付(2 个 Run,全部由 codex Worker 经 Shell Adapter 驱动)
|
|
@@ -41,4 +41,4 @@ inbox 两单终态决定:`RUN-CHICK-0037`(candidate `b866d5c`)与 `RUN-CHI
|
|
|
41
41
|
|
|
42
42
|
## 5. 对 M4 的意义
|
|
43
43
|
|
|
44
|
-
外部试点项目数达到 **2(
|
|
44
|
+
外部试点项目数达到 **2(pilot-backend + pilot-app)**,D6 原文口径满足;六退出指标在两项目上同向达标(试点 Run 自动到达率 4/4)。M4 就此关闭。
|
|
@@ -50,4 +50,4 @@ RUN-SELF-001 (work WORK-SELF-001) [final-decision] enter-wait-merge
|
|
|
50
50
|
## 5. 边界
|
|
51
51
|
|
|
52
52
|
- 本次 accept 与 run 启动由受托会话以 `claude-delegated` 身份执行并如实落账;**合并决定未被代行**,留在 inbox。
|
|
53
|
-
- 外部试点(D6
|
|
53
|
+
- 外部试点(D6:试点工作区内有测试的单仓项目 + 含 UI 项目)待项目所有者点名与授权后执行,指标回填前 M4 不宣称关闭。
|
|
@@ -64,8 +64,8 @@ v1 进入 `v1-maintenance` 维护线,只修安全与严重缺陷;npm `latest
|
|
|
64
64
|
| 2 | **事件台账 + reducer + 终态压实**(events.jsonl、state 重建、run-record) | 厂商日志是私有格式、随会话消亡、不落 Git;工具中立、可重放、可审计的交付台账没有厂商会提供 | 卡点 1:attempts/token/费用无统一 ledger;度量表全列 `UNVERIFIED` |
|
|
65
65
|
| 3 | **中断恢复**(checkpoint、resume、恢复点裁决) | 厂商的 session resume 只恢复自家会话上下文,不恢复跨 Worker 的交付状态(该继续 Verify、回 Build 还是废弃候选) | F5 = `RECOVERY_MISSING`:重开只能 fail-closed,需人读现场 |
|
|
66
66
|
| 4 | **Approval 对象与 stale 检测**(transition + candidate + planDigest + evidenceDigest 绑定) | 厂商审批是工具内 UI 动作,不产生持久化、跨工具、绑定 digest 的审批对象,更不会在对象变化时自动失效 | F6 = `APPROVAL_STALE_MISSING`;卡点 3:单阶段生产滚动 P1 正是"审批未绑定 rollout plan"的真实事故形态 |
|
|
67
|
-
| 5 | **Policy/Gate 语义检查器 + 强制等级报告**(四类 Policy、`doctor` 报告实际强制等级) | 厂商各有权限系统,但没人会检查"你声称的规则实际达到哪级强制"并跨工具编译到 hook/CI | 边界节:提示词禁令只算 `ADVISORY`;
|
|
68
|
-
| 6 | **多 Workspace 绑定**(一个 Work 绑定多仓 candidate 到同一 Decision) | 厂商 Workspace 即"当前打开的仓";跨 meta 仓 + 代码仓的原子绑定是协议层需求 | 卡点 2、卡点 4:
|
|
67
|
+
| 5 | **Policy/Gate 语义检查器 + 强制等级报告**(四类 Policy、`doctor` 报告实际强制等级) | 厂商各有权限系统,但没人会检查"你声称的规则实际达到哪级强制"并跨工具编译到 hook/CI | 边界节:提示词禁令只算 `ADVISORY`;pilot-app 会话始终持有生产能力,未被机器剥离 |
|
|
68
|
+
| 6 | **多 Workspace 绑定**(一个 Work 绑定多仓 candidate 到同一 Decision) | 厂商 Workspace 即"当前打开的仓";跨 meta 仓 + 代码仓的原子绑定是协议层需求 | 卡点 2、卡点 4:pilot-app 与 AI 试点工作区均为 meta+代码多仓,单仓 loop 无法原子关联 |
|
|
69
69
|
| 7 | **统一 Evidence Contract**(回读制证据、grade L0–L4、manifest digest) | 厂商各自产出日志与测试结果,但"什么算证据、谁回读、怎么分级"的合同必须工具中立 | 能力矩阵"证据来源、digest 与未验证范围"= PARTIAL:事后人工汇总、截图无 digest |
|
|
70
70
|
|
|
71
71
|
### 7.2 组装面(一律不自研)
|
|
@@ -59,19 +59,20 @@
|
|
|
59
59
|
|
|
60
60
|
| type | actor | data 最小集 | 语义 |
|
|
61
61
|
|---|---|---|---|
|
|
62
|
-
| `RUN_CREATED` | kernel | `workflowRef, workflowDigest, base, riskPreset` | Run 登记(卡点 5 的回应:没有本事件的工作不得计入 v2 闭环) |
|
|
62
|
+
| `RUN_CREATED` | kernel | `workflowRef, workflowDigest, base, riskPreset`;additive(迭代 08):`supersedes?`(本 Run 起跑时被记为 `SUPERSEDED` 的同 Work 旧 Run id 列表)、`envelopeDigest?` / `envelopeSource?`(run 配置 `envelope:` 的 digest 与来源) | Run 登记(卡点 5 的回应:没有本事件的工作不得计入 v2 闭环) |
|
|
63
63
|
| `RUN_STARTED` | kernel | —— | 进入 RUNNING |
|
|
64
64
|
| `WORKSPACE_BOUND` | kernel | `workspaceId, repo, branch, worktreePath, base` | 一个 Run 可多次(多仓绑定,卡点 2/4) |
|
|
65
65
|
| `STEP_STARTED` | kernel | `step, attempt, worker, adapter, workspaceId` | Step 开跑 |
|
|
66
|
-
| `STEP_FINISHED` | kernel | `step, attempt, status ∈ {succeeded,failed,blocked,invalid-output,timeout,crashed}, exitCode
|
|
67
|
-
| `CANDIDATE_PINNED` | kernel | `workspaceId, base, candidate` | candidate 由 Git 回读后固定 |
|
|
68
|
-
| `EVIDENCE_RECORDED` | kernel/provider | `evidenceRef, kind, subject, digest, status, grade
|
|
66
|
+
| `STEP_FINISHED` | kernel | `step, attempt, status ∈ {succeeded,failed,blocked,invalid-output,timeout,crashed}, exitCode?`;additive(迭代 09):`infra?: true`(worker 基础设施故障:timeout / crashed / invalid-output / exit 75;状态 `steps[step].infraAttempts` 累加,不扣预算) | Adapter 异常退出也必须落此事件(不变量 15) |
|
|
67
|
+
| `CANDIDATE_PINNED` | kernel / human | `workspaceId, base, candidate`;additive(迭代 09):`adopted?: true`(人或驾驶会话手修后经 `resume --adopt` 供出的候选,actor 为 human) | candidate 由 Git 回读后固定 |
|
|
68
|
+
| `EVIDENCE_RECORDED` | kernel/provider | `evidenceRef, kind, subject, digest, status, grade`;additive(迭代 08):`cacheKey?`(树+命令+信封的复用键)、`reused? {run, evidenceRef, digest}`(本记录引用了哪次已通过的证据而未重跑) | 指向满足 Evidence Contract 的记录 |
|
|
69
69
|
| `POLICY_EVALUATED` | kernel | `policy, phase ∈ {pre,post,transition,action}, result ∈ GateResult, enforcement, reason` | 每次 Policy 裁决可解释 |
|
|
70
70
|
| `TRANSITION` | kernel | `from, to, cause` | 每次状态转换一条(不变量 5) |
|
|
71
71
|
| `FAILURE_FINGERPRINT` | kernel | `step, command, exitCode, errorDigest, diffDigest` | 无进展/相同失败检测的输入 |
|
|
72
72
|
| `BUDGET_CONSUMED` | kernel | `kind ∈ {attempts,tokens,cost,time}, amount, remaining` | 预算台账(卡点 1:token/费用不再 `UNVERIFIED`) |
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
73
|
+
| `BUDGET_EXTENDED` | kernel | `step, amount, maxAttempts, approvalRef, scope?` | additive(迭代 09):人批准了预算耗尽的 `resume-<step>`,该步上限 +`amount`;状态 `budgetExtensions[step]` 累加,`maxAttemptsFor(step)` 据此重放。`scope: work` 时是 Work 级 review 轮数上限(`budgets.reviewRoundsPerWork`)被人放行一轮,累加到 `workReviewGrants` |
|
|
74
|
+
| `HUMAN_REQUESTED` | kernel | `transition, subject{candidate,planDigest,evidenceDigest}, reasons`;additive:`kind ∈ {boundary,final-decision,finding-triage,stale,infra}` | 进入 WAITING_HUMAN |
|
|
75
|
+
| `DECISION_RECORDED` | human | `decision ∈ {approved,rejected}, transition, subject, decisionRef`;additive(迭代 09):`adopted?: <sha>`、`resumeAt?: <step>`(adopt 时 subject 即该提交,恢复从 `resumeAt` 起而非 transition 所指的步) | 同步落 Git 决策记录 |
|
|
75
76
|
| `APPROVAL_STALE` | kernel | `approvalRef, changed ⊆ {candidate,plan,evidence}` | F6 的机器化 |
|
|
76
77
|
| `CHECKPOINT` | kernel | `resumePoint{step,attempt}, workspaceStates[]` | F5 的机器化:恢复只允许从最近 CHECKPOINT 或安全推导点继续 |
|
|
77
78
|
| `RUN_INTERRUPTED` | kernel | `cause` | 尽力而为;崩溃时允许缺失,恢复逻辑不得依赖其存在 |
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# 怎么和装了 BuildBeat 的会话说话(按项目阶段)
|
|
2
|
+
|
|
3
|
+
> 这是给**用户**看的一页。你在任意一个 AI 编程会话里工作(哪家工具都可以,一个会话就够),会话装载了 BuildBeat Skill;你说人话,它去调 `buildbeat-v2`、读输出、按「已做 → 未做 → 下一步」回你。**你不需要记任何命令**。下面的例句就是平时的说法,照抄即可;同一格里的几句话意思相同,挑顺口的。
|
|
4
|
+
> 会话侧的对应规则在 `SKILL.md` §0.5;项目专属的路由与红线在各项目根的 `AGENTS.md`。
|
|
5
|
+
|
|
6
|
+
## 一张表:项目从零到换期
|
|
7
|
+
|
|
8
|
+
| 阶段 | 你想干什么 | 你就这么说 | 会话背后做什么 | 你会得到什么 / 注意 |
|
|
9
|
+
|---|---|---|---|---|
|
|
10
|
+
| **0. 未开始** | 判断值不值得上 BuildBeat | 「这个项目适合用 BuildBeat 吗」 | 看仓库规模、周期、仓/部署单元/会话数 | 一句判断:多期迭代 / 多仓 / 多会话才值得;一周收尾的小活直接干 |
|
|
11
|
+
| | 新项目搭骨架 | 「帮我用 BuildBeat 搭一下这个项目」「初始化协作骨架」 | 自查代码 → 少量提问(栈、仓、部署单元、有没有 UI)→ 一屏确认 → 生成 `AGENTS.md`(v2 模板)、`delivery/`、`.buildbeat/observe.yaml`、`.buildbeat/notify.yaml`、`scripts/bus-check.sh`、pre-commit | 一屏确认单,你说「可以」它才写;生成后给文件清单 |
|
|
12
|
+
| | 接管老项目 | 「给这个老项目套上 BuildBeat」「接管这个仓」 | 摸底(测试、契约、部署事实)→ 划绞杀边界(新地盘 / 老地盘 / 只读)→ 第 0 期补最小验证 | 摸底报告 + 边界草案,你拍板边界;历史不回改 |
|
|
13
|
+
| | 装上通知 | 「Run 停下来时通知我」 | 写 `.buildbeat/notify.yaml`(钉钉 / webhook) | 它告诉你要建什么机器人、`export` 哪个环境变量;URL 永远不进 Git |
|
|
14
|
+
| **1. 立项 · 定方案** | 立一件事 | 「开个 Work:〔一句话目标〕」「把当前目标收成一个 Work」 | 写 `delivery/work/<ID>/intent.md`(为什么做、做到什么算完)+ `plan.md`(怎么做、分几步)+ `run-config.yaml` | 摘要 + 「看完说接受」;你说「接受」才 digest 绑定生效 |
|
|
15
|
+
| | 先看方案不写代码 | 「先别动代码,给我方案」「A 和 B 的差别是什么,说给我听听」 | 只出草案与对比,不起 Run | 方案对比 + 推荐 + 后果;没拍板不动手 |
|
|
16
|
+
| | 把该我定的挑出来 | 「方案里哪些要我拍板」「有什么需要我拍板的吗」 | 把验收清单压成真实取舍(含域名 / 服务名 / 时长这类你以后要念的名字) | 一次给 2~5 个决策项,每项带推荐值;不逐条连环问 |
|
|
17
|
+
| | 拍板 | 「接受」「全部按推荐」「第 2 个选 B,其余按推荐」 | `accept` intent/plan;决策落 `decisions.jsonl` / `pm/decisions.md` | 一句确认;改了 plan 会自动变 stale,需要再接受 |
|
|
18
|
+
| | 定风险等级 | 「这个用快轨」「这个要严一点」 | `riskPreset: fast / standard / controlled` | fast 只在合并前停;standard 加 plan 接受;controlled 加 intent 接受与发布门 |
|
|
19
|
+
| **2. 准备执行** | 看环境齐不齐 | 「环境有什么要求」「预检一下」 | `requires:`(二进制版本 + `probe:` 探针)核验;`preflight --step` 干跑首个失败边界 | 一次报清缺什么;干跑不产证据,正式 Run 要复现 |
|
|
20
|
+
| | 冻结信封 | 「把 prompt 钉住」「信封冻结」 | `envelope:` 加 `pin: <sha>` | 之后每个 Run 记 envelopeDigest,可追溯 |
|
|
21
|
+
| **3. 执行 · 推进** | 让它干活 | 「开工」「〔WORK-ID〕该你了」 | `start --config … --attempt new`(自动编号、旧等待自动作废、脱离启动) | 「已起 RUN-X-02,停在合并决定会通知你」 |
|
|
22
|
+
| | 看进度 | 「当前进度」「到哪了」「按仓分别说」 | `overview`(每个 Work 的阶段 + 下一步该谁)+ `observe status` | 每件事一句:阶段、卡在谁、下一步;不列命令 |
|
|
23
|
+
| | 看下一步 | 「下一步做什么」「需要我做什么」 | overview 的 next 行,只挑「在你手里」的 | 只列你要做的:DNS、凭据、批准、亲自操作 |
|
|
24
|
+
| | 怕它卡住 | 「怎么样了」「卡住了吗」「半小时了正常吗」 | `status --run`(每步耗时、历史中位数、最后输出、STALLED) | 一句带数字:「verify 已 14 分钟,历史中位 6 分钟,最后输出 2 分钟前,还在动」;疑似卡住会直说 |
|
|
25
|
+
| | 看有什么等我 | 「有什么要我批」「有什么等我」 | `inbox` + `delivery/observe/intents/` | 逐项:等什么、证据在哪、推荐 A/B |
|
|
26
|
+
| | 批 / 不批 | 「批准」「批准 RUN-X」「拒绝,原因是…」 | `approve` / `reject`;非终态自动 `resume` | 批准 = merge-ready;合并 / push / 部署你另说 |
|
|
27
|
+
| | 裁 finding | 「这条不算,那条接受」「这个是误报」 | `findings adjudicate dismiss/accept` → 放行 fixer | 被 dismiss 的同一条以后不再阻断;严重度升级会重开 |
|
|
28
|
+
| | 再来一轮 | 「再来一轮」「继续」 | 新 attempt | 旧的等待自动作废,inbox 只剩活的 |
|
|
29
|
+
| | 停下来 | 「这次再不成功就停」「先停下来」 | 封顶轮数;`stop --reason` 记账,候选与证据保留 | 已做 / 未做 / 下一步,带 hash;之后由你决定改方案、手工修还是关掉 Work |
|
|
30
|
+
| **4. 验收 · 合并** | 验收 | 「验收」「验 RUN-X 候选」 | 测试视角对精确 candidate 独立核验,报告落 Work 目录 | 通过 / 不通过 + 证据;写者的话不算证据 |
|
|
31
|
+
| | 合并 | 「merge 吧」「授权 push」 | 人类动作由会话代执行(在 `AGENTS.md` 红线内),回读远端 | 报合并后 hash;`overview` 显示 MERGED |
|
|
32
|
+
| **5. 上线** | 上线 | 「上线」「准备 Gate4」 | `release-readback` 预设 + `riskPreset: release`:先回读 → 停下来 | 「回读全绿,现在轮到你做〔动作〕;做完说一声」 |
|
|
33
|
+
| | 我做完了 | 「做完了」「做到一半了,你核一下」 | 批准 apply-readback → 回读 + 观察 → 停关窗 | 差什么逐条说;任一步失败即停 |
|
|
34
|
+
| | 拍上线卡 | 「批准上线」「先不上线」 | 按决策卡执行 / 不执行 | 生产动作永远是你的;它只回读和记账 |
|
|
35
|
+
| | 生产报警 | 「生产有报警」「体检一下」 | `observe run` → 草稿入队 | 草稿一句 + 「fix_now / schedule / dismiss 你选」 |
|
|
36
|
+
| **6. 完结 · 换期 · 复盘** | 收尾一件事 | 「这个 Work 完了」「关掉这个 Work」 | 记决策、确认 run-record 在 Git 面 | `overview` 不再把它列为待办 |
|
|
37
|
+
| | 打扫 | 「打扫卫生」 | `gc`(先出计划,你点头再 `--apply`) | 清了几个工作树;仅此分支可达的候选一律保留并说明 |
|
|
38
|
+
| | 换期 | 「换期」「进入下一期」 | 换期压缩仪式:归档、截断、指针清零、回灌一问 | 一屏清单;新一期从干净的入口开始 |
|
|
39
|
+
| | 复盘 | 「复盘一下 BuildBeat 做了什么」「正向负向各是什么」 | `metrics` + Run 台账 + 会话记录 | 正向 / 负向 / 改革条,带数字 |
|
|
40
|
+
| | 记经验 | 「把这次卡点的经验记一下」 | 写 `pm/<日期>-<主题>.md` + `env-facts.md`,能机器化的转成 `requires:` 探针 | 下一窗直接引用,不口口相传 |
|
|
41
|
+
| | 回灌 | 「有哪些是 BuildBeat 可以吸收的」 | 对照 lessons 找新坑 | 候选清单,逐条 A/B/C |
|
|
42
|
+
| | 升级 | 「更新一下 BuildBeat 版本」 | 改 `BUILDBEAT.md` / `AGENTS.md` / `.buildbeat/*.yaml`,跑 `doctor` | 版本标记 + doctor 报告 |
|
|
43
|
+
|
|
44
|
+
## 贯穿全程的几句话
|
|
45
|
+
|
|
46
|
+
| 你说 | 它会 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| 「为什么」「不做会怎样」「这名字怎么起的」 | 用人话解释,并把该你定的(名字、时长、范围)转成决策项让你批,而不是替你定 |
|
|
49
|
+
| 「说人话」「别列 1234」 | 已做 / 未做 / 下一步各一句;只有事项差异大才分条 |
|
|
50
|
+
| 「你觉得呢」 | 给一个推荐和理由,不是一堆选项 |
|
|
51
|
+
| 「授权」「批准」单独出现 | 只对它上一条明确提出的那件事生效,不外推到别的动作 |
|
|
52
|
+
|
|
53
|
+
## 三条底线(省你追问)
|
|
54
|
+
|
|
55
|
+
- **批准 ≠ 执行**:合并、push、部署、花费、删除,每一项都要你逐项说。
|
|
56
|
+
- **数字必须落地**:问「正常吗」得到的回答一定带已用时间 / 历史中位数 / 最后输出时间;没数据就说没数据。
|
|
57
|
+
- **名字先过你**:域名、服务名、环境名、自停时长这类你以后要念的东西,会话在决策卡里给推荐值让你批,不自己定。
|
|
@@ -82,6 +82,10 @@ buildbeat-v2 approve --repo . --run RUN-DEMO-1 --transition enter-wait-merge --b
|
|
|
82
82
|
|
|
83
83
|
想让 finding 先过你的手再派 fixer:run 配置加 `reviewTriage: required`,配套 `findings list` / `findings adjudicate` 逐指纹裁决(dismiss 后同指纹不再阻断);正式起 Run 前可用 `preflight --step <id>` 在主 checkout 分钟级干跑单步(不产证据);信封的环境依赖用 `requires:` 声明,启动前 fail-closed 核验。详见 [Approval 指南](07-approval-guide.md)、[Evidence 指南](06-evidence-guide.md)、[Workflow 指南](02-workflow-guide.md)。
|
|
84
84
|
|
|
85
|
+
跑起来之后三件事不用再问 AI:`status` 会说每步跑了多久、历史上通常多久、worker 最后一次输出是什么时候(无输出超过 15 分钟标 `STALLED`);`status` / `inbox` 在每个等待后面直接给出可复制的下一句命令;`.buildbeat/notify.yaml` 配一条钉钉或 webhook 通道,Run 停下来会来找你。同一个 Work 再起新 Run 时旧的等待自动作废,终态 Run 留下的工作树用 `gc --repo .` 清(默认只出计划)。详见 [Approval 指南](07-approval-guide.md) 与 [故障恢复](10-recovery.md)。
|
|
86
|
+
|
|
87
|
+
「到哪了」问 `buildbeat-v2 overview --repo .`:每个 Work 的阶段与下一步该谁。`start --attempt new` 让一份 run 配置跑到底(自动编号 `RUN-X-01/02…`);`envelope:` 让内核喂 prompt、`cache: {verify: tree}` 让同树同命令的 verify 不重跑、`requires:` 的 `probe:` 把环境事实前置核验,见 [Workflow 指南](02-workflow-guide.md)。**在 AI 会话里用 BuildBeat 的人不需要记这些命令**:`SKILL.md` §0.5 是给会话读的驾驶手册,用户说「当前进度 / 开工 / 怎么样了 / 批准 / 上线 / 打扫卫生」即可。
|
|
88
|
+
|
|
85
89
|
## 5. observe:让系统盯生产(v0)
|
|
86
90
|
|
|
87
91
|
```bash
|
|
@@ -52,7 +52,54 @@ run 配置还可声明(beta.3,皆来自三十轮部署战役的真实事故
|
|
|
52
52
|
|
|
53
53
|
## review 轮数预算
|
|
54
54
|
|
|
55
|
-
官方预设自带 `budgets.maxAttempts.review: 2`(战役章程"每 Run 2 轮 review 封顶"的原生化):第三轮 review 在启动前即停 `WAITING_HUMAN
|
|
55
|
+
官方预设自带 `budgets.maxAttempts.review: 2`(战役章程"每 Run 2 轮 review 封顶"的原生化):第三轮 review 在启动前即停 `WAITING_HUMAN`,理由写明预算耗尽。机制就是每步 `maxAttempts`,无需新概念。
|
|
56
|
+
|
|
57
|
+
预算耗尽后停的那次 `resume-<step>`,**人批准即多给一次**:内核落一条 `BUDGET_EXTENDED`(台账事实,可重放),该步上限 +1 再跑;拒绝即终止 Run。此前批准只会让同一请求立刻回来(试点两条应用登录 Run 因此以 CANCELLED 收场,候选却已在生产)。
|
|
58
|
+
|
|
59
|
+
run 配置可覆盖预设(run 配置 > 预设 > 全局 `maxAttemptsPerStep`):
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
budgets:
|
|
63
|
+
maxAttempts:
|
|
64
|
+
review: 3
|
|
65
|
+
verify: 6
|
|
66
|
+
reviewRoundsPerWork: 6 # 跨本 Work 所有 Run(含已作废)累计的 review 轮数上限,见 overview 指南
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`doctor` 打印每步生效的上限与来源(run config / workflow preset / default)。
|
|
70
|
+
|
|
71
|
+
**按 Work 累计的 review 轮数(迭代 09)**:每 Run 的预算挡不住"每轮一个新 Run"——试点一个 Work 跑了 21 个 Run、9 轮 review,2 轮封顶从未触发。`budgets.reviewRoundsPerWork: N` 让内核在 review 步起跑前统计本 Work **所有** Run(含已作废、含已压成 run-record 的)的 review 轮数,达到 N 即停 `WAITING_HUMAN`(kind `work-review-cap`,transition `enter-review`):批准即再审一轮(台账 `BUDGET_EXTENDED scope=work`),拒绝则按手头证据合并或关闭。`overview` 每个 Work 多一行 `cost: review rounds · findings · human waits · worker 时长`,run-record 也带 `cost` 块——"继续还是砍"之前先看这一行;intent 里的止损线(最多几个 Run / 几轮 review / 几小时)就对着它核。
|
|
72
|
+
|
|
73
|
+
## 工作树在仓内:把 `.buildbeat/` 排除出测试收集(迭代 09)
|
|
74
|
+
|
|
75
|
+
Run 的隔离工作树在 `<repo>/.buildbeat/worktrees/<RUN>/`,运行时台账在 `<repo>/.buildbeat/runtime/`。两者都不入 git(模板 `.gitignore` 已排除;尊重 `.gitignore` 的工具如 `rg`、`gitleaks` 随之不再走进去),但**测试框架按文件系统收集用例**:试点合并后的主干 vitest 把残留工作树里旧候选的用例一起跑了,噪声直到 `gc` 才消失。在项目里加:
|
|
76
|
+
|
|
77
|
+
- vitest:`test.exclude: ['**/node_modules/**', '**/.buildbeat/**']`
|
|
78
|
+
- jest:`testPathIgnorePatterns: ['/node_modules/', '/.buildbeat/']`
|
|
79
|
+
- pytest:`norecursedirs = .buildbeat`
|
|
80
|
+
- Maven / Gradle 只收集 `src/**`,不受影响;Playwright 的 `testDir` 指到具体目录即可。
|
|
81
|
+
|
|
82
|
+
`start` 被「another run is active」挡住时,CLI 现在打印持锁的 Run、它在哪一步、最后一次事件多久前,以及可复制的 `status` 命令;仓级单活动 Run 锁本身没放开——工作树已隔离,锁只剩台账与合并安全的意义,等真出现第二次多小时排队再动。
|
|
83
|
+
|
|
84
|
+
## 基础设施故障与候选缺陷分开算(迭代 09)
|
|
85
|
+
|
|
86
|
+
worker 的超时、崩溃、非信封输出,以及 worker 主动以退出码 **75** 结束(约定:verify / 包装脚本发现环境不满足——命令不在 PATH、端口被占、后端 404、沙箱禁止监听——就 `exit 75`),内核一律判 `infra`:`STEP_FINISHED.data.infra = true`,不记失败指纹、不派 fixer、该步预算不扣(`steps[step].infraAttempts` 抵回),停 `WAITING_HUMAN`(kind `infra`)。人批准 `resume-<step>` 重跑,拒绝结束。其余非零退出仍是候选失败,走 `on: failed` 边。
|
|
87
|
+
|
|
88
|
+
没有转移边的失败结果(预设里 build、review、fix 的 `failed`)也不再终态,停 `resume-<step>` 交人决定。终态 FAILED 只剩 policy `BLOCK`。
|
|
89
|
+
|
|
90
|
+
## 迭代 08 新增的 run 配置段
|
|
91
|
+
|
|
92
|
+
- **`envelope:`** —— `prompts:`(目录,相对 run 配置)+ `vars:`(`{vars.x}` 替换)+ 可选 `pin: <sha>`(从该提交读 prompt,冻结信封)。内核按 `<component>-<worker>.md` → `<worker>.md` 取 prompt,落到 `runs/<RUN>/prompts/<step>-<n>.md`,以 `BUILDBEAT_PROMPT`(路径)和 `input.envelope`(`promptRef / file / digest / vars`)交给 worker;worker args 里可用 `{prompt}` 与 `{vars.x}`。`RUN_CREATED` 记 `envelopeDigest`。
|
|
93
|
+
- **`start --attempt new`** —— `run:` 写家族名(`RUN-X`),内核编成 `RUN-X-01/02…`(扫运行时面与 Git 面 run-record,删 runtime 也不撞号);同 Work 旧的等待自动作废([Approval 指南](07-approval-guide.md))。
|
|
94
|
+
- **`cache:`** —— `verify: tree`:同 `HEAD^{tree}` + 同 worker 命令 + 同信封 digest 且**已通过**的 verify 复用证据(台账 `reused`,status 标 `(reused from RUN-X)`);失败、脏树不复用。verifier 依赖树外事物(远端、时间)的项目不要开。
|
|
95
|
+
- **增量审查** —— readonly 步的 input 带 `lastReviewed {candidate, run, evidenceRef, range}`(同 Work 最近一次 review 的候选且为当前候选祖先);reviewer prompt 可要求只审 `range` 内 diff,锚定裁决照旧(`anchor`)。
|
|
96
|
+
- **`redact:`** —— 正则列表,证据日志落盘前替换为 `<REDACTED>`;digest 绑脱敏后文本。实时流(`.live`)不脱敏、步结束即删。
|
|
97
|
+
- **`requires:` 的 `probe:` 条目** —— `probe: <shell 命令>` + 可选 `expect: <正则>` + `name:`;退出码非 0 或输出不匹配即 fail-closed,与二进制版本项一次报清。把踩出来的环境事实(Redis ≥ 7、目标机 Python 版本、端口可达)写成 probe,下窗不重踩;叙述性事实放 `delivery/work/<ID>/env-facts.md`。
|
|
98
|
+
- **step `grade:`** —— workflow 步骤可声明该步命令证据的等级(L0–L4,默认 L2)。
|
|
99
|
+
|
|
100
|
+
## 上线回读车道:`release-readback` + `riskPreset: release`
|
|
101
|
+
|
|
102
|
+
内核没有部署能力(不变量 20),生产动作永远是人的。这条车道只把动作前后的**回读**记成 L4 台账:`preflight`(动作前只读检查)→ 停 `enter-apply-readback`(人做动作)→ `apply-readback`(证明动作生效)→ `observe`(证明健康)→ `wait-close`(人关窗)。三个回读步全部 `readonly`、`grade: L4`、`maxAttempts 1`:任一步失败即停人批,没有 fix 边。风险预设 `release` 提供 `stopAt: apply-readback` 与关窗证据门(L4 命令证据)。worker 是任意回读脚本(curl 健康、读版本、比对配置指纹),退出码就是结论。试点项目上线那天的四十条手工 readback 提交,就是这条车道该做的事。
|
|
56
103
|
|
|
57
104
|
## 修改纪律
|
|
58
105
|
|
|
@@ -43,3 +43,7 @@ Adapter 只报告事实:exitCode / signal / timedOut / spawnError / stdout / s
|
|
|
43
43
|
## 何时写专用 Adapter
|
|
44
44
|
|
|
45
45
|
只有当 Shell 表达不了(需要流式交互、会话保持)才写专用 Adapter;按 M3 裁决,先用 Shell 接一切,等真实试点证明不够再说。
|
|
46
|
+
|
|
47
|
+
## 实时输出(迭代 08)
|
|
48
|
+
|
|
49
|
+
编排器给 Shell Adapter 传 `liveDir` 时,子进程的 stdout/stderr 直接写到 `<liveDir>/<step>-<attempt>.{stdout,stderr}.live`(fd 直连,不经父进程缓冲),并写 `live.json`(`step / attempt / worker / command / startedAt`)。步返回后 Adapter 读回两份流作为 `stdout` / `stderr`,删掉实时文件——结果形状不变,证据收集器照旧。自写 Adapter 若想被 `status` 的"最后输出距今"识别,产出同名文件即可;不产出则 `status` 只显示已用时间。
|
|
@@ -35,3 +35,13 @@
|
|
|
35
35
|
- prompt 里明确引用 `delivery/work/<id>/plan.md`,让 Worker 的目标与被批准的 digest 是同一份文件;
|
|
36
36
|
- builder 的提交动作可以由包装脚本机械执行(M4 试点即如此:codex 只改文件,`git commit` 在包装层);
|
|
37
37
|
- reviewer 的 prompt 要求"只输出信封 JSON",并用 `-o`/重定向落到 `$BUILDBEAT_OUTPUT`。
|
|
38
|
+
|
|
39
|
+
## 迭代 08:输入里多了什么
|
|
40
|
+
|
|
41
|
+
- `BUILDBEAT_PROMPT`(环境变量,文件路径)与 `input.envelope`(`promptRef / file / digest / vars`):run 配置 `envelope:` 声明的 prompt 已由内核替换变量并落盘,worker 直接 `cat "$BUILDBEAT_PROMPT"`,不再自己 `git show`。
|
|
42
|
+
- `input.lastReviewed`(仅 readonly 步):`{candidate, run, evidenceRef, range}`——上一次 review 看过的候选与到当前候选的 `range`;reviewer 可只审增量,但**已裁决结论不得翻案**(`anchor` 仍在)。
|
|
43
|
+
- `input.findings`(写入步)与 `input.anchor`(readonly 步)不变。
|
|
44
|
+
|
|
45
|
+
## 所有者可见命名不由 worker 决定(迭代 08)
|
|
46
|
+
|
|
47
|
+
builder / planner 在实现中会顺手起名:域名、服务名、环境名、自停时长、窗口时长。**凡所有者以后要看见或念出来的名字与参数,不是实现细节,是门前决策项**:写进 intent,或攒进门前决策卡给推荐值与理由,人批后再落地。真实事故:一个按内部术语起的服务名让所有者连问四轮才改成他听得懂的业务名。prompt 里写明这条,reviewer 清单里把"引入了未经批准的可见命名"记为 P2。
|
|
@@ -42,3 +42,7 @@
|
|
|
42
42
|
## 完整率
|
|
43
43
|
|
|
44
44
|
`buildbeat-v2 metrics` 输出证据完整率(有证据的步/应有证据的步);M4/M5 退出线 ≥95%,试点实测 100%。
|
|
45
|
+
|
|
46
|
+
## 运行中的读数 ≠ 证据(迭代 08)
|
|
47
|
+
|
|
48
|
+
Shell Adapter 在步运行期间把 worker 的 stdout/stderr 实时流到 `.buildbeat/runtime/runs/<RUN>/<step>-<n>.{stdout,stderr}.live`,并留 `live.json`(命令、开始时间)。它们是**读数**:`status` 拿来回答"还在动吗、动了多久、最后一次输出是什么时候",步一结束就收回;证据日志仍由回读生成、digest 仍绑最终日志。耗时同理——每步耗时、同仓历史中位数(`metrics` 的 `typical step duration`)都从台账时间戳推导,不进台账、不进 run-record。
|
|
@@ -49,3 +49,50 @@ merge 批准只表示 **merge-ready**:真正的合并、push、发布是你在
|
|
|
49
49
|
3. **锚定注入**:Reviewer(readonly 步)的 `BUILDBEAT_INPUT` 带 `anchor`(历史 finding+裁决全表),信封 prompt 应告知 reviewer"已裁决的结论不得翻案";fixer 等写入步的 input 带 `findings`(上一轮 review 的 finding 及其裁决状态)——fixer 只修 accepted/open,不猜。
|
|
50
50
|
|
|
51
51
|
裁决记忆在 Git 面,删 runtime 不丢(不变量 23 同款测试覆盖)。
|
|
52
|
+
|
|
53
|
+
## 等待要能找到人(迭代 08)
|
|
54
|
+
|
|
55
|
+
试点工作区 58 个 Run 里 32 个被取消,多数是在 `WAITING_HUMAN` 挂满一天后批量清掉;人批平均等 7~12 小时。原因不是人慢,是**没人知道有东西等他**。三件事配套:
|
|
56
|
+
|
|
57
|
+
1. **下一句该说什么**:`status` 与 `inbox` 在每个等待后面直接给出可复制的命令(`approve` / `reject`,分诊时加 `findings list|adjudicate`);`inbox` 按 Work 分组并显示已等待时长。输出里的 `--repo` 只在项目内给相对路径,项目外给 `<repo-path>` 占位——本机绝对路径永不进输出。
|
|
58
|
+
2. **同 Work 新 Run 取代旧等待**:`start` 时同一 Work 下仍在等待的旧 Run 记 `SUPERSEDED`(终态、压成 run-record),新 Run 的 `RUN_CREATED.data.supersedes` 记血统;inbox 只剩活的等待。不想要这个行为就在 run 配置写 `supersede: off`。RUNNING 的 Run 不受影响(active 锁),被别的进程锁住的旧 Run 跳过并明示。
|
|
59
|
+
3. **通知出站**:Git 面 `.buildbeat/notify.yaml`:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
kind: notify
|
|
63
|
+
version: 1
|
|
64
|
+
channels:
|
|
65
|
+
- id: owner
|
|
66
|
+
type: dingtalk # 或 webhook
|
|
67
|
+
urlEnv: BUILDBEAT_NOTIFY_URL # URL 只能来自环境变量;写 url 直接拒绝
|
|
68
|
+
events:
|
|
69
|
+
- HUMAN_REQUESTED
|
|
70
|
+
- RUN_TERMINAL
|
|
71
|
+
- STALLED
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Run 停在人批或到终态时由 CLI 出站;订阅 `STALLED` 时 `start`/`resume` 会派一个脱离的 `watch` 进程盯 worker 输出静默(阈值 `stallAfterMs`,默认 15 分钟)。发送失败只记 `runs/<RUN>/notify.log` 与屏幕,**永不影响 Run**;载荷只有标识、原因、候选 SHA 与下一句命令,零日志零候选内容。钉钉自定义机器人需配置关键词(默认 `BuildBeat`)。`doctor` 报告通道与环境变量是否就位。
|
|
75
|
+
|
|
76
|
+
通知不是审批通道:拍板仍只能在 CLI 完成,digest 绑定不变。
|
|
77
|
+
|
|
78
|
+
## 从「等我批」到「到哪了」:overview(迭代 08)
|
|
79
|
+
|
|
80
|
+
`inbox` 只知道哪个 Run 在等人;`buildbeat-v2 overview --repo .` 按 Work 回答「走到哪、下一步该谁」——intent/plan 是否被接受(接受后改过即 `stale`)、最新 Run 状态与候选、候选是否已合入当前分支、未裁决 P0/P1 数、是否有 `env-facts.md`,每行附下一句命令。运行时被删后由 Git 面 run-record 补足。会话开场先跑它,再回答用户「当前进度」。
|
|
81
|
+
|
|
82
|
+
**阶段判定的真相修正(迭代 09)**:候选只要合入了当前分支,Work 就是 `MERGED`,哪怕最新 Run 是 CANCELLED(试点一条应用登录 Run 因预算问题被取消,候选却已在生产,overview 曾报 `STOPPED_CANCELLED` 并催重试);`release-readback` 车道成功关窗的 Work 显示 `RELEASED`,不再说 "nothing to merge";已合并 / 已发布 / 已关闭的 Work 不再提示未裁决 finding 数。`overview` 每个 Work 还多一行 `cost:`(见 [Workflow 指南](02-workflow-guide.md) 的 Work 级预算)。
|
|
83
|
+
|
|
84
|
+
## 你自己改好了:`resume --adopt`(迭代 09)
|
|
85
|
+
|
|
86
|
+
Run 停在 `enter-fix` / `resume-fix` 时,驾驶会话或人常常已经在 Run 的 worktree 里把问题修掉并提交了。此时再 `approve` 会派一个无事可做的 fixer,再多跑一次 verify(试点一条前端 Run 因此跑到 verify 第 5 次、fix 第 3 次)。改用:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
buildbeat-v2 resume --config <run-config.yaml> --adopt <sha> --by <名字>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
内核回读 worktree:树必须干净、HEAD 必须就是 `<sha>`(前缀 7 位起),否则拒绝;然后以人为 actor 落 `CANDIDATE_PINNED`(`adopted: true`)、以该提交为 subject 记 `DECISION_RECORDED`(`adopted`、`resumeAt`),并从 verify 继续(预设里 fix 成功后的下一步)。台账里看得出这一版候选是谁供的。合并决定处不接受 adopt。
|
|
93
|
+
|
|
94
|
+
`doctor` 现在还打印本仓 `delivery/work/<ID>/` 里 intent / plan 的存在与接受状态,并对每条要求 `artifact.accepted` 的 policy 预告"start 会停在哪一步"——此前两次 doctor 通过、start 却被"plan 未镜像到子仓"挡住。
|
|
95
|
+
|
|
96
|
+
## 可见命名是门前决策项(迭代 08)
|
|
97
|
+
|
|
98
|
+
审批三级里 `BATCH_AT_GATE` 明确包含:域名、服务名、环境名、自停时长、窗口时长等**所有者以后要看见或念出来的名字与参数**。worker 顺手定的名字进不了台账;planner 在 intent 里列出并给推荐值,人一次批。
|
|
@@ -34,9 +34,10 @@ buildbeat-v2 stop --repo . --run RUN-X --reason "crashed; releasing lock"
|
|
|
34
34
|
|
|
35
35
|
### Worker 行为异常
|
|
36
36
|
|
|
37
|
-
-
|
|
37
|
+
- **worker 基础设施故障(迭代 09)**:超时、崩溃、输出不是信封(`invalid-output`)、或 worker 自己以退出码 **75**(`EX_TEMPFAIL`,"环境不可用")结束——内核判为 `infra`:不记失败指纹、不派 fixer、**不扣该步预算**,停 `WAITING_HUMAN`(kind `infra`,transition `resume-<step>`),通知照常出站。后端恢复后 `approve --transition resume-<step>` 重跑该步;`reject` 结束 Run。真实事故:worker 服务端 404 与非 JSON 输出两天杀掉 5 个 Run,驾驶会话手写探针每两分钟试一次;PATH 缺 rg、端口撞车、宿主负载 280 各派了一次 fixer。
|
|
38
|
+
- **没有转移边的失败**(如预设里 build / review / fix 的 `failed`)不再终态 FAILED,同样停 `resume-<step>` 由人决定重跑或结束。
|
|
38
39
|
- 越界写入 → Run BLOCK 且不固定 candidate:检查 `allowedPaths` 与 Worker prompt 的范围声明;
|
|
39
|
-
- 超时 →
|
|
40
|
+
- 超时 → 先看是不是环境(`infra` 已停人),再调 `timeoutMs`;超预算 → 这是刹车不是故障,批准 `resume-<step>` 即多给一次,或收 scope。
|
|
40
41
|
|
|
41
42
|
### observe 面
|
|
42
43
|
|
|
@@ -54,4 +55,25 @@ rm -rf .buildbeat/runtime/
|
|
|
54
55
|
|
|
55
56
|
## 诊断入口
|
|
56
57
|
|
|
57
|
-
`buildbeat-v2 doctor --config <run-config>`:配置可解析、workflow 无出口环、adapter env 姿态、digest
|
|
58
|
+
`buildbeat-v2 doctor --config <run-config>`:配置可解析、workflow 无出口环、adapter env 姿态、digest 可算、supersede 与 stall 阈值、通知通道与环境变量是否就位。`events`/`replay`/`metrics` 全部只读,可随时跑。
|
|
59
|
+
|
|
60
|
+
## "是不是卡住了"(迭代 08)
|
|
61
|
+
|
|
62
|
+
先看 `buildbeat-v2 status --repo . --run <RUN>`:在飞步骤有已用时间、同仓历史中位数、worker 命令、最后一次输出距今多久与末三行输出。无输出超过阈值(默认 15 分钟,`--stall-after <分钟>` 或 run 配置 `stallAfterMs`)标 `STALLED`——**只标不杀**。判断口径:
|
|
63
|
+
|
|
64
|
+
- 有输出在持续 → 等(对照 `typical` 看是否已远超中位数);
|
|
65
|
+
- STALLED 且 worker 是 Agent CLI → 多半在长推理或等一个永远不来的交互,`stop --reason` 后按崩溃恢复重跑(中断的步重跑自身);
|
|
66
|
+
- STALLED 且 worker 是脚本 → 看末三行,通常是等外部资源(端口、锁、网络)。
|
|
67
|
+
|
|
68
|
+
想不盯屏就订阅 `STALLED` 通知([Approval 指南](07-approval-guide.md))。`watch --repo . --run <RUN> --once true` 可手工探测一次。
|
|
69
|
+
|
|
70
|
+
## 打扫卫生:gc(迭代 08)
|
|
71
|
+
|
|
72
|
+
终态 Run 会留下工作树、`run/*` 分支和偶尔的锁。`buildbeat-v2 gc --repo .` 默认只出计划,`--apply true` 执行:
|
|
73
|
+
|
|
74
|
+
- 只动**终态且已压成 run-record** 的 Run(Git 面有账才动运行时面);
|
|
75
|
+
- 工作树可删(提交都在分支上);脏工作树不带 `--force true` 不动;
|
|
76
|
+
- 分支只在候选**已可从其他 ref 到达**(已合并 / 打 tag / 在远端)或 Run 未产出候选时删;否则明示"仅此分支可达,保留"——它是证据的最后一根线;
|
|
77
|
+
- 终态 Run 的残留 `locks/<RUN>.lock` 一并清;`active-run` 锁仍按上文人工处置。
|
|
78
|
+
|
|
79
|
+
gc 永不写台账(终态后只允许 `RUN_COMPACTED`),所以随时可跑、可重复。
|
package/docs/v2/guide/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
| # | 文档 | 一句话 |
|
|
6
6
|
|---|---|---|
|
|
7
|
+
| 0 | [怎么和会话说话](00-how-to-talk.md) | **给用户看的**:项目从未开始到换期,每个阶段你说什么、会话做什么、你得到什么 |
|
|
7
8
|
| 1 | [快速开始](01-quickstart.md) | 5 分钟:装 beta → 写 run 配置 → 跑到合并决定 |
|
|
8
9
|
| 2 | [Workflow 编写指南](02-workflow-guide.md) | 步序、显式转换、readonly、terminal |
|
|
9
10
|
| 3 | [Policy 指南](03-policy-guide.md) | 四类 Policy、8 算子、三值逻辑、强制等级 |
|
|
@@ -16,3 +17,5 @@
|
|
|
16
17
|
| 10 | [故障恢复手册](10-recovery.md) | 台账损坏、Run 中断、锁、runtime 全删重建 |
|
|
17
18
|
|
|
18
19
|
observe v0(探测→分层响应→Intent 草稿→人分诊)在 [快速开始 §5](01-quickstart.md) 与 [Evidence 指南](06-evidence-guide.md) 中覆盖;schema 冻结见 [`RFC-0003 §8`](../RFC-0003-workflow-policy.md)。
|
|
20
|
+
|
|
21
|
+
迭代 08 起:`SKILL.md` §0.5 是给 AI 会话读的 v2 驾驶手册(用户一句话 → 会话调什么),v2 项目的装载入口模板在 [`templates/v2/`](../../../templates/v2/AGENTS.md)。
|
package/lessons.md
CHANGED
|
@@ -125,3 +125,40 @@
|
|
|
125
125
|
**根因**:BuildBeat 规定了 `pm/status/{视角}.md` 的持久状态写法,却只笼统要求「一屏收尾」,没有给面向人的回复一个简单统一的出口。模型便按各自任务的局部叙事优化,交接信息结构自然漂移。
|
|
126
126
|
|
|
127
127
|
**解药**:每个 AI 视角面向人收口时统一用「已做 → 未做 → 下一步」。`已做`只写功能/业务结果,证据紧跟它支持的事项;多项共用才放一条共同证据。`未做`必须写原因,同时承载未验证边界。本域完成就说下一棒是谁、做什么;未完成就说需要谁提供或确认什么;自己还能继续就不伪求助。格式只约束收口,不约束中间探索;持久真相仍在 Git/status/证据文件。通用原则:**统一交接接口,不统一模型怎么思考。**
|
|
128
|
+
|
|
129
|
+
## 20. Worker 顺手起的名字,所有者要连问四轮
|
|
130
|
+
|
|
131
|
+
**症状**:一个非生产的健康聚合服务被 AI 会话按内部术语命名(形如 `<内部术语>-nonprod`),域名也照此申请;所有者在会话里连问「这是干啥的」「名字怎么起的」「非生产?后面还要建生产?」「不做会影响什么」,最后自己改成一个业务上听得懂的名字。同期「服务 4 小时自停」也是 worker 按契约自定,所有者见到才问「为什么需要 4 个小时」。
|
|
132
|
+
**根因**:名字、时长这类参数在实现视角里是"细节",在所有者视角里是"以后天天要念的东西";流程只把契约/范围/不可逆动作列为决策项,没把**可见命名**列进去,于是它们从 worker 手里直接落地。
|
|
133
|
+
**解药**:凡所有者以后要看见或念出来的名字与参数(域名、服务名、环境名、自停时长、窗口时长)默认 `BATCH_AT_GATE`——写进 intent 或门前决策卡,给推荐值和理由,人一次批;reviewer 清单把「引入未经批准的可见命名」记 P2(v2 AGENTS 模板第 ⑪ 条、Skill §0.5.2)。通用原则:**决策项的边界按"谁以后要面对它"划,不按"实现上是不是细节"划。**
|
|
134
|
+
|
|
135
|
+
## 21. Run 停下来了,但没人知道;人守着屏幕,但看不见时间
|
|
136
|
+
|
|
137
|
+
**症状**:两个子仓 58 个 Run 里 32 个被取消,多数是在等人批的状态挂满一天后被批量清掉,人批平均等 7~12 小时;另一边所有者守着一场部署战役时,在同一会话里问了十几次「半小时了正常吗」「十分钟了是卡住了吗」「现在到底是谁在干活?」。事后清理时又发现 16 个终态 Run 的工作树无人收拾。
|
|
138
|
+
**根因**:Run 的状态只存在于台账和 `inbox`,不主动出站——人只有开一个 AI 会话问才知道有东西等他;`status` 只有步骤和次数,worker 输出被同步 spawn 缓冲到步结束才可见,人手里没有任何时间读数;新 Run 起跑不作废旧等待,残留物也没有回收命令。
|
|
139
|
+
**解药**:`status` 带每步耗时、同仓历史中位数、最后一次输出距今与末几行,无输出超阈值标 `STALLED`(只标不杀);`.buildbeat/notify.yaml` 把 `HUMAN_REQUESTED / RUN_TERMINAL / STALLED` 出站到钉钉或 webhook(URL 只走环境变量,失败不影响 Run);同 Work 新 Run 自动作废旧等待;`gc` 按"终态且已压账、候选可从别处到达"的规则清工作树;`overview` 回答「到哪了、下一步该谁」。通用原则:**等待必须能找到人,时间必须能被读到;做不到这两条,人就会用反复追问来补,而追问本身就是流程债。**
|
|
140
|
+
|
|
141
|
+
## 22. 多仓 map 假设"一仓一版本一 CHANGELOG",真实工作区三条都不成立
|
|
142
|
+
|
|
143
|
+
**症状**:试点工作区登记 `buildbeat-multirepo-map:v1` 后,unverified 从 4 条涨到 10 条:后端是多模块仓(根下没有 CHANGELOG,每个服务一份);CLI 仓的 CHANGELOG 是 npm 包 semver,跟能力契约版本根本不是一个域;只读存量前端没有任何契约;两个真有契约关系的仓,CHANGELOG 首行是「Deployed 日期 · sha」而不是版本号。map 的本意(契约↔实现↔部署三源对齐)对这套仓形状一条都核不上,结果是"登记了反而更吵",会话只能在「不登记(4 条 absent)」和「登记(10 条各种缺来源)」之间选噪音更小的。
|
|
144
|
+
**根因**:模板把 Keep a Changelog 单包仓当成唯一形状;"缺来源保留 unverified"是对的,但缺的不是来源而是**表达能力**——没有办法说"这个仓的版本在这个子路径"和"这个仓就是没有契约版本域"。
|
|
145
|
+
**解药**:map 行可选 `changelog=<仓内模块路径>`;`contract=n/a` 只登记不核对(注释里写清不得拿它掩盖真实契约关系);被核对的 CHANGELOG 首个已发布标题以契约版本开头(`## [v1.3 · Deployed …]`),Deployed·sha 信息保留在版本号之后。登记后两个有契约的仓 ✅、两个无版本域的仓一行登记,把契约版本临时改成 v1.4 能触发 conflict,门确实在守。
|
|
146
|
+
**同批发现**:`overview` 的 `next:` 让人"record the work as closed in decisions.jsonl",但代码里没有任何读者,12 个已关闭 Work 常年显示 `READY_TO_RUN`——提示里出现的每个动作都要有读它的代码,否则是给人挖坑。
|
|
147
|
+
|
|
148
|
+
## 23. 预算是刹车不是墙:人批了内核当没批,按 Run 计的预算又被"每轮一个新 Run"绕过
|
|
149
|
+
|
|
150
|
+
**症状**:review 预算(预设 2 轮)耗尽后停人,人批准 `resume-review`,内核在下一轮起跑前再判一次"超预算",同一请求立刻回来;run-config 改不动预设里的数。驾驶会话只好在 Run 外另找 reviewer 做 closure,两条已上生产的应用登录 Run 都以 CANCELLED 收场。另一头,像素风 Work 每修一轮就起一个新 Run,21 个 Run、9 轮 review、22 条 finding,每 Run 2 轮的封顶一次没触发;驾驶会话口头"止损"三次才真正停手。platform-health 烧了 10 个 Run 约一天,所有者在 Gate4 后才以"成本太高"砍掉,此前没有任何地方能看到花了多少。
|
|
151
|
+
**根因**:预算只是一个数,不是一个可以被人延展的台账事实;预算的计数单位(Run)和人的决策单位(Work)不一致;成本只在事后复盘时才被算出来。
|
|
152
|
+
**解药**:批准预算耗尽的 `resume-<step>` 即落 `BUDGET_EXTENDED`,该步上限 +1;run-config `budgets:` 覆盖预设;`budgets.reviewRoundsPerWork` 跨本 Work 所有 Run(含作废、含已压账)累计 review 轮数,达标在 review 起跑前停人;`overview` 每个 Work 一行 `cost:`(review 轮 / finding / 等人次 / worker 时长),intent 模板加止损线。通用原则:**预算的计数单位要和人做决定的单位一致,预算耗尽后的人批必须改变内核状态,否则人批等于没批。**
|
|
153
|
+
|
|
154
|
+
## 24. worker 基础设施故障被当成候选失败:杀 Run、派 fixer、烧预算
|
|
155
|
+
|
|
156
|
+
**症状**:worker 后端断服(review 退出码 97)、reviewer 输出不是 JSON、fix 步超时,三种都走"no transition for (review, failed)"直接终态 FAILED,两天 5 个 Run 这样死;驾驶会话为了等后端恢复手写探针,每两分钟起一个"Reply with exactly: OK"会话,共 14 次。另一类:verify 因 PATH 缺 rg、端口 4173 撞车、宿主负载 280、守卫误报被判失败,5 次派了 fixer 去修一个没问题的候选。
|
|
157
|
+
**根因**:step 状态词汇里已有 timeout / crashed / invalid-output,但路由把它们和"候选没过"一起压成 `failed`;verify 脚本没有办法告诉内核"是环境不行不是代码不行";没有边的失败一律终态。
|
|
158
|
+
**解药**:timeout / crashed / invalid-output / 退出码 75(`EX_TEMPFAIL`,约定为"环境不可用")判 `infra`:不记失败指纹、不派 fixer、不扣预算(`infraAttempts` 抵回),停 `WAITING_HUMAN`(kind `infra`)等后端恢复;没有转移边的失败也停人而不终态,终态 FAILED 只剩 policy BLOCK。模板 worker 合同写清三条环境事实(沙箱不能监听端口、PATH 只认 POSIX、环境不满足 `exit 75`)。通用原则:**先问"这次失败说的是候选还是环境",再决定谁来修;答不上来的失败交给人,不交给 fixer。**
|
|
159
|
+
|
|
160
|
+
## 25. 台账说的和人看到的不是一回事:已上线显示为取消、已发布显示为"没东西可合"、doctor 过了 start 却被挡
|
|
161
|
+
|
|
162
|
+
**症状**:候选已合入生产的 Work 因最后一个 Run 是 CANCELLED 而显示 `STOPPED_CANCELLED`,`next:` 催重试;四个已关窗的 release 车道 Work 显示 `MERGE_READY`、"nothing to merge";已合并的 Work 仍报 14 条未裁决 finding;`doctor` 通过两次,`start` 都被"plan 未镜像到子仓"的 policy 挡在 build;驾驶会话在 worktree 里手修并提交后,只能批准 enter-fix 让 fixer 空跑再多一次 verify(前端 Run 因此 verify 5 次、fix 3 次)。
|
|
163
|
+
**根因**:阶段判定只看最后一个 Run 的终态,不看候选是否已在主干;overview 不认识 release 车道;doctor 检查的是配置合法性,不是 start 第一道门会读的事实;内核只认 worker 产出的候选,没有"人供候选"的入口。
|
|
164
|
+
**解药**:候选合入主干即 `MERGED`(哪怕最新 Run 取消),release 车道成功关窗即 `RELEASED`,已合并/已发布/已关闭不再提示未裁决数;`doctor` 打印本仓 intent/plan 存在与接受状态,并按 policy 预告 start 会停在哪一步;`resume --adopt <sha>` 以人为 actor 钉候选、从 verify 续跑。**同批发现**:仓内 `.buildbeat/worktrees/` 会被 vitest 等按文件系统收集的框架当成测试目录(模板 gitignore 与指南补排除);`start` 被仓锁挡住时只说"another run is active",现在打印持锁 Run 与 `status` 命令,锁本身未放开。通用原则:**任何"到哪了"的读数都必须从事实(主干、车道、门)推导,而不是从最后一条记录的状态字段抄。**
|
package/package.json
CHANGED
package/src/v2/adapters/mock.js
CHANGED
|
@@ -11,7 +11,7 @@ export class MockScriptError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const BEHAVIORS = new Set(["succeed", "fail", "timeout", "crash", "invalid-output"]);
|
|
14
|
+
const BEHAVIORS = new Set(["succeed", "fail", "timeout", "crash", "invalid-output", "env-fail"]);
|
|
15
15
|
|
|
16
16
|
export function createMockAdapter(script) {
|
|
17
17
|
const remaining = {};
|
|
@@ -53,12 +53,19 @@ export function createMockAdapter(script) {
|
|
|
53
53
|
return { ...base, exitCode: 0 };
|
|
54
54
|
case "fail":
|
|
55
55
|
return { ...base, exitCode: 1, stderr: "mock failure" };
|
|
56
|
+
case "env-fail":
|
|
57
|
+
// Exit 75 (EX_TEMPFAIL): the worker says its environment, not the
|
|
58
|
+
// candidate, is what failed.
|
|
59
|
+
return { ...base, exitCode: 75, stderr: "mock environment unavailable" };
|
|
56
60
|
case "timeout":
|
|
57
61
|
return { ...base, exitCode: null, timedOut: true };
|
|
58
62
|
case "crash":
|
|
59
63
|
return { ...base, exitCode: null, signal: "SIGKILL" };
|
|
60
64
|
case "invalid-output":
|
|
61
|
-
|
|
65
|
+
// The kernel judges the envelope, not stdout: hand back a string
|
|
66
|
+
// that is not JSON so parseEnvelope fails the way a real worker's
|
|
67
|
+
// prose reply does.
|
|
68
|
+
return { ...base, exitCode: 0, stdout: "not-a-valid-worker-envelope", envelope: "not-a-valid-worker-envelope" };
|
|
62
69
|
default:
|
|
63
70
|
throw new MockScriptError(`unreachable behavior: ${behavior}`);
|
|
64
71
|
}
|