@haiyangbg/buildbeat 3.3.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.en.md +20 -124
  3. package/README.md +20 -124
  4. package/SKILL.md +73 -84
  5. package/docs/CAPABILITY-MATRIX.md +30 -39
  6. package/docs/MIGRATION.md +93 -0
  7. package/docs/README.md +8 -37
  8. package/docs/RELEASING.md +4 -3
  9. package/docs/v2/RFC-0001-product-definition.md +3 -0
  10. package/docs/v2/RFC-0002-domain-model.md +3 -0
  11. package/docs/v2/RFC-0003-workflow-policy.md +3 -0
  12. package/docs/v2/SPEC-0001-events-v1.md +3 -0
  13. package/docs/v2/guide/00-how-to-talk.en.md +12 -56
  14. package/docs/v2/guide/00-how-to-talk.md +12 -56
  15. package/docs/v2/guide/01-quickstart.en.md +42 -111
  16. package/docs/v2/guide/01-quickstart.md +42 -111
  17. package/docs/v2/guide/02-workflow-guide.en.md +13 -123
  18. package/docs/v2/guide/02-workflow-guide.md +14 -122
  19. package/docs/v2/guide/03-policy-guide.en.md +6 -50
  20. package/docs/v2/guide/03-policy-guide.md +6 -50
  21. package/docs/v2/guide/04-adapter-guide.en.md +7 -60
  22. package/docs/v2/guide/04-adapter-guide.md +7 -60
  23. package/docs/v2/guide/05-worker-contract.en.md +6 -53
  24. package/docs/v2/guide/05-worker-contract.md +7 -52
  25. package/docs/v2/guide/06-evidence-guide.en.md +8 -44
  26. package/docs/v2/guide/06-evidence-guide.md +9 -43
  27. package/docs/v2/guide/07-approval-guide.en.md +8 -110
  28. package/docs/v2/guide/07-approval-guide.md +8 -121
  29. package/docs/v2/guide/09-security-boundaries.en.md +13 -37
  30. package/docs/v2/guide/09-security-boundaries.md +13 -37
  31. package/docs/v2/guide/10-recovery.en.md +12 -96
  32. package/docs/v2/guide/10-recovery.md +12 -96
  33. package/docs/v2/guide/11-session-handoff.en.md +10 -75
  34. package/docs/v2/guide/11-session-handoff.md +10 -75
  35. package/docs/v2/guide/12-without-runtime.en.md +46 -0
  36. package/docs/v2/guide/12-without-runtime.md +46 -0
  37. package/docs/v2/guide/README.en.md +16 -36
  38. package/docs/v2/guide/README.md +16 -36
  39. package/example/AGENTS.md +15 -69
  40. package/example/README.md +8 -23
  41. package/example/delivery/envelope/prompts/builder.md +3 -1
  42. package/example/delivery/envelope/prompts/fixer.md +3 -1
  43. package/example/delivery/envelope/prompts/reviewer.md +3 -1
  44. package/example/delivery/work/WORK-EXPORT-DATE-FILTER/run-config.yaml +1 -1
  45. package/example//346/214/207/346/214/245/345/217/260.md +8 -40
  46. package/package.json +2 -1
  47. package/src/v2/adapters/notification-payload.js +40 -0
  48. package/src/v2/adapters/shell.js +3 -1
  49. package/src/v2/cli/run-config-check.js +129 -45
  50. package/src/v2/cli/run.js +853 -254
  51. package/src/v2/engine/reducer.js +2 -0
  52. package/src/v2/engine/risk-preset.js +14 -47
  53. package/src/v2/engine/workflow.js +177 -185
  54. package/src/v2/evidence/collector.js +12 -1
  55. package/src/v2/evidence/image.js +346 -0
  56. package/src/v2/policy/policy.js +219 -286
  57. package/src/v2/presets/software-delivery.yaml +1 -16
  58. package/src/v2/runtime/cache.js +22 -1
  59. package/src/v2/runtime/decisions.js +185 -31
  60. package/src/v2/runtime/findings.js +4 -1
  61. package/src/v2/runtime/notify.js +42 -36
  62. package/src/v2/runtime/orchestrator.js +811 -297
  63. package/src/v2/runtime/overview-repos.js +79 -0
  64. package/src/v2/runtime/overview.js +279 -47
  65. package/src/v2/runtime/release.js +233 -0
  66. package/src/v2/runtime/run-record.js +1 -0
  67. package/src/v2/workspace/workspace-manager.js +8 -0
  68. package/templates/v2/AGENTS.md +20 -73
  69. package/templates/v2/BUILDBEAT.md +2 -13
  70. package/templates/v2/envelope/prompts/builder.md +3 -1
  71. package/templates/v2/envelope/prompts/fixer.md +3 -1
  72. package/templates/v2/envelope/prompts/reviewer.md +3 -1
  73. package/templates/v2/run-config.example.yaml +9 -7
  74. package/templates/v2/work.example.md +16 -0
  75. package/templates/v2//346/214/207/346/214/245/345/217/260.md +8 -40
  76. package/docs/v2/skill/01-principles.md +0 -26
  77. package/docs/v2/skill/02-project-layout.md +0 -31
  78. package/docs/v2/skill/03-collaboration-rules.md +0 -45
  79. package/docs/v2/skill/04-rhythm-and-rituals.md +0 -104
  80. package/docs/v2/skill/05-red-lines.md +0 -13
  81. package/docs/v2/skill/06-bootstrap-and-takeover.md +0 -84
  82. package/docs/v2/skill/07-templates-and-lessons.md +0 -24
  83. package/example/.buildbeat/observe.yaml +0 -31
  84. package/src/v2/observe/observe-config.js +0 -194
  85. package/src/v2/observe/observe-reducer.js +0 -117
  86. package/src/v2/observe/observe.js +0 -427
  87. package/src/v2/presets/observe.yaml +0 -45
  88. package/src/v2/presets/policies/ui-render-gate.yaml +0 -13
  89. package/src/v2/presets/release-readback.yaml +0 -36
  90. package/src/v2/presets/risk/controlled.yaml +0 -39
  91. package/src/v2/presets/risk/fast.yaml +0 -19
  92. package/src/v2/presets/risk/release.yaml +0 -21
  93. package/src/v2/presets/risk/standard.yaml +0 -28
  94. package/templates/ARCHITECTURE.md +0 -50
  95. package/templates/contracts/PROTOCOL.md +0 -28
  96. package/templates/pm/adr/ADR-0000-template.md +0 -25
  97. package/templates/pm/adr/README.md +0 -15
  98. package/templates/pm/decisions.md +0 -11
  99. package/templates/standards/CODE.md +0 -23
  100. package/templates/standards/DESIGN.md +0 -36
  101. package/templates/standards/REVIEW.md +0 -20
  102. package/templates/standards/STACK.md +0 -31
@@ -0,0 +1,46 @@
1
+ # 不装运行时也能参与
2
+
3
+ [English](12-without-runtime.en.md)
4
+
5
+ 没有 `buildbeat` 运行时的人或 AI 工具(没有 Node 的机器、网页版 AI 对话、受限的公司电脑)仍可以通过项目文件参与。它们没有自动闭环、隔离工作树、批准校验、预算和恢复,也不得声称拥有这些能力。
6
+
7
+ ## 可以读
8
+
9
+ | 文件(`delivery/work/<ID>/` 下) | 内容 |
10
+ |---|---|
11
+ | `work.md` | 目标、范围、验收、实施计划 |
12
+ | `decisions.jsonl` | 接受、批准、关窗等决定,一行一个 JSON |
13
+ | `review-findings.jsonl` | 审查发现与人的裁决 |
14
+ | `runs/<RUN>/run-record.json` | 已结束 Run 的终态、候选、证据摘要与成本 |
15
+ | `releases.jsonl` | 合并后的上线回读记录 |
16
+
17
+ 正在进行的 Run 台账在运行它的机器的 `.buildbeat/runtime/` 中,不进 Git;进度以那一方的 `buildbeat status` 为准。
18
+
19
+ ## 可以写
20
+
21
+ - **起草或修改 `work.md`**。修改会让已有接受失效,运行时会显示为待接受或已过期。
22
+ - **由工作所有者接受 `work.md`**:在 `decisions.jsonl` 末尾追加一行,`digest` 是 `work.md` 文件内容的 SHA-256(例如 `shasum -a 256 work.md`),`decisionRef` 用 `A-<ID>-<当前行数+1>`:
23
+
24
+ ```json
25
+ {"ts":"2026-10-04T08:00:00.000Z","decisionRef":"A-WORK-X-1","decision":"approved","transition":"accept-work","subject":{"artifact":"work","digest":"sha256:<64 位十六进制>"},"by":"<姓名>"}
26
+ ```
27
+
28
+ - **裁决已记录的审查发现**:在 `review-findings.jsonl` 末尾追加一行,`fingerprint` 必须是已有发现的指纹,`action` 为 `accept` 或 `dismiss`:
29
+
30
+ ```json
31
+ {"ts":"2026-10-04T08:00:00.000Z","kind":"adjudication","fingerprint":"<指纹>","action":"dismiss","by":"<姓名>","note":"<理由>"}
32
+ ```
33
+
34
+ - **关闭不需要上线的 Work**(例如只改文档):追加 `{"ts":"2026-10-04T09:00:00.000Z","decision":"closed","transition":"close-work","subject":{"result":"<结论>"},"by":"<姓名>"}`。需要上线的 Work 用运行时的 `release` 与 `decide --action close` 关窗,关窗会绑定回读记录。
35
+
36
+ 每行必须是完整的一行 JSON,只追加不改旧行。写完后提交(commit),交给装有运行时的一方。
37
+
38
+ ## 不得做
39
+
40
+ - 批准或拒绝 Run、采纳候选:这些决定由运行时写入 Run 台账,并在盖章前重新回读候选与证据。
41
+ - 写或改 Run 台账、`run-record.json`、`releases.jsonl` 或任何证据;声称验证、审查或上线回读已经通过。
42
+ - 以别人的名义写决定;修改已有行。
43
+
44
+ ## 交给装有运行时的一方
45
+
46
+ 对方拉取提交后运行 `buildbeat status --repo . --work <ID>`:新改的 `work.md` 显示为待接受,手写的接受行与裁决行按同样的摘要与指纹规则生效。之后 `buildbeat run --config <config>` 继续交付。
@@ -1,36 +1,16 @@
1
- # BuildBeat v2 documentation
2
-
3
- [简体中文](README.md) | **English**
4
-
5
- > The normative authority is the RFCs/SPEC ([`RFC-0001`](../RFC-0001-product-definition.md) product definition / [`RFC-0002`](../RFC-0002-domain-model.md) domain model / [`RFC-0003`](../RFC-0003-workflow-policy.md) workflow and policy / [`SPEC-0001`](../SPEC-0001-events-v1.md) event schema; all in Chinese); this directory is the operator's view, and where it conflicts with the implementation, the RFCs/SPEC and the code win — please report it. Design history (V2-PLAN, iteration records) is not in this directory.
6
-
7
- ## First use
8
-
9
- | Document | In one line |
10
- |---|---|
11
- | [How to talk to a session](00-how-to-talk.en.md) | **For users**: from not started to the next phase, what you say at each stage, what the session does, what you get |
12
- | [Quickstart](01-quickstart.en.md) | The first Run: install `@latest` → work item and run config → accept → doctor → start → read the evidence and decide; failure branches included |
13
- | [`templates/v2/`](../../../templates/v2/AGENTS.md) | Project entry points (AGENTS / CLAUDE / command board / BUILDBEAT marker), run config sample, envelope prompts and the worker wrapper |
14
-
15
- People working in an AI session only need document 0; the session reads the driving manual in `SKILL.md` §0.5.
16
-
17
- ## Day to day
18
-
19
- | Document | In one line |
20
- |---|---|
21
- | [Human approval guide](07-approval-guide.en.md) | inbox / approve / stale; what accept, approving a transition and the merge decision each mean; the triage gate; waits must reach a person; overview |
22
- | [Evidence guide](06-evidence-guide.en.md) | Read-back evidence, status/grade, the UNVERIFIED culture, observe |
23
- | [Session and team handoff](11-session-handoff.en.md) | Writing context down, closing the old chat, a new member taking over, cross-tool and cross-machine boundaries |
24
- | [Recovery](10-recovery.en.md) | Corrupted ledger, interrupted Run, infra stops, locks, rebuilding after deleting the runtime, gc |
25
-
26
- ## Configuration reference
27
-
28
- | Document | In one line |
29
- |---|---|
30
- | [Workflow authoring guide](02-workflow-guide.en.md) | Step order, explicit transitions, readonly, terminal, budgets, cache, requires, parallel runs |
31
- | [Policy guide](03-policy-guide.en.md) | Four policy types, 8 operators, three-valued logic, enforcement levels |
32
- | [Adapter guide](04-adapter-guide.en.md) | Shell/Mock, the env allowlist and `env:` injection, plugging in any CLI agent, live output |
33
- | [Worker contract](05-worker-contract.en.md) | The input/output envelope (`severity` + `summary`), blocking semantics, per-role discipline, the wrapper script |
34
- | [Security and permission boundaries](09-security-boundaries.en.md) | What the kernel actually does vs what cannot be concluded from it; the three preconditions for running unattended |
35
-
36
- observe v0 (probe → tiered response → intent drafts → human triage) is covered in [Quickstart §9](01-quickstart.en.md) and the [Evidence guide](06-evidence-guide.en.md); its frozen schema is [`RFC-0003 §8`](../RFC-0003-workflow-policy.md).
1
+ # BuildBeat guides
2
+
3
+ - [00-how-to-talk](00-how-to-talk.en.md)
4
+ - [01-quickstart](01-quickstart.en.md)
5
+ - [02-workflow-guide](02-workflow-guide.en.md)
6
+ - [03-policy-guide](03-policy-guide.en.md)
7
+ - [04-adapter-guide](04-adapter-guide.en.md)
8
+ - [05-worker-contract](05-worker-contract.en.md)
9
+ - [06-evidence-guide](06-evidence-guide.en.md)
10
+ - [07-approval-guide](07-approval-guide.en.md)
11
+ - [09-security-boundaries](09-security-boundaries.en.md)
12
+ - [10-recovery](10-recovery.en.md)
13
+ - [11-session-handoff](11-session-handoff.en.md)
14
+ - [12-without-runtime](12-without-runtime.en.md)
15
+
16
+ [Migration](../../MIGRATION.md)
@@ -1,36 +1,16 @@
1
- # BuildBeat v2 使用文档
2
-
3
- **简体中文** | [English](README.en.md)
4
-
5
- > 规范权威在 RFC/SPEC([`RFC-0001`](../RFC-0001-product-definition.md) 产品定位 / [`RFC-0002`](../RFC-0002-domain-model.md) 域模型 / [`RFC-0003`](../RFC-0003-workflow-policy.md) workflow 与 policy / [`SPEC-0001`](../SPEC-0001-events-v1.md) 事件 schema);本目录是操作视角,与实现冲突时以 RFC/SPEC 与代码为准并回报。设计历史(V2-PLAN、迭代记录)不在本目录。
6
-
7
- ## 第一次使用
8
-
9
- | 文档 | 一句话 |
10
- |---|---|
11
- | [怎么和会话说话](00-how-to-talk.md) · [English](00-how-to-talk.en.md) | **给用户看的**:项目从未开始到换期,每个阶段你说什么、会话做什么、你得到什么 |
12
- | [快速开始](01-quickstart.md) · [English](01-quickstart.en.md) | 第一个 Run:装 `@latest` → 工作项与 run 配置 → accept → doctor → start → 看证据拍板;含失败分支 |
13
- | [`templates/v2/`](../../../templates/v2/AGENTS.md) | 项目装载入口(AGENTS / CLAUDE / 指挥台 / BUILDBEAT 标记)、run 配置样板、信封 prompt 与 worker 包装 |
14
-
15
- 在 AI 会话里用的人只需读第 0 篇;会话读 `SKILL.md` §0.5 驾驶手册。
16
-
17
- ## 日常使用
18
-
19
- | 文档 | 一句话 |
20
- |---|---|
21
- | [Human Approval 指南](07-approval-guide.md) · [English](07-approval-guide.en.md) | inbox / approve / stale;接受、批准某转换、合并决定五词各指什么;分诊门;等待要能找到人;overview |
22
- | [Evidence 指南](06-evidence-guide.md) · [English](06-evidence-guide.en.md) | 回读制证据、状态/等级、UNVERIFIED 文化、observe |
23
- | [跨会话与团队接续](11-session-handoff.md) · [English](11-session-handoff.en.md) | 上下文落盘、关闭旧聊天、新成员接手、跨工具与跨机器边界 |
24
- | [故障恢复手册](10-recovery.md) · [English](10-recovery.en.md) | 台账损坏、Run 中断、infra 停人、锁、runtime 全删重建、gc |
25
-
26
- ## 配置参考
27
-
28
- | 文档 | 一句话 |
29
- |---|---|
30
- | [Workflow 编写指南](02-workflow-guide.md) · [English](02-workflow-guide.en.md) | 步序、显式转换、readonly、terminal、预算、cache、requires、并行 Run |
31
- | [Policy 指南](03-policy-guide.md) · [English](03-policy-guide.en.md) | 四类 Policy、8 算子、三值逻辑、强制等级 |
32
- | [Adapter 指南](04-adapter-guide.md) · [English](04-adapter-guide.en.md) | Shell/Mock、env 白名单与 `env:` 注入、接任意 CLI Agent、实时输出 |
33
- | [Worker 合同](05-worker-contract.md) · [English](05-worker-contract.en.md) | 输入输出信封(`severity` + `summary`)、阻断语义、各角色纪律、包装脚本 |
34
- | [安全与权限边界](09-security-boundaries.md) · [English](09-security-boundaries.en.md) | 内核实际做到的 vs 不能由此推出的;无人值守三层前置条件 |
35
-
36
- observe v0(探测→分层响应→Intent 草稿→人分诊)在 [快速开始 §9](01-quickstart.md) 与 [Evidence 指南](06-evidence-guide.md) 中覆盖;schema 冻结见 [`RFC-0003 §8`](../RFC-0003-workflow-policy.md)。
1
+ # BuildBeat 使用指南
2
+
3
+ - [00-how-to-talk](00-how-to-talk.md)
4
+ - [01-quickstart](01-quickstart.md)
5
+ - [02-workflow-guide](02-workflow-guide.md)
6
+ - [03-policy-guide](03-policy-guide.md)
7
+ - [04-adapter-guide](04-adapter-guide.md)
8
+ - [05-worker-contract](05-worker-contract.md)
9
+ - [06-evidence-guide](06-evidence-guide.md)
10
+ - [07-approval-guide](07-approval-guide.md)
11
+ - [09-security-boundaries](09-security-boundaries.md)
12
+ - [10-recovery](10-recovery.md)
13
+ - [11-session-handoff](11-session-handoff.md)
14
+ - [12-without-runtime](12-without-runtime.md)
15
+
16
+ [Migration](../../MIGRATION.md)
package/example/AGENTS.md CHANGED
@@ -1,73 +1,19 @@
1
- # AGENTS.md — 简账 工作区 · BuildBeat 协作契约
1
+ # Project delivery instructions
2
2
 
3
- > 本文件走开放标准 `AGENTS.md`,由工作区下的会话按各工具自己的方式装载(Claude Code / Codex / Cursor / Gemini CLI / Aider / Zed 等多数会自动读根目录 `AGENTS.md` 或 `CLAUDE.md`;不自动读的工具由人开场贴给会话——用哪个工具就按它的文档核对一次,不要假设)。目的:每个会话开工即知道「当前工作在哪 / 我是什么视角 / 读哪 / 写哪 / 该调哪条命令」,不靠人转述上下文。
4
- > **层叠规则**(标准语义):会话从被编辑文件所在目录向上收集沿途所有 `AGENTS.md` 合并,**离得越近优先级越高**。本文件只写全局的(路由 / 协作规则 / 红线),各代码子仓的局部细节写进**该仓自己的 `AGENTS.md`**。
5
- > 根目录 `CLAUDE.md` 只是一行指针(兼容只认该文件名的工具),内容单点在本文件。单仓项目,没有 `ARCHITECTURE.md` 与 `contracts/`。
6
- > **本仓运行 BuildBeat**(运行时 `@haiyangbg/buildbeat@3.0.0`,`buildbeat` 由会话调用,人不必手敲)。
3
+ Load BuildBeat's Skill according to the host tool's rules. Read project-owned
4
+ constraints, delivery/work/<ID>/work.md (or legacy intent/plan), Git and status.
5
+ One Work owns one user-visible result. Continue safe authorized work to its
6
+ acceptance condition; intermediate commits are progress, not completion.
7
7
 
8
- ## 0. 工作怎么发生(一页流程)
8
+ Use accept once the user has accepted the scope, run to execute/continue, status
9
+ to inspect, decide for a real human decision, and check for diagnostics. Keep
10
+ candidate/evidence/approval binding, allowedPaths, read-only review, and failure
11
+ budgets. Do not impersonate a human approval or silently weaken requirements.
9
12
 
10
- 1. **工作项**:每件事一个 `delivery/work/<WORK-ID>/`(`intent.md` 为什么做 + **止损线**(最多几个 Run / 几轮 review / 几小时,越线先问所有者"继续还是砍")+ `plan.md` 怎么做,可选 `env-facts.md` 记踩出来的环境事实);被 digest 绑定接受(`buildbeat accept`)前只是草稿、不产生义务。`overview` 的 `cost:` 行就是止损线的读数。
11
- 2. **代码工作跑 Run**:`buildbeat start --config <run-config.yaml> --attempt new` → 隔离 worktree 内 Build→Verify→Fix→Review 自动闭环 → **停在合并决定**。push、合并、部署永远是人批之后的人类动作。
12
- 3. **人怎么知道该做什么**:`buildbeat overview --repo .` 回答「每件事走到哪、下一步该谁」;`inbox` 只列等人批的 Run,每条后面附可复制的下一句命令;`status --run <RUN>` 回答「还在动吗、动了多久、卡没卡」。
13
- 4. **上线**:生产动作是人的;`release-readback` 预设 + `release` 风险预设把「做之前回读 → 人做 → 做之后回读 → 观察 → 人关窗」记成 L4 证据,任一步失败即停人批。
14
- 5. **observe 盯生产**:`buildbeat observe run --config .buildbeat/observe.yaml` 一次=一轮只读体检;异常分层(落账→只读诊断→intent 草稿入队 `delivery/observe/intents/`),草稿**绝不自动执行**,人用 `observe triage` 分诊。
15
- 6. **拍板台账**:平台级真实决策包一行进 `pm/decisions.md`(从 `templates/pm/decisions.md` 拷,`pm/` 下只有这一个文件与可选的 `adr/`);Run 级批准落各 Work 的 `decisions.jsonl`;finding 裁决落 `review-findings.jsonl`。单仓项目没有 `contracts/`。
16
- 7. **通知**:`.buildbeat/notify.yaml` 配一条通道(URL 只能来自环境变量),Run 停在人批 / 终态 / 疑似卡住会来找人。
17
- 8. **打扫**:终态 Run 留下的工作树用 `buildbeat gc --repo .` 清(默认只出计划)。工作树在仓内 `.buildbeat/worktrees/`:`.gitignore` 排除 `.buildbeat/runtime/` 与 `.buildbeat/worktrees/`,测试框架的收集范围也要排除 `**/.buildbeat/**`(vitest `exclude`、jest `testPathIgnorePatterns`、pytest `norecursedirs`),否则主干测试会把旧候选的用例一起跑。
18
- 9. **worker 信封**:`delivery/envelope/`(从 `templates/v2/envelope/` 拷)放 `worker.sh` 与 builder / reviewer / fixer 的 prompt,run 配置 `envelope.prompts` 指向它;换工具只改 run 配置里 `--` 后的命令。**worker 环境事实(写进 prompt)**:worker 的沙箱通常**不能监听端口**,需要起服务或绑定 loopback 的集成测试交给 verify 步,worker 只跑单测与静态检查,不要反复尝试;PATH 只认 POSIX 工具(`grep -E` 不用 `rg`,`find` 不用 `fd`)或在 `requires:` 里声明;verify / 包装脚本发现环境不满足(命令不在 PATH、端口被占、后端 404)就 `exit 75`,内核会当基础设施故障停人、不派 fixer、不扣预算。
13
+ Respect the host sandbox and environment contract. Missing tools or unavailable
14
+ backends are infrastructure failures (exit 75). Never print or commit secrets.
15
+ Stage only owned files. Merge, push, publication and deployment require their
16
+ own authorization. Existing project standards stay under project ownership.
19
17
 
20
- ## 1. 工作包路由 —— Builder 端到端负责,会话按 AI 视角隔离
21
-
22
- > 协作单元是需求/功能工作包(= Work)。一个 Builder 对工作包的产品判断、实现、测试、合并与发布证据端到端负责;下表是可调用的 AI 专业视角和文件写边界,不是人类岗位或固定交接流水线。共享事实走 Git(`delivery/` 与 Run 台账)。
23
-
24
- | AI 视角 | cwd | 可写(拥有) | 只读 | 开工先读 |
25
- |---|---|---|---|---|
26
- | **产品**(规格/编排) | 仓根 | `delivery/**`、`pm/decisions.md` | 全仓 | `buildbeat overview --repo .` |
27
- | **全栈**(实现) | 仓根 | `src/**`、`tests/**`(Run 内受 `allowedPaths` 机器约束) | `delivery/*` | 所属 Work 的 intent/plan + `run-config.yaml` |
28
- | **测试**(独立核验) | 仓根 | `tests/**`、独立核验报告(落所属 Work 目录) | 实现 + 规格 | 所属 Work |
29
-
30
- > 🔴 **边界**:新地盘 `src/export.js` 与 `tests/`;`src/ledger.js` 是记账核心,只维护、改动一律先问人;不得借道写入 `delivery/work/*/runs/`(内核写)。
31
- > 🔴 **写者≠审者的机器化**:Run 内置 fresh-context 只读 reviewer(快照强制,写入即失败落账);merge 门绑定 candidate + plan + 证据 digest,过期即 stale。
32
- > **开工/收工护栏**:任意会话开工先各仓 `git pull`,再 `buildbeat overview --repo .`(活动 Work、等人的 Run、成本);收工前再跑一次 `overview` 并把 warning / unverified 原样写进收口。生产状态问 `observe status`,不猜。
33
-
34
- ## 1.5 UI 规范摘要(非 UI 项目可删)
35
-
36
- - 延用既定设计语言与 token;每个可见界面处理加载 / 空 / 错误 / 移动端四态。
37
- - **界面零元注释**:上线的可见界面不得出现给"做的人"看的文字;每次上线核查门必查。
38
- - UI 交付的拍板对象必须含可渲染证据(真渲染入口 + 截图 digest);静态描述不构成拍板对象。
39
-
40
- ## 2. 协作规则
41
-
42
- **① 唯一入口** —— 活动工作看 `delivery/`(`overview` / `inbox`);不另建进度文件、状态文件或看板,进度由内核从台账与 Git 回读。
43
- **② 接口先落盘再动手** —— 单仓项目没有跨边界契约;对外导出格式(CSV 列序、日期格式)的变化先写进所属 Work 的 plan 并被接受,再动代码。
44
- **③ 交接靠 candidate hash + 台账** —— Run 停在合并决定时 candidate 已由 Git 回读固定;跨会话接力读 `delivery/work/<id>/` 即知全部事实,hash 不得编造。
45
- **④ 护栏与不可逆动作** —— 开工 `overview`;部署/改契约/migration 等不可逆动作前再核一次并走人批;exit 0 不消除 `warning/unverified`。
46
- **⑤ 风险分轨** —— Risk Preset:`fast`(仅 merge 人批)/ `standard`(plan+merge,默认)/ `controlled`(intent+plan+merge+release)/ `release`(上线回读车道)。
47
- **⑥ 核查门** —— Run 内 reviewer 只读、结构化 findings;`reviewTriage: required` 时 P0/P1 先过人分诊再派 fixer;review 不收敛(修过的 finding 又出现,或阻断数多于上一轮)即停人,每 Work 默认 6 轮兜底(`reviewRoundsPerWork`)。**完成 = hash + 可核验证据**;标准轨最低 L3,上线必须 L4。`UNVERIFIED` 永不当作通过。
48
- **⑦ 状态单点** —— 事实进 Run 证据与 Work 记录;进度看 `overview`,度量看 `metrics`(本地只读)。
49
- **⑧ 视觉问题带图对比** —— 提 UI bug 必附『实现截图 ⟷ 设计稿截图』并排 + 标注差异点。
50
- **⑨ 单点事实** —— 线上版本只信实查(`observe status` / 部署平台);任何文档不写「当前线上 vX」;每个收敛后的真实决策包只在 `pm/decisions.md` 记一行;历史台账不回改。
51
- **⑩ 真渲染拍板** —— 有 UI 的拍板对象必须是真渲染证据。
52
- **⑪ 所有者可见命名进决策卡** —— 域名、服务名、环境名、自停时长、窗口时长等**所有者以后要看见或要念出来的名字与参数**,不由 worker 顺手定:进 intent 或门前决策卡(`BATCH_AT_GATE`),给推荐值和理由(用业务上听得懂的名字,不用内部术语)。
53
-
54
- ## 2.5 任务包与人批节奏
55
-
56
- **任务包信封** —— 开工时从用户目标与活动 Work 明确 `objective / in_scope / terminal_condition`(即 Work 的 intent/plan)。默认一个工作包覆盖多个子项;每个会话同时只认领一个。只要仍有安全、可逆、在范围内且能推进目标的工作,会话就继续做,不因子产物完成交还接力棒。
57
-
58
- **域回复格式** —— 面向用户收口、交接或回复明确检查点时,统一写「已做 → 未做 → 下一步」;已做/未做默认各总结成一句,不习惯性 1234 分条;只有事项性质差异大时才列举,每条仍是"结果+证据"。
59
-
60
- **审批三级**:`STOP_NOW`(跨发布门 / 扩范围 / 改冻结契约 / 不可逆外部动作 / 接受风险);`BATCH_AT_GATE`(冻结前可逆取舍、默认值、阈值、可见命名——攒到门前一次批 2~5 个);`NO_APPROVAL`(能实查的事实、派生约束、文档归档、普通 P2)。
61
-
62
- **人批预算** —— 每个工作包、每道门默认 1 个 `BATCH_AT_GATE` 请求;待批项必须带 findings 摘要与风险声明。
63
-
64
- > **元原则:能实查的不问人** —— 查代码 / 配置 / 部署平台 / `overview` / `status` / `observe status` 能得到的事实,不拿去问用户、不信文档、不信上游转述。
65
-
66
- ## 3. 红线(每个会话受约束)
67
-
68
- 1. **凭据不入 git、不出本机**:文档只标位置不写值;本地 .env gitignore + 600;机器闸 = gitleaks pre-commit;Worker 默认 env 白名单;通知 URL 只能来自环境变量。
69
- 2. **不 `git add -A`**:只 stage 当前工作包拥有的具体文件;各仓分别提交。
70
- 3. **不未授权部署**、不 force-push、不 `--amend` 已推送历史、不 `--no-verify`。Run 的合并决定只表示候选具备合并条件(`SUCCEEDED` ≠ 已合并),合并/push/发布是其后的人类动作、逐项授权。
71
- 4. **每次部署完必更对应仓 `CHANGELOG.md`**;部署后 `observe run` 一轮。
72
- 5. **写者≠审者**:Run 内置只读 reviewer 机器强制;写者转述不构成证据。
73
- 6. **事实分层**:代码已确认事实 / 运行时待核事实 / 拟议需求 / 已实现行为,四类严格分开;未实查一律写「待核」。
18
+ Preserve active runtime files and worktrees when switching sessions. Candidates
19
+ and terminal records must remain reachable before cleanup.
package/example/README.md CHANGED
@@ -1,25 +1,10 @@
1
- # example/ —— 可直接拷的示例:虚构项目「简账」跑完一个 Work 的快照
1
+ # Example project
2
2
 
3
- > 这是一个**单仓、无 UI、一个 Builder 端到端**的最小项目,展示 `templates/` 填好项目事实之后长什么样,以及一个 Work 从立项、接受、Run、合并决定到合并之后,仓库里留下哪些文件。项目、人物、决策全部虚构。
3
+ This fictional ledger project retains its original intent/plan, decisions and
4
+ terminal run record as a legacy-readback fixture. Its old official workflow
5
+ remains pinned, not overwritten with the new default. The regression suite runs
6
+ a copied project through the current compatibility path with scripted workers.
4
7
 
5
- ## 里面是什么
6
-
7
- | 路径 | 是什么 | 来源 |
8
- |---|---|---|
9
- | `AGENTS.md` / `CLAUDE.md` / `指挥台.md` / `BUILDBEAT.md` | 会话装载入口、一行指针、给人看的操作卡、运行时版本标记 | `templates/v2/` 填好项目名、边界、视角路由 |
10
- | `gitignore.template` | 拷入后改名 `.gitignore`(npm 包不带点文件,所以这里不带点) | `templates/gitignore.template` |
11
- | `pm/decisions.md` | 平台级拍板台账:接入时的【BOOTSTRAP】决策包和本 Work 的【EXPORT-1】决策包各一行 | `templates/pm/decisions.md` |
12
- | `.buildbeat/notify.yaml` / `observe.yaml` | 通知通道(URL 只走环境变量)与只读体检配置的样例 | 按 [Approval 指南](../docs/v2/guide/07-approval-guide.md) / [Evidence 指南](../docs/v2/guide/06-evidence-guide.md) |
13
- | `delivery/envelope/` | worker 包装脚本 + builder / reviewer / fixer 三份 prompt,末尾补了本项目的环境事实 | `templates/v2/envelope/` |
14
- | `delivery/work/WORK-EXPORT-DATE-FILTER/` | 一个完整的 Work:`intent.md`(为什么 + 止损线)、`plan.md`、`run-config.yaml`、`workflow.yaml`(官方预设的逐字副本)、`decisions.jsonl`、`runs/RUN-EXPORT-01/run-record.json` | 前四份人写;后两份是运行时产出(见下) |
15
- | `src/` / `tests/` / `package.json` | 应用本体:`ledger.js` 是"老地盘"(只维护),`export.js` 是本 Work 加了日期筛选之后的样子,`npm test` 是 verify 步跑的真实命令 | 虚构 |
16
-
17
- ## 快照的时点与哪些是机器写的
18
-
19
- 快照 = **所有者批准合并决定并合并候选之后**的仓库。`decisions.jsonl` 里的三条(接受 intent、接受 plan、合并决定)和 `runs/RUN-EXPORT-01/run-record.json` 不是手写的:它们是把本目录放进一个一次性 Git 仓库、用脚本 worker 代替 `codex exec` 真跑一遍 `accept → doctor → start → approve` 得到的原始输出,只在拷出时去掉了本机路径(运行时本来就不输出绝对路径)。因此 run-record 里的 `candidate` / `base` 两个 commit 指向那个一次性仓库,在你拷出的仓库里查不到——真项目里它们必须可以 `git cat-file -t` 查到。review 的那条 P2 finding 也是脚本 worker 按 reviewer 合同返回的样例。
20
-
21
- `tests/example-firstrun.test.js` 锁住两件事:这些工件彼此一致(plan 的 digest 与批准对象一致、无绝对路径),以及把本目录原样拷进一个新仓库后,用脚本 worker 能再起一个 Run(`RUN-EXPORT-02`)跑到合并决定,verify 步跑的是本项目真实的 `npm test`。它不证明某个真实模型能完成任务。
22
-
23
- ## 怎么用它起自己的项目
24
-
25
- 不要整目录照抄——项目名、边界、视角、验证命令都是简账的。按 [SKILL.md](../SKILL.md) §8 的"自查 → 少量提问 → 一屏确认 → 生成"走,让会话从 `templates/` 生成;本目录只用来对照"填好之后应该长什么样"。想手动核对命令,按 [快速开始](../docs/v2/guide/01-quickstart.md):把 `run-config.yaml` 里 `--` 后面的 `codex exec …` 换成你实际用的工具,`accept` 之后 `doctor`,再 `start --attempt new`。
8
+ New work should use [work.example.md](../templates/v2/work.example.md) and the
9
+ [quickstart](../docs/v2/guide/01-quickstart.md), without copying a workflow file.
10
+ The example does not prove real AI business capability.
@@ -1,6 +1,6 @@
1
1
  你是本 Run 的 builder,在一个隔离的 git worktree 里工作(当前目录)。
2
2
 
3
- 1. 读环境变量 `BUILDBEAT_INPUT`(JSON):`workId` / `runId` / `step` / `attempt`。目标与计划在 `delivery/work/<workId>/intent.md` 与 `plan.md`——只做 plan 里写的,plan 没写的记到 `delivery/work/<workId>/notes.md` 交给人,不要顺手做。
3
+ 1. 读环境变量 `BUILDBEAT_INPUT`(JSON):`workId` / `runId` / `step` / `attempt`。优先读取 `workArtifact.ref` 指定的已确认工作说明;新工作是 `delivery/work/<workId>/work.md`,其中包含目标、范围、验收和实施计划。没有这个输入字段时,先检查 work.md;只有 work.md 不存在时才回退到旧 intent.md 与 plan.md。只做确认范围内的事,范围外想法交给人,不要为满足旧路径而另造一套计划。
4
4
  2. 只改 run 配置 `allowedPaths` 列出的目录(通常是 `src` 与 `tests`);越界改动不会成为候选。
5
5
  3. 不要 `git commit`、不要 `git push`、不要改分支:提交由包装脚本机械完成,push 已被封禁。
6
6
  4. 沙箱通常不能监听端口:需要起服务的集成测试交给 verify 步,你只跑单测与静态检查;PATH 只认 POSIX 工具(`grep -E` 不用 `rg`)。
@@ -8,3 +8,5 @@
8
8
  6. 改完自检一次能跑的测试;把做了什么、没做什么、下一步各一句写到 stdout 末尾。
9
9
 
10
10
  本项目(简账)的环境事实:Node ≥ 20,测试命令 `npm test`(`node --test tests/*.test.js`),零依赖、不用装包;沙箱不能监听端口,本项目也没有需要端口的测试;`src/ledger.js` 是老地盘,只维护、不重构;PATH 只认 POSIX 工具。
11
+
12
+ 若 workArtifact.ref 指向旧 plan.md,同时读取同目录的 intent.md(如存在);work.md 存在时不要拼接旧文件覆盖新范围。
@@ -1,6 +1,6 @@
1
1
  你是本 Run 的 fixer,在隔离的 git worktree 里工作(当前目录)。你只修被指出的问题,不做别的。
2
2
 
3
- 1. 读环境变量 `BUILDBEAT_INPUT`(JSON):
3
+ 1. 读环境变量 `BUILDBEAT_INPUT`(JSON),对照 workArtifact.ref 指定的 work.md(缺失字段时优先 work.md,否则回退旧 intent.md/plan.md)确认范围:
4
4
  - 有 `findings[]`:这是上一轮 review 的结论及裁决状态,**只修 `adjudication` 为 `accepted` 或 `open` 的条目**,`dismissed` 的不碰;
5
5
  - 没有 `findings`:说明是 verify 失败进来的。失败日志在主仓 `.buildbeat/runtime/runs/<runId>/logs/verify-<n>.log`(`<n>` 是最近一次 verify 的 attempt;从 worktree 找主仓:`cd "$(git rev-parse --git-common-dir)/.."`),里面有命令、退出码、stdout / stderr。先读日志,再改代码。
6
6
  2. 只改 run 配置 `allowedPaths` 列出的目录;不要 `git commit` / `git push`(提交由包装脚本完成,push 已封禁)。
@@ -8,3 +8,5 @@
8
8
  4. 改完跑一次相关测试;把修了哪条、没修哪条及原因各一句写到 stdout 末尾。
9
9
 
10
10
  本项目(简账)的环境事实:Node ≥ 20,测试命令 `npm test`(`node --test tests/*.test.js`),零依赖、不用装包;沙箱不能监听端口,本项目也没有需要端口的测试;`src/ledger.js` 是老地盘,只维护、不重构;PATH 只认 POSIX 工具。
11
+
12
+ 若 workArtifact.ref 指向旧 plan.md,同时读取同目录的 intent.md(如存在);work.md 存在时不要拼接旧文件覆盖新范围。
@@ -2,7 +2,7 @@
2
2
 
3
3
  1. 读环境变量 `BUILDBEAT_INPUT`(JSON)。若有 `lastReviewed.range`,只审 `git diff <range>` 的增量;否则审本分支相对 base 的全部改动(`git log --oneline` 与 `git diff HEAD~1` 起步,按需扩大)。
4
4
  2. `BUILDBEAT_INPUT.anchor` 是历史 finding 与人的裁决:**已裁决(accepted / dismissed)的结论不得翻案**;同一问题不要换措辞重提。
5
- 3. 对照 `delivery/work/<workId>/plan.md`:做了 plan 没写的事、引入了未经批准的可见命名(域名 / 服务名 / 环境名)→ 记 P2。
5
+ 3. 对照 `BUILDBEAT_INPUT.workArtifact.ref` 指定的已确认工作说明。新工作使用 `delivery/work/<workId>/work.md`;没有该输入字段时优先 work.md,只有它不存在才回退到旧 intent.md 与 plan.md。检查目标、范围、验收与实施计划,不以缺失的旧文件判断新工作。范围外改动或未经批准的可见命名记为具体 finding。
6
6
  4. 严重度:P0 数据丢失 / 安全 / 不可逆;P1 功能错误或测试未覆盖计划要求的行为;P2 可维护性、命名、边界;P3 建议。只有 P0 / P1 会阻断。
7
7
  5. **最后只输出一个 JSON 对象,不要别的文字**(可以裹一层 ```json 代码栏):
8
8
 
@@ -11,3 +11,5 @@
11
11
  ```
12
12
 
13
13
  没有问题就 `"findings": []`。每条必须有 `severity`(P0–P3)和 `summary`;缺字段或不是 JSON 会被当成 worker 故障停人,而不是候选缺陷。
14
+
15
+ 若 workArtifact.ref 指向旧 plan.md,同时读取同目录的 intent.md(如存在);work.md 存在时不要拼接旧文件覆盖新范围。
@@ -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
@@ -1,40 +1,8 @@
1
- # 指挥台 — 简账:Builder 怎么驱动工作(忘了怎么开场就看这页)
2
-
3
- > 本仓运行 BuildBeat。会话按所用工具的方式装载 `AGENTS.md`(多数工具自动读根目录 `AGENTS.md` / `CLAUDE.md`;不自动读的,开场把它贴给会话);活动工作在 `delivery/`。你基本只需说下面这几句话,命令由会话调、不用你手敲。
4
-
5
- ## 日常六句话
6
-
7
- | 你想干什么 | 说什么 | 会话背后调什么 |
8
- |---|---|---|
9
- | 看现在到哪了、该谁动 | 「当前进度」/「待办是什么」 | `buildbeat overview --repo .`(每个 Work 的阶段 + 下一步)+ `observe status` |
10
- | 看有什么等我批 | 「有什么要我拍板」 | `buildbeat inbox --repo .`(每条附可复制的下一句) |
11
- | 立一件新事 | 「开个 Work:〔一句话目标〕」→ 看完说「接受」 | 建 `delivery/work/<ID>/intent.md + plan.md` → `accept`(digest 绑定) |
12
- | 让它干活 | 「开工」/「再来一轮」 | 先 `buildbeat doctor --config …`(配置、intent/plan 接受状态、env 姿态、预算),再 `start --config … --attempt new`(自动编号、自动作废旧等待,停在合并决定) |
13
- | 它是不是卡了 | 「怎么样了」/「卡住了吗」 | `buildbeat status --repo . --run <RUN>`(耗时、历史中位数、最后输出、STALLED) |
14
- | 拍板 | 「批准 / 拒绝〔RUN-ID〕」;分诊时「这条接受,那条不算」 | `approve` / `reject` / `findings adjudicate`;会话说清批的是哪一步(放行 fixer / 再跑一次 / 合并决定),非终态批准后 `resume` 续跑;合并决定只表示候选够格合并,合并/push/部署逐项另说 |
15
-
16
- 其他:生产报警看 `delivery/observe/intents/` 草稿 → 「fix_now / schedule / dismiss」;上线用 `release-readback` 预设开一个 Run,「我做完了」就是批准 `enter-apply-readback`;「打扫卫生」= `gc --repo .`(先看计划再 `--apply true`)。
17
-
18
- ## 新开一个 Work 时会话要动的文件
19
-
20
- `delivery/work/<WORK-ID>/`:`intent.md`(为什么 + 止损线)、`plan.md`(怎么做)、`run-config.yaml`(从 `templates/v2/run-config.example.yaml` 改 work / run / allowedPaths / workers)、`workflow.yaml`(从 `$(npm root -g)/@haiyangbg/buildbeat/src/v2/presets/software-delivery.yaml` 复制)。信封与 worker 包装在仓级 `delivery/envelope/`,不用每个 Work 重写。
21
-
22
- ## 视角开场白
23
-
24
- | AI 视角 | 在哪开(cwd) | 你的一句话 |
25
- |---|---|---|
26
- | **产品** | 工作区根 | 「把当前目标收成一个 Work」/「验收」——定 intent/plan、维护决策收件箱、按决策包记 `pm/decisions.md` |
27
- | **全栈** | `<代码仓>/` | 「〔WORK-ID〕该你了,开工」——授权范围内实现,Run 停在合并决定回人批 |
28
- | **测试** | 工作区根 | 「验〔RUN-ID〕候选」——对精确 candidate 独立核验,报告落所属 Work 目录 |
29
-
30
- ## 域回复怎么写
31
-
32
- 面向你收口统一「已做 → 未做 → 下一步」,各一句;证据紧跟事项。下一步只写一条:下一棒是谁、需要你确认什么、或「我继续做,暂不交棒」。
33
-
34
- ## 按项目阶段的完整问法
35
-
36
- 未开始 → 立项定方案 → 准备执行 → 执行推进 → 验收合并 → 上线 → 完结换期复盘,每阶段说什么、会话做什么、你得到什么:见 BuildBeat 指南第 0 篇 `docs/v2/guide/00-how-to-talk.md`(Skill 装载目录下同路径)。
37
-
38
- ## 红线、写边界、审批三级、可见命名进决策卡
39
-
40
- 全在 `AGENTS.md`,本页不复制。
1
+ # Daily work
2
+
3
+ - Prepare the goal, scope, acceptance and plan in work.md.
4
+ - Accept once the owner has decided.
5
+ - Continue with run; inspect with status.
6
+ - Resolve the exact pending decision; resume afterward when nonterminal.
7
+ - Diagnose with check; check --step executes a real command outside the ledger.
8
+ - Preserve context and active runs before handoff. Cleanup plans precede apply.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haiyangbg/buildbeat",
3
- "version": "3.3.1",
3
+ "version": "4.1.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": {
@@ -11,6 +11,7 @@
11
11
  "docs/README.md",
12
12
  "docs/CAPABILITY-MATRIX.md",
13
13
  "docs/RELEASING.md",
14
+ "docs/MIGRATION.md",
14
15
  "docs/v2/",
15
16
  "!docs/v2/M[124]-*.md",
16
17
  "example/",
@@ -0,0 +1,40 @@
1
+ // Outbound channel presentation; the runtime owns event timing and delivery.
2
+ function renderText(notification) {
3
+ const lines = [
4
+ notification.title,
5
+ `work: ${notification.work} status: ${notification.status}`,
6
+ ];
7
+ for (const reason of notification.reasons ?? []) {
8
+ lines.push(`- ${reason}`);
9
+ }
10
+ if (notification.candidate) {
11
+ lines.push(`candidate: ${notification.candidate}`);
12
+ }
13
+ for (const shot of notification.screenshots ?? []) {
14
+ lines.push(`screenshot: ${shot.ref} ${shot.digest}`);
15
+ }
16
+ if (notification.nextReply?.length) {
17
+ lines.push("next:");
18
+ for (const line of notification.nextReply) {
19
+ lines.push(` ${line}`);
20
+ }
21
+ }
22
+ return lines.join("\n");
23
+ }
24
+
25
+ export function payloadFor(channel, notification) {
26
+ if (channel.type === "dingtalk") {
27
+ // DingTalk custom robots require a configured keyword in the text; the
28
+ // title carries it (default "BuildBeat").
29
+ const text = renderText(notification);
30
+ return {
31
+ msgtype: "text",
32
+ text: {
33
+ content: text.includes(channel.keyword)
34
+ ? text
35
+ : `${channel.keyword}\n${text}`,
36
+ },
37
+ };
38
+ }
39
+ return { ...notification, text: renderText(notification) };
40
+ }
@@ -67,7 +67,7 @@ export function createShellAdapter(config) {
67
67
  return {
68
68
  name,
69
69
  envMode,
70
- execute({ step, worker, workspacePath, input, timeoutMs, outputPath, liveDir, promptPath, vars }) {
70
+ execute({ step, worker, workspacePath, input, timeoutMs, outputPath, liveDir, promptPath, vars, extraEnv }) {
71
71
  const context = { step, worker, workspacePath, promptPath, vars };
72
72
  const args = (config.args ?? []).map((arg) => fillTemplate(String(arg), context));
73
73
  const command = [config.command, ...args].join(" ");
@@ -91,6 +91,8 @@ export function createShellAdapter(config) {
91
91
  if (promptPath) {
92
92
  env.BUILDBEAT_PROMPT = promptPath;
93
93
  }
94
+ // Step-specific variables the kernel sets (BUILDBEAT_SCREENSHOT_DIR).
95
+ Object.assign(env, extraEnv ?? {});
94
96
 
95
97
  let live = null;
96
98
  let stdio = "pipe";