@haiyangbg/buildbeat 3.2.0 → 3.2.1
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 +10 -0
- package/README.en.md +3 -3
- package/docs/README.md +1 -0
- package/docs/RELEASING.md +2 -2
- package/docs/v2/RFC-0001-product-definition.md +1 -1
- package/docs/v2/RFC-0002-domain-model.md +1 -1
- package/docs/v2/RFC-0003-workflow-policy.md +1 -1
- package/docs/v2/SPEC-0001-events-v1.md +1 -1
- package/docs/v2/guide/00-how-to-talk.en.md +61 -0
- package/docs/v2/guide/00-how-to-talk.md +2 -0
- package/docs/v2/guide/02-workflow-guide.en.md +125 -0
- package/docs/v2/guide/02-workflow-guide.md +3 -1
- package/docs/v2/guide/03-policy-guide.en.md +55 -0
- package/docs/v2/guide/03-policy-guide.md +2 -0
- package/docs/v2/guide/04-adapter-guide.en.md +66 -0
- package/docs/v2/guide/04-adapter-guide.md +2 -0
- package/docs/v2/guide/05-worker-contract.en.md +60 -0
- package/docs/v2/guide/05-worker-contract.md +2 -0
- package/docs/v2/guide/09-security-boundaries.en.md +41 -0
- package/docs/v2/guide/09-security-boundaries.md +3 -1
- package/docs/v2/guide/README.en.md +36 -0
- package/docs/v2/guide/README.md +8 -6
- package/package.json +3 -3
- package/src/v2/cli/run.js +28 -10
- package/src/v2/runtime/orchestrator.js +456 -401
package/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
> 本项目吃自己的狗粮(红线④:必更 CHANGELOG)。格式循 Keep a Changelog,倒序。
|
|
4
4
|
|
|
5
|
+
## v3.2.1 — 2026-09-26(补丁:开关参数单写、drive() 拆分、信封脚本测试、英文指南)
|
|
6
|
+
|
|
7
|
+
- CLI 开关参数可单写:`--json`、`--apply`、`--force`、`--once` 不必再跟 `true`(旧写法照旧有效);缺值时报错点名参数(`--config needs a value`),多余的裸词报 `unexpected argument`。
|
|
8
|
+
- `drive()` 从 459 行的单一循环体拆成五个有名字的阶段函数(原代码按原顺序搬移,循环本身 19 行),review 步判定收成一处 `isReviewStep()`;行为零变化——未改动任何测试,全量通过。
|
|
9
|
+
- CodeQL 也分析推到 `v2` 的提交。
|
|
10
|
+
- M-1 脚本驱动试点 `pilot/` 归档到 `docs/history/pilot/`,不再进 CI 与发布前检查;CI 必需检查「Script behavior」改测随包发给用户的信封 `worker.sh`(`tests/envelope-worker.test.sh`,18 项,macOS 上用 `/bin/bash` 3.2 跑),`test:envelope` 取代 `test:pilot`。
|
|
11
|
+
- 7 份只有中文的指南补齐英文版(怎么和会话说话、Workflow、Policy、Adapter、Worker 合同、安全边界、指南索引),中英互相切换;英文 README 改指英文指南;顺带更正两处 3.2.0 后过时的「每仓只有一个活动 Run」表述。
|
|
12
|
+
|
|
5
13
|
## v3.2.0 — 2026-09-26(并行 Run 开关、docs 归档、SKILL.md 瘦身、测试卫生)
|
|
6
14
|
|
|
15
|
+
> **发布状态**:`@haiyangbg/buildbeat@3.2.0` 已于 2026-09-26 从 `main`(PR #50 内容、release PR #51,merge commit `cda4bd6`,tag `v3.2.0`)经 OIDC Trusted Publishing 发布到 dist-tag **`latest`**(run 36233928546,publish 与 verify 双 job 一次 success;所有者授权「推送并发 3.2.0 / 继续」)。独立回读(直连 npmjs.org):`latest` = 3.2.0、integrity 与发布前本地候选逐字一致、SLSA v1 provenance、隔离安装 `--version` = 3.2.0、裸调用零写入、包内 7 份 `docs/v2/skill` 参考在位且无 `docs/history|releases`、已发布包的 `doctor` 正确报告 `parallel` 模式、`npm audit signatures` 通过;GitHub Release v3.2.0 标 Latest,证据见 [`docs/releases/V3.2.0-RELEASE-EVIDENCE-2026-09-26.md`](docs/releases/V3.2.0-RELEASE-EVIDENCE-2026-09-26.md)。
|
|
16
|
+
|
|
7
17
|
- 修正 3.1.0 延后的两个 P2:run-config「显式 null 报错」只作用于会静默回落默认值的标量键(`cache: null` 重新表示不开缓存);YAML 多行列表项恢复修改前的判定与报错原文。
|
|
8
18
|
- 并行 Run(开关,默认关):run 配置 `parallel: true` 的 Work 可与其他同样打开开关的 Work 同时驱动,同一 Work 的 Run 仍互斥;未打开的 Run 照旧独占仓库,两种模式互不越界(独占 Run 持有 `active-run` 全程,并行 Run 只在建立自己的标记时短暂经过它)。共享仓库的 git 写操作(建/删 worktree、分支、`.git/config`)改在短时 `@repo-git` 锁内执行;`gc` 同样回收持有者已死的 `@work` / `@parallel` / `@repo-git` 锁。`doctor` 打印当前模式。
|
|
9
19
|
- 测试不再泄漏临时目录:所有测试经 `tests/support/tmp.js` 的 `tempDir()` 建临时目录并在文件结束时删除;一次全量测试从留下 147 个目录(24 MB)降到 0,`tests/v2-test-hygiene.test.js` 禁止测试文件直接调用 `mkdtempSync`。
|
package/README.en.md
CHANGED
|
@@ -50,7 +50,7 @@ The project files carry the context needed to continue. A fresh session with Bui
|
|
|
50
50
|
|
|
51
51
|
**Taking over anytime and anywhere starts with accessible records, a working environment, and appropriate permissions.** Whether you continue yourself or hand work to someone else, start from project files without carrying the old chat transcript. Git supplies version control and collaboration; repository hosting and execution platforms control access.
|
|
52
52
|
|
|
53
|
-
Different tools can read and write the protocol. Running the Loop also requires a compatible adapter, permissions, and environment. Existing real Worker evidence covers `codex exec`, with deterministic tests for script Workers. Having a CLI alone does not establish that another tool is verified. See the [capability matrix](docs/CAPABILITY-MATRIX.md) and [adapter guide](docs/v2/guide/04-adapter-guide.md) (Chinese).
|
|
53
|
+
Different tools can read and write the protocol. Running the Loop also requires a compatible adapter, permissions, and environment. Existing real Worker evidence covers `codex exec`, with deterministic tests for script Workers. Having a CLI alone does not establish that another tool is verified. See the [capability matrix](docs/CAPABILITY-MATRIX.md) and [adapter guide](docs/v2/guide/04-adapter-guide.en.md) (Chinese).
|
|
54
54
|
|
|
55
55
|
## Multiple perspectives, one shared objective
|
|
56
56
|
|
|
@@ -86,7 +86,7 @@ The diagram shows normal and repair paths. Risk presets, finding triage, infrast
|
|
|
86
86
|
- **Interruption has a recovery path:** the Runner can resume from its ledger. An interrupted step may run again; a dirty worktree requires a decision first.
|
|
87
87
|
- **Loops have limits:** budgets, repeated failures, and infrastructure problems become explicit pending actions. Notifications are configurable.
|
|
88
88
|
|
|
89
|
-
The merge decision means the candidate is ready for a merge. A human or a separately authorized tool performs merge, push, and deployment outside the Runner. The `release-readback` workflow can record release checks and observations. Runtime checks and host isolation have distinct scopes; see [security and permission boundaries (Chinese)](docs/v2/guide/09-security-boundaries.md).
|
|
89
|
+
The merge decision means the candidate is ready for a merge. A human or a separately authorized tool performs merge, push, and deployment outside the Runner. The `release-readback` workflow can record release checks and observations. Runtime checks and host isolation have distinct scopes; see [security and permission boundaries (Chinese)](docs/v2/guide/09-security-boundaries.en.md).
|
|
90
90
|
|
|
91
91
|
## Start your first handoff
|
|
92
92
|
|
|
@@ -142,7 +142,7 @@ Once configured, talk to the session directly:
|
|
|
142
142
|
|
|
143
143
|
BuildBeat fits ongoing projects with frequent AI context changes, specialist collaboration, and a need for verifiable delivery records. Individuals can keep their own work moving; teams can hand work over through shared records. One-off scripts and very small changes usually do not need the full workflow. The project should have real verification commands, or establish minimum verification first.
|
|
144
144
|
|
|
145
|
-
Teams collaborate through a shared Git repository and project agreements. BuildBeat does not provide multi-user accounts, roles and permissions. It does not collect or upload project usage data and has no telemetry collection. Configured AI tools and notification services have their own data practices. More examples are in the [conversation guide (Chinese)](docs/v2/guide/00-how-to-talk.md).
|
|
145
|
+
Teams collaborate through a shared Git repository and project agreements. BuildBeat does not provide multi-user accounts, roles and permissions. It does not collect or upload project usage data and has no telemetry collection. Configured AI tools and notification services have their own data practices. More examples are in the [conversation guide (Chinese)](docs/v2/guide/00-how-to-talk.en.md).
|
|
146
146
|
|
|
147
147
|
## Learn more and contribute
|
|
148
148
|
|
package/docs/README.md
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
| 发布证据([`releases/`](releases/)) | 每个发布一份 `<版本>-RELEASE-EVIDENCE-<日期>.md`;当前 `latest` 是哪一版以 [`RELEASING.md`](RELEASING.md) 顶部的现状段与 registry 回读为准 |
|
|
31
31
|
| 规划与决策(2026-08) | [`V2-PLAN.md`](history/V2-PLAN.md)(执行基线,已交付)、[`V2-PROPOSAL.md`](history/V2-PROPOSAL.md)、[`V2-DECISIONS.md`](history/V2-DECISIONS.md)、[`V2-D2-DECISION-CARD.md`](history/V2-D2-DECISION-CARD.md)、[《BuildBeat v2:AI 原生软件交付控制平面》](history/BuildBeat%20v2%EF%BC%9AAI%20%E5%8E%9F%E7%94%9F%E8%BD%AF%E4%BB%B6%E4%BA%A4%E4%BB%98%E6%8E%A7%E5%88%B6%E5%B9%B3%E9%9D%A2.md) |
|
|
32
32
|
| 迭代与里程碑记录 | [`history/`](history/) 下的 `V2-ITERATION-01~08.md`、[`v2/`](v2/) 下的 M1/M2/M4 验收与试点记录 |
|
|
33
|
+
| M-1 脚本驱动试点(2026-08,已被运行时取代,不再执行) | [`history/pilot/`](history/pilot/README.md)(`loop.sh`、试点度量与证据) |
|
|
33
34
|
| 早期版本史 | [`../CHANGELOG-v1.md`](../CHANGELOG-v1.md)(2026-06 ~ 2026-08 的条目原文;当前条目在根 [`CHANGELOG.md`](../CHANGELOG.md)) |
|
|
34
35
|
| 早期路线与阶段试点(2026-08,已移除的文件总线时代) | [`ROADMAP.md`](history/ROADMAP.md)、[`EXECUTION-PLAN.md`](history/EXECUTION-PLAN.md)、[`history/`](history/) 下的 `PHASE1/2/4-*.md`、[`CLI-STRATEGY-2026-08.md`](history/CLI-STRATEGY-2026-08.md)、[`CLI-PILOT-2026-08-23.md`](history/CLI-PILOT-2026-08-23.md)、[`PHASE4-STABILITY-AUDIT-2026-08-25.md`](history/PHASE4-STABILITY-AUDIT-2026-08-25.md) |
|
|
35
36
|
|
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@3.2.
|
|
5
|
+
Release evidence at source package version `@haiyangbg/buildbeat@3.2.1`; latest independently verified BuildBeat npm distribution `@haiyangbg/buildbeat@3.2.0` (dist-tag `latest`; `next` stays `3.0.1`), anchored by annotated tag `v3.2.0` at commit `cda4bd6`, workflow run [36233928546](https://github.com/HaiYangBG1/BuildBeat/actions/runs/36233928546), and 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
|
|
|
@@ -36,7 +36,7 @@ bash -n .github/scripts/*.sh tests/*.sh
|
|
|
36
36
|
shellcheck -x .github/scripts/*.sh tests/*.sh
|
|
37
37
|
actionlint .github/workflows/*.yml
|
|
38
38
|
bash tests/check-docs.sh
|
|
39
|
-
npm run test:
|
|
39
|
+
npm run test:envelope
|
|
40
40
|
npm run test:plugin
|
|
41
41
|
npm test
|
|
42
42
|
npm publish --dry-run --access public --registry=https://registry.npmjs.org/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> 状态:`FINAL`(2026-08-28 项目所有者定稿,`V2-D3`;M0 随三份 RFC 与 [`SPEC-0001-events-v1.md`](SPEC-0001-events-v1.md) 定稿退出)
|
|
4
4
|
> 日期:2026-08-28
|
|
5
5
|
> 上游:[`V2-PLAN.md`](../history/V2-PLAN.md)(执行基线,`V2-D0=B`);内核范围:完整内核(`V2-D2=A`,[`V2-DECISIONS.md`](../history/V2-DECISIONS.md))
|
|
6
|
-
> 需求来源:M-1 试点记录——[`pilot/metrics.md`](
|
|
6
|
+
> 需求来源:M-1 试点记录——[`history/pilot/metrics.md`](../history/pilot/metrics.md)(能力矩阵 + 卡点 1–5)、[`history/pilot/evidence/2026-08-28-m1-runtime-gap.md`](../history/pilot/evidence/2026-08-28-m1-runtime-gap.md)(F5/F6)、[`V2-ITERATION-01.md`](../history/V2-ITERATION-01.md)
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> 状态:`FINAL`(2026-08-28 项目所有者定稿,`V2-D3`)
|
|
4
4
|
> 日期:2026-08-28
|
|
5
5
|
> 上游:[`V2-PLAN.md`](../history/V2-PLAN.md) §3–4;报告 B §6 / §13 / §14(经基线裁决修订)
|
|
6
|
-
> 需求来源:[`pilot/metrics.md`](
|
|
6
|
+
> 需求来源:[`history/pilot/metrics.md`](../history/pilot/metrics.md) 卡点 1–5;[`history/pilot/evidence/2026-08-28-m1-runtime-gap.md`](../history/pilot/evidence/2026-08-28-m1-runtime-gap.md)
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> 状态:`FINAL`(2026-08-28 项目所有者定稿,`V2-D3`;§8 observe/bands schema 已随定稿冻结)
|
|
4
4
|
> 日期:2026-08-28
|
|
5
5
|
> 上游:[`V2-PLAN.md`](../history/V2-PLAN.md) §3.2–3.7;报告 B §8–11 / WP1.4–1.5 / WP4.3–4.5
|
|
6
|
-
> 需求来源:[`pilot/metrics.md`](
|
|
6
|
+
> 需求来源:[`history/pilot/metrics.md`](../history/pilot/metrics.md) 卡点 3/5、故障矩阵 F1–F6;[`history/pilot/evidence/2026-08-28-m1-runtime-gap.md`](../history/pilot/evidence/2026-08-28-m1-runtime-gap.md)
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
> 日期:2026-08-28
|
|
5
5
|
> 冻结范围:**信封字段、通用规则、损坏处理、reducer 合同、初始事件类型注册表的语义**。文件摆放位置、快照格式、CLI 展示均为非规范内容,可变。
|
|
6
6
|
> 演进规则:一切修改 **additive-only**(新增可选字段、新增事件类型);破坏性变更必须升 `v` 并提供旧版读取器。
|
|
7
|
-
> 需求来源:[`pilot/metrics.md`](
|
|
7
|
+
> 需求来源:[`history/pilot/metrics.md`](../history/pilot/metrics.md) 卡点 1(无统一 ledger)、卡点 5(无 Run 登记);F5/F6 见 [`history/pilot/evidence/2026-08-28-m1-runtime-gap.md`](../history/pilot/evidence/2026-08-28-m1-runtime-gap.md)
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# How to talk to a session that has BuildBeat (by project stage)
|
|
2
|
+
|
|
3
|
+
[简体中文](00-how-to-talk.md) | **English**
|
|
4
|
+
|
|
5
|
+
> This page is for **users**. You work in any AI coding session (any tool will do, and one session is enough) that has loaded the BuildBeat Skill; you speak plainly, and the session calls `buildbeat`, reads the output and answers you as "done → not done → next". **You do not need to remember any command.** The example phrases below are what people normally say; copy them as they are. Phrases in the same cell mean the same thing; pick whichever comes naturally.
|
|
6
|
+
> The session-side rules that correspond to this page are in `SKILL.md` §0.5; project-specific routing and red lines are in each project's root `AGENTS.md`.
|
|
7
|
+
|
|
8
|
+
Before switching sessions, say "write the key context down, I am closing the old session"; in the new session say "read the project entry point, check progress and pending approvals, and carry on with this work". When handing over to a teammate, say "sync the project records and the candidate so the person taking over can check the current scope and the environment the original Run lives in". The detailed steps, and what an active Run needs to keep, are in [Session and team handoff](11-session-handoff.en.md).
|
|
9
|
+
|
|
10
|
+
## One table: a project from zero to the next phase
|
|
11
|
+
|
|
12
|
+
| Stage | What you want | Just say | What the session does behind it | What you get / watch for |
|
|
13
|
+
|---|---|---|---|---|
|
|
14
|
+
| **0. Not started** | Decide whether BuildBeat is worth it | "Is this project a good fit for BuildBeat?" | Looks at repository size, timeline, number of repositories / deployment units / sessions | A one-line verdict: worth it for multi-phase iterations / several repositories / several sessions; a job that wraps up within a week is simply done directly |
|
|
15
|
+
| | Set up a new project | "Set this project up with BuildBeat" "Initialise the collaboration skeleton" | Reads the code first → asks a few questions (stack, repositories, deployment units, whether there is a UI) → one confirmation screen → generates `AGENTS.md`, `delivery/` (with the envelope), `.buildbeat/observe.yaml`, `.buildbeat/notify.yaml`, a gitleaks pre-commit hook | One confirmation screen; it writes only after you say "OK", then lists the files it generated |
|
|
16
|
+
| | Take over an existing project | "Put this existing project on BuildBeat" "Take over this repository" | Surveys it (tests, contracts, deployment facts) → draws strangler boundaries (new ground / old ground / read-only) → phase 0 adds a minimal verification suite | A survey report plus a boundary draft; you decide the boundaries; history is not rewritten |
|
|
17
|
+
| | Turn on notifications | "Notify me when a Run stops" | Writes `.buildbeat/notify.yaml` (DingTalk / webhook) | It tells you which robot to create and which environment variable to `export`; the URL never enters Git |
|
|
18
|
+
| **1. Start a work · settle the approach** | Start something | "Open a Work: 〔one-line goal〕" "Turn the current goal into a Work" | Writes `delivery/work/<ID>/intent.md` (why, and what counts as done) + `plan.md` (how, in how many steps) + `run-config.yaml` | A summary + "say accept once you have read it"; only your "accept" makes the digest binding take effect |
|
|
19
|
+
| | See the approach before any code | "Don't touch the code yet, give me the approach" "Tell me the difference between A and B" | Produces drafts and comparisons only; no Run starts | A comparison + a recommendation + consequences; nothing is built before you decide |
|
|
20
|
+
| | Pick out what is mine to decide | "What in this approach needs my decision?" "Is there anything I need to decide?" | Compresses the acceptance list into real trade-offs (including names you will say out loud later: domains, service names, durations) | 2–5 decision items at once, each with a recommended value; no chain of one-by-one questions |
|
|
21
|
+
| | Decide | "Accept" "Go with all the recommendations" "Take B for the second one, recommendations for the rest" | `accept` the intent/plan; decisions land in `decisions.jsonl` / `pm/decisions.md` | A one-line confirmation; editing the plan makes the acceptance stale automatically, and it must be accepted again |
|
|
22
|
+
| | Set the risk level | "Use the fast track for this" "Be stricter with this one" | `riskPreset: fast / standard / controlled` | fast stops only before the merge; standard adds plan acceptance; controlled adds intent acceptance and the release gate |
|
|
23
|
+
| **2. Get ready to run** | Check the environment | "What does the environment need?" "Run a preflight" | Checks `requires:` (binary versions + `probe:` probes); `preflight --step` dry-runs up to the first failure boundary | Everything missing reported at once; a dry run produces no evidence, a real Run has to reproduce it |
|
|
24
|
+
| | Freeze the envelope | "Pin the prompts" "Freeze the envelope" | Adds `pin: <sha>` under `envelope:` | Every Run from then on records the envelopeDigest, so it can be traced |
|
|
25
|
+
| **3. Run · move forward** | Put it to work | "Go" "〔WORK-ID〕, your turn" | `start --config … --attempt new` (numbered automatically, older waiting runs superseded, started detached) | "RUN-X-02 has started; I will tell you when it stops at the merge decision" |
|
|
26
|
+
| | See progress | "Where are we?" "Current progress" "Per repository, please" | `overview` (each Work's stage + whose move is next) + `observe status` | One line per item: stage, who it is waiting on, next; no commands listed |
|
|
27
|
+
| | See the next step | "What's next?" "What do you need from me?" | The `next` lines of `overview`, only those in your hands | Only what you have to do: DNS, credentials, approvals, hands-on actions |
|
|
28
|
+
| | Worried it is stuck | "How's it going?" "Is it stuck?" "It has been half an hour, is that normal?" | `status --run` (time per step, historical median, last output, STALLED) | One line with numbers: "verify has run 14 minutes, the historical median is 6, last output 2 minutes ago, still moving"; a suspected stall is said plainly |
|
|
29
|
+
| | See what is waiting for me | "What needs my approval?" "What's waiting on me?" | `inbox` + `delivery/observe/intents/` | Item by item: what it waits for, where the evidence is, a recommended A/B |
|
|
30
|
+
| | Approve / refuse | "Approve" "Approve RUN-X" "Reject, because …" | `approve` / `reject`; after approving a non-terminal transition, `resume` continues the run | It says which step you approved: release the fixer / run once more / the merge decision; the merge decision only says the candidate is fit to merge, and merge / push / deploy are separate things you ask for |
|
|
31
|
+
| | Rule on a finding | "This one doesn't count, accept that one" "This is a false positive" | `findings adjudicate dismiss/accept` → releases the fixer | The same dismissed finding no longer blocks; a higher severity reopens it |
|
|
32
|
+
| | Another round | "One more round" "Continue" | A new attempt | The old waiting run is superseded automatically; the inbox shows only live items |
|
|
33
|
+
| | Stop | "Stop if it fails again this time" "Stop for now" | Caps the rounds; `stop --reason` records it, and the candidate and evidence are kept | Done / not done / next, with hashes; then you decide whether to change the approach, fix it by hand or close the Work |
|
|
34
|
+
| **4. Acceptance · merge** | Accept the result | "Run acceptance" "Check the RUN-X candidate" | The testing perspective verifies the exact candidate independently; the report lands in the Work directory | Pass / fail + evidence; the writer's own word is not evidence |
|
|
35
|
+
| | Merge | "Go ahead and merge" "You may push" | A human action carried out by the session (within the red lines in `AGENTS.md`), then read back from the remote | The hash after the merge; `overview` shows MERGED |
|
|
36
|
+
| **5. Release** | Release | "Release it" "Get ready to release" | The `release-readback` preset + `riskPreset: release`: read back first → stop | "Readback is all green; now it is your turn to do 〔the action〕; tell me when it is done" |
|
|
37
|
+
| | I have done it | "Done" "Half done, check it for me" | Approves apply-readback → readback + observation → stops at closing the window | What is still missing, item by item; any failed step stops it |
|
|
38
|
+
| | Decide the release card | "Approve the release" "Don't release yet" | Carries out / does not carry out the decision card | Production actions are always yours; it only reads back and records |
|
|
39
|
+
| | Production alert | "Production is alerting" "Run a health check" | `observe run` → drafts queued | A one-line draft + "fix_now / schedule / dismiss, your call" |
|
|
40
|
+
| **6. Wrap up · next phase · retrospective** | Close something | "This Work is done" "Close this Work" | Records the decision, confirms the run-record is in the Git plane | `overview` stops listing it as open |
|
|
41
|
+
| | Clean up | "Tidy up" | `gc` (the plan first; `--apply` once you nod) | How many worktrees were cleaned; any candidate reachable only from its branch is always kept, with the reason |
|
|
42
|
+
| | Next phase | "Start the next phase" "Move on to the next phase" | The phase-change compaction ritual: archive, truncate, reset pointers, ask what to feed back | One screen of checklist; the new phase starts from a clean entry point |
|
|
43
|
+
| | Retrospective | "Review what BuildBeat did for us" "What went well and what didn't?" | `metrics` + Run ledgers + session records | Positives / negatives / changes, with numbers |
|
|
44
|
+
| | Keep the lesson | "Write down what we learned from this blocker" | Writes `pm/<date>-<topic>.md` + `env-facts.md`, and turns what can be checked by machine into `requires:` probes | The next window refers to it directly instead of passing it on by word of mouth |
|
|
45
|
+
| | Feed back upstream | "What could BuildBeat itself learn from this?" | Compares against lessons to find new pitfalls | A candidate list, each item A/B/C |
|
|
46
|
+
| | Upgrade | "Update the BuildBeat version" | Updates `BUILDBEAT.md` / `AGENTS.md` / `.buildbeat/*.yaml`, runs `doctor` | The version marker + the doctor report |
|
|
47
|
+
|
|
48
|
+
## Phrases that work at any stage
|
|
49
|
+
|
|
50
|
+
| You say | It will |
|
|
51
|
+
|---|---|
|
|
52
|
+
| "Why?" "What happens if we don't?" "Where did this name come from?" | Explain in plain words, and turn what is yours to decide (names, durations, scope) into decision items for you to approve instead of deciding for you |
|
|
53
|
+
| "Plain words, please" "No numbered lists" | One line each for done / not done / next; separate items only when they really differ |
|
|
54
|
+
| "What do you think?" | Give one recommendation and the reason, not a pile of options |
|
|
55
|
+
| "Authorised" or "Approved" on its own | Applies only to the one thing it explicitly proposed just before, never to other actions |
|
|
56
|
+
|
|
57
|
+
## Three baselines (so you don't have to ask)
|
|
58
|
+
|
|
59
|
+
- **Approval ≠ execution**: merge, push, deploy, spending and deletion each need you to say so, one by one.
|
|
60
|
+
- **Numbers must be concrete**: asking "is this normal?" always gets elapsed time / historical median / time of last output; with no data it says there is no data.
|
|
61
|
+
- **Names go through you first**: domains, service names, environment names, auto-stop durations — anything you will say out loud later — come as recommended values on a decision card for you to approve; the session does not decide them itself.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# 怎么和装了 BuildBeat 的会话说话(按项目阶段)
|
|
2
2
|
|
|
3
|
+
**简体中文** | [English](00-how-to-talk.en.md)
|
|
4
|
+
|
|
3
5
|
> 这是给**用户**看的一页。你在任意一个 AI 编程会话里工作(哪家工具都可以,一个会话就够),会话装载了 BuildBeat Skill;你说人话,它去调 `buildbeat`、读输出、按「已做 → 未做 → 下一步」回你。**你不需要记任何命令**。下面的例句就是平时的说法,照抄即可;同一格里的几句话意思相同,挑顺口的。
|
|
4
6
|
> 会话侧的对应规则在 `SKILL.md` §0.5;项目专属的路由与红线在各项目根的 `AGENTS.md`。
|
|
5
7
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Workflow authoring guide
|
|
2
|
+
|
|
3
|
+
[简体中文](02-workflow-guide.md) | **English**
|
|
4
|
+
|
|
5
|
+
Authority: [`RFC-0003 §2`](../RFC-0003-workflow-policy.md) (Chinese); implementation: `src/v2/engine/workflow.js`. The official preset [`software-delivery.yaml`](../../../src/v2/presets/software-delivery.yaml) is the best model.
|
|
6
|
+
|
|
7
|
+
## Shape
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
kind: workflow
|
|
11
|
+
version: 1
|
|
12
|
+
name: software-delivery
|
|
13
|
+
entry: intent
|
|
14
|
+
steps:
|
|
15
|
+
- id: build
|
|
16
|
+
worker: builder
|
|
17
|
+
- id: review
|
|
18
|
+
worker: reviewer
|
|
19
|
+
readonly: true
|
|
20
|
+
- id: wait-merge
|
|
21
|
+
transitions:
|
|
22
|
+
- from: verify
|
|
23
|
+
on: failed
|
|
24
|
+
to: fix
|
|
25
|
+
terminal:
|
|
26
|
+
- wait-merge
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Rules (checked fail-closed at load time)
|
|
30
|
+
|
|
31
|
+
1. **Step order is the default edge**: the order in which `steps` are written defines the happy path — each step's `succeeded` goes to the next step by default; a step that should not be on the default chain (such as `fix`) goes at the end and is reached only through explicit transitions.
|
|
32
|
+
2. **Explicit transitions**: `on` in `transitions` takes the worker's result (`succeeded` / `failed` / `findings-blocking`); an explicit edge takes precedence over the default one.
|
|
33
|
+
3. **`readonly: true`**: any write to the worktree by that step's worker makes the step count as failed and is recorded — "the reviewer does not change code" is invariant 9, enforced by the Runner's before/after snapshot comparison, not by trusting the prompt.
|
|
34
|
+
4. **`optional` / `requiredWhen`**: optional steps are skipped by default; `requiredWhen: ui-delivery` makes the step mandatory for a UI delivery (together with the [ui-render-gate](03-policy-guide.en.md) and invariant 22).
|
|
35
|
+
5. **`terminal`**: the listed steps are exits. The loader checks for **cycles without an exit** — a cycle such as verify ⇄ fix must have a path to a terminal step, or the workflow is refused.
|
|
36
|
+
6. **Steps without a worker** (such as `wait-merge`) are pure wait / decision points; the Runner raises `HUMAN_REQUESTED` there or stops according to `stopAt`.
|
|
37
|
+
|
|
38
|
+
## How it relates to the run config
|
|
39
|
+
|
|
40
|
+
`entry` in the run config can override the workflow's `entry` (for example to start at `build` and skip the intent/plan steps — the digests are still bound into the approval subject); `stopAt` names stop points. The whole workflow file is hashed with sha256 into `RUN_CREATED.workflowDigest`, so it can be proved afterwards which workflow a run used.
|
|
41
|
+
|
|
42
|
+
**What a mistake looks like**: `start` / `resume` / `doctor` / `preflight` / `approve --config` validate the whole run config before doing anything, and **list every problem at once**:
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
error: run config delivery/work/WORK-X/run-config.yaml has 3 problem(s):
|
|
46
|
+
- stopat: unknown key (did you mean stopAt?)
|
|
47
|
+
- repo: required and missing
|
|
48
|
+
- workers.reviwer: no step of the workflow uses this worker (did you mean reviewer?); workers in this workflow: planner, builder, verifier, reviewer, fixer
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
What is checked: required keys (`repo` `work` `run` `workflow` `workers`); unknown top-level keys and unknown worker / envelope fields (with the closest spelling); types and values (positive integers, lists, `inheritEnv` only `true` / `false`); every worker name must be used by a workflow step; `stopAt` / `entry` must be workflow steps; `work` / `run` may only contain letters, digits, `.` `_` `-`, and a numeric value must be quoted.
|
|
52
|
+
|
|
53
|
+
**Parallel runs (`parallel: true`, off by default)**: by default a repository drives one Run at a time, and another Work's `start` is refused with a note on whom it is queued behind (real incident: a session waited 3 hours 23 minutes behind another Work's Run).
|
|
54
|
+
A Work whose run config sets `parallel: true` can drive at the same time as other Works that set it too; Runs of the same Work are always mutually exclusive; a Run without the switch keeps the whole repository to itself as before — while it runs, parallel Runs cannot start, and while a parallel Run runs, it cannot start.
|
|
55
|
+
Before turning it on, make sure verify does not take fixed ports or share one database or other external state, or the parallel runs will collide. `doctor` prints the current mode; parallel markers left by a killed process are reclaimed by owner, like locks.
|
|
56
|
+
|
|
57
|
+
The run config can also declare (beta.3, all from real incidents in a thirty-round deployment campaign):
|
|
58
|
+
|
|
59
|
+
- **`requires:` environment contract** — the binaries and minimum versions the envelope implicitly depends on, checked fail-closed in full before the Run starts, with every problem reported at once (real incidents: `rg` present only on one session's vendored PATH, `/bin/bash` 3.2, a new shell resolving to Node 14 — each burned a whole Run before the real cause surfaced):
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
requires:
|
|
63
|
+
- command: bash
|
|
64
|
+
min: 4
|
|
65
|
+
- command: rg
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **`reviewTriage: required` finding triage gate** — blocking review findings stop for a human to triage before a fixer is dispatched (see the [Approval guide](07-approval-guide.en.md)).
|
|
69
|
+
|
|
70
|
+
## Review round budget
|
|
71
|
+
|
|
72
|
+
The official preset carries `budgets.maxAttempts.review: 2` (the campaign charter's "at most 2 review rounds per Run", made native): a third review round stops at `WAITING_HUMAN` before it starts, with the reason stating that the budget is exhausted. The mechanism is simply the per-step `maxAttempts`; no new concept.
|
|
73
|
+
|
|
74
|
+
For the `resume-<step>` a Run stops at once the budget is exhausted, **a human approval grants one more attempt**: the kernel records a `BUDGET_EXTENDED` (a ledger fact, replayable), raises that step's limit by one and runs it; a rejection ends the Run. Before this, approving only made the same request come straight back (two pilot app-login Runs ended CANCELLED because of it, although their candidates were already in production).
|
|
75
|
+
|
|
76
|
+
The run config can override the preset (run config > preset > global `maxAttemptsPerStep`):
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
budgets:
|
|
80
|
+
maxAttempts:
|
|
81
|
+
review: 3
|
|
82
|
+
verify: 6
|
|
83
|
+
reviewRoundsPerWork: 6 # review rounds counted across every Run of this Work (superseded ones included); see the overview guide
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`doctor` prints each step's effective limit and where it comes from (run config / workflow preset / default).
|
|
87
|
+
|
|
88
|
+
**Review rounds counted per Work (iteration 09)**: a per-Run budget cannot stop "a new Run for every round" — one pilot Work ran 21 Runs and 9 review rounds without the 2-round cap ever triggering. `budgets.reviewRoundsPerWork: N` makes the kernel count, before a review step starts, the review rounds of **every** Run of this Work (superseded ones and ones already compacted into a run-record included); at N it stops at `WAITING_HUMAN` (kind `work-review-cap`, transition `enter-review`): approving grants one more review round (ledger `BUDGET_EXTENDED scope=work`), rejecting means merging or closing on the evidence at hand. Each Work in `overview` gets a line `cost: review rounds · findings · human waits · worker time`, and the run-record carries a `cost` block — read that line before deciding "continue or cut"; the stop-loss line in the intent (at most so many Runs / review rounds / hours) is checked against it.
|
|
89
|
+
|
|
90
|
+
## Worktrees live inside the repository: keep `.buildbeat/` out of test collection
|
|
91
|
+
|
|
92
|
+
> Since 2.0.0-beta.5 (iteration 09).
|
|
93
|
+
A Run's isolated worktree is `<repo>/.buildbeat/worktrees/<RUN>/`, and the runtime ledgers are in `<repo>/.buildbeat/runtime/`. Neither enters git (the template `.gitignore` excludes them; tools that honour `.gitignore`, such as `rg` and `gitleaks`, no longer walk into them), but **test frameworks collect tests from the file system**: after a pilot merge, the mainline vitest also ran the old candidates' tests in leftover worktrees, and the noise lasted until `gc`. Add to the project:
|
|
94
|
+
|
|
95
|
+
- vitest: `test.exclude: ['**/node_modules/**', '**/.buildbeat/**']`
|
|
96
|
+
- jest: `testPathIgnorePatterns: ['/node_modules/', '/.buildbeat/']`
|
|
97
|
+
- pytest: `norecursedirs = .buildbeat`
|
|
98
|
+
- Maven / Gradle only collect `src/**` and are unaffected; point Playwright's `testDir` at a specific directory.
|
|
99
|
+
|
|
100
|
+
When `start` is refused with "another run is active", the CLI now prints the Run holding the lock, the step it is at, how long ago its last event was, and a copyable `status` command. By default a repository still drives one Run at a time; since 3.2.0, Works whose tests do not compete for ports or databases can set `parallel: true` in their run config and drive in parallel (see "Parallel runs" above).
|
|
101
|
+
|
|
102
|
+
## Infrastructure failures and candidate defects are counted apart
|
|
103
|
+
|
|
104
|
+
> Since 2.0.0-beta.5 (iteration 09).
|
|
105
|
+
A worker's timeout, crash or non-envelope output, and a worker that deliberately ends with exit code **75** (the convention: verify / a wrapper script that finds the environment unsatisfied — a command not on PATH, a port taken, a backend 404, a sandbox forbidding listening — exits 75), are all classified by the kernel as `infra`: `STEP_FINISHED.data.infra = true`, no failure fingerprint, no fixer, the step's budget is not charged (refunded through `steps[step].infraAttempts`), and the Run stops at `WAITING_HUMAN` (kind `infra`). A human approving `resume-<step>` reruns it; a rejection ends it. Any other non-zero exit is still a candidate failure and takes the `on: failed` edge.
|
|
106
|
+
|
|
107
|
+
A failure with no transition edge (`failed` of build, review and fix in the preset) is no longer terminal either: the Run stops at `resume-<step>` for a human to decide. The only terminal FAILED left is a policy `BLOCK`.
|
|
108
|
+
|
|
109
|
+
## Run config sections added in iteration 08
|
|
110
|
+
|
|
111
|
+
- **`envelope:`** — `prompts:` (a directory, relative to the run config) + `vars:` (`{vars.x}` substitution) + optional `pin: <sha>` (read the prompts from that commit, freezing the envelope). The kernel takes the prompt `<component>-<worker>.md` → `<worker>.md`, writes it to `runs/<RUN>/prompts/<step>-<n>.md`, and hands it to the worker as `BUILDBEAT_PROMPT` (the path) and `input.envelope` (`promptRef / file / digest / vars`); worker args may use `{prompt}` and `{vars.x}`. `RUN_CREATED` records the `envelopeDigest`.
|
|
112
|
+
- **`start --attempt new`** — `run:` names the family (`RUN-X`) and the kernel numbers it `RUN-X-01/02…` (scanning the runtime plane and the Git-plane run-records, so deleting the runtime does not reuse a number); older waiting Runs of the same Work are superseded automatically ([Approval guide](07-approval-guide.en.md)).
|
|
113
|
+
- **`cache:`** — `verify: tree`: a verify with the same `HEAD^{tree}` + the same worker command + the same envelope digest that **already passed** reuses its evidence (ledger `reused`, `status` marks `(reused from RUN-X)`); failures and dirty trees are never reused. Do not turn it on for a project whose verifier depends on things outside the tree (remotes, time).
|
|
114
|
+
- **Incremental review** — the input of a readonly step carries `lastReviewed {candidate, run, evidenceRef, range}` (the candidate of this Work's latest review, when it is an ancestor of the current candidate); the reviewer prompt may ask to review only the diff within `range`, with anchored verdicts as before (`anchor`).
|
|
115
|
+
- **`redact:`** — a list of regular expressions; evidence logs are replaced with `<REDACTED>` before they are written; the digest binds the redacted text. Live streams (`.live`) are not redacted and are deleted when the step ends.
|
|
116
|
+
- **`probe:` entries in `requires:`** — `probe: <shell command>` + optional `expect: <regex>` + `name:`; a non-zero exit or a non-matching output fails closed, reported together with the binary version items. Turn environment facts you ran into (Redis ≥ 7, the target machine's Python version, a reachable port) into probes so the next window does not rediscover them; narrative facts go in `delivery/work/<ID>/env-facts.md`.
|
|
117
|
+
- **Step `grade:`** — a workflow step can declare the grade of its command evidence (L0–L4, default L2).
|
|
118
|
+
|
|
119
|
+
## The release readback lane: `release-readback` + `riskPreset: release`
|
|
120
|
+
|
|
121
|
+
The kernel has no deployment capability (invariant 20); production actions are always a human's. This lane only records the **readbacks** before and after the action as L4 ledger entries: `preflight` (read-only checks before the action) → stop at `enter-apply-readback` (the human performs the action) → `apply-readback` (proves the action took effect) → `observe` (proves health) → `wait-close` (the human closes the window). All three readback steps are `readonly`, `grade: L4`, `maxAttempts 1`: any failing step stops for a human; there is no fix edge. The risk preset `release` provides `stopAt: apply-readback` and the window-closing evidence gate (L4 command evidence). The worker is any readback script (curl a health endpoint, read a version, compare a config fingerprint), and its exit code is the verdict. The forty hand-made readback commits of a pilot project's release day are exactly what this lane is for.
|
|
122
|
+
|
|
123
|
+
## Change discipline
|
|
124
|
+
|
|
125
|
+
The preset is part of the product: before changing `software-delivery.yaml`, first ask whether this is a project difference — a project difference uses its own workflow file (point the run config's `workflow:` at it) and leaves the official preset alone. The schema is additive-only; a breaking change bumps `version`.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Workflow 编写指南
|
|
2
2
|
|
|
3
|
+
**简体中文** | [English](02-workflow-guide.en.md)
|
|
4
|
+
|
|
3
5
|
权威:[`RFC-0003 §2`](../RFC-0003-workflow-policy.md);实现:`src/v2/engine/workflow.js`。官方预设 [`software-delivery.yaml`](../../../src/v2/presets/software-delivery.yaml) 是最好的范本。
|
|
4
6
|
|
|
5
7
|
## 形状
|
|
@@ -95,7 +97,7 @@ Run 的隔离工作树在 `<repo>/.buildbeat/worktrees/<RUN>/`,运行时台账
|
|
|
95
97
|
- pytest:`norecursedirs = .buildbeat`
|
|
96
98
|
- Maven / Gradle 只收集 `src/**`,不受影响;Playwright 的 `testDir` 指到具体目录即可。
|
|
97
99
|
|
|
98
|
-
`start` 被「another run is active」挡住时,CLI 现在打印持锁的 Run、它在哪一步、最后一次事件多久前,以及可复制的 `status`
|
|
100
|
+
`start` 被「another run is active」挡住时,CLI 现在打印持锁的 Run、它在哪一步、最后一次事件多久前,以及可复制的 `status` 命令。默认仍是一个仓库同时驱动一个 Run;自 3.2.0 起,测试互不抢端口/数据库的 Work 可在 run 配置里打开 `parallel: true` 并行驱动(见上文「并行 Run」)。
|
|
99
101
|
|
|
100
102
|
## 基础设施故障与候选缺陷分开算
|
|
101
103
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Policy guide
|
|
2
|
+
|
|
3
|
+
[简体中文](03-policy-guide.md) | **English**
|
|
4
|
+
|
|
5
|
+
Authority: [`RFC-0003 §4`](../RFC-0003-workflow-policy.md) (Chinese); implementation: `src/v2/policy/policy.js`. Models: the policies embedded in the risk presets (`src/v2/presets/risk/*.yaml`) and [`ui-render-gate.yaml`](../../../src/v2/presets/policies/ui-render-gate.yaml).
|
|
6
|
+
|
|
7
|
+
## The shape of a policy
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
kind: policy
|
|
11
|
+
version: 1
|
|
12
|
+
name: merge-evidence-floor
|
|
13
|
+
type: transition # pre | post | transition | action
|
|
14
|
+
appliesTo: enter-wait-merge # pre/post: a step id; transition: enter-<step>
|
|
15
|
+
enforcement: LOCAL_ENFORCED # ADVISORY | LOCAL_ENFORCED | SERVER_ENFORCED
|
|
16
|
+
rule:
|
|
17
|
+
all:
|
|
18
|
+
- evidence.exists:
|
|
19
|
+
kind: command
|
|
20
|
+
minGrade: L2
|
|
21
|
+
- finding.maxSeverity:
|
|
22
|
+
atMost: P2
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 8 operators and three-valued logic
|
|
26
|
+
|
|
27
|
+
| Operator | Meaning |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `all` / `any` / `not` | Combinators |
|
|
30
|
+
| `evidence.exists: {kind, minGrade}` | Evidence of the given kind exists at or above the grade |
|
|
31
|
+
| `artifact.accepted: {artifact}` | The artifact (plan/intent/spec) has been accepted, bound to its digest |
|
|
32
|
+
| `attempts.lt: {step, max}` | A step's attempts are below the limit |
|
|
33
|
+
| `budget.remaining: {kind}` | The budget still has room |
|
|
34
|
+
| `candidate.clean` | A pinned, clean candidate exists |
|
|
35
|
+
| `human.approved: {transition}` | A matching approval exists and is not stale |
|
|
36
|
+
| `finding.maxSeverity: {atMost}` | Unresolved findings are at most this severe |
|
|
37
|
+
|
|
38
|
+
Evaluation is **three-valued**: `PASS` / `FAIL` / `UNVERIFIED`. Missing data (no evidence, no candidate) is always `UNVERIFIED`, never a pass — `UNVERIFIED` is never treated as `PASS` at any gate (the six GateResult values are in RFC-0003 §3.3).
|
|
39
|
+
|
|
40
|
+
**Candidate scope**: when the subject awaiting approval carries a candidate, `evidence.exists` and `finding.maxSeverity` count only that candidate's evidence — old review findings superseded by a newer fix round do not block a candidate that has been fixed (the permanent regressions for the real incident of 2026-08-28 live in `tests/` and `evals/`).
|
|
41
|
+
|
|
42
|
+
## Four hook points
|
|
43
|
+
|
|
44
|
+
- `pre`: before a step starts (for example `plan-accepted` in front of build);
|
|
45
|
+
- `post`: after a step ends;
|
|
46
|
+
- `transition`: at the moment of a state transition (for example a re-check at the instant the merge decision is stamped — the approve command re-reads the live state before stamping, and refuses to stamp if the gate fails);
|
|
47
|
+
- `action`: before a protected action (together with [Security boundaries](09-security-boundaries.en.md)).
|
|
48
|
+
|
|
49
|
+
## Enforcement levels
|
|
50
|
+
|
|
51
|
+
`ADVISORY` only informs the worker; `LOCAL_ENFORCED` is carried out physically by the Runner/Workspace (use it for everything that can be guaranteed locally); `SERVER_ENFORCED` declares that the gate lives on the server (branch protection / CI / deployment platform) — the Runner records it but cannot guarantee it on the server's behalf. Label honestly: do not mark as LOCAL what cannot be stopped locally.
|
|
52
|
+
|
|
53
|
+
## Wiring it in
|
|
54
|
+
|
|
55
|
+
The run config's `policies:` list references file paths; the risk presets (`fast` / `standard` / `controlled` / `release`) bring their own policies and stop points, turned on with a single `riskPreset:` line, and project policies are layered on top.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Policy 指南
|
|
2
2
|
|
|
3
|
+
**简体中文** | [English](03-policy-guide.en.md)
|
|
4
|
+
|
|
3
5
|
权威:[`RFC-0003 §4`](../RFC-0003-workflow-policy.md);实现:`src/v2/policy/policy.js`。范本:risk 预设内嵌策略(`src/v2/presets/risk/*.yaml`)与 [`ui-render-gate.yaml`](../../../src/v2/presets/policies/ui-render-gate.yaml)。
|
|
4
6
|
|
|
5
7
|
## 一条 Policy 的形状
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Adapter guide
|
|
2
|
+
|
|
3
|
+
[简体中文](04-adapter-guide.md) | **English**
|
|
4
|
+
|
|
5
|
+
Authority: [`RFC-0002 §Adapter`](../RFC-0002-domain-model.md) (Chinese); implementation: `src/v2/adapters/shell.js` (production), `src/v2/adapters/mock.js` (tests). Ruling #5: vendor-neutral — not tied to any agent vendor.
|
|
6
|
+
|
|
7
|
+
## Shell adapter: every CLI is a worker
|
|
8
|
+
|
|
9
|
+
`workers.<role>` in the run config is exactly one shell adapter configuration:
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
workers:
|
|
13
|
+
builder:
|
|
14
|
+
command: codex
|
|
15
|
+
args:
|
|
16
|
+
- exec
|
|
17
|
+
- -s
|
|
18
|
+
- workspace-write
|
|
19
|
+
- <a prompt or script arguments>
|
|
20
|
+
timeoutMs: 900000
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- The working directory is the step's isolated worktree (not the main checkout);
|
|
24
|
+
- `args` support templates: `{workspace}` `{step}` `{worker}`;
|
|
25
|
+
- Proven workers: `codex exec` (four real M4 pilots) and any bash script; `claude -p` has the same shape and can be swapped in.
|
|
26
|
+
|
|
27
|
+
## Env allowlist (the default; part of removing capabilities)
|
|
28
|
+
|
|
29
|
+
A worker subprocess receives **only** `PATH HOME LANG LC_ALL TMPDIR TERM USER SHELL` by default — cloud credentials and token **environment variables** in the host shell cannot reach the worker (note that `HOME` is on the allowlist: credential **files** are outside this boundary; see [Security boundaries](09-security-boundaries.en.md)). `inheritEnv: true` opens it explicitly (doctor labels that isolation as ADVISORY only); single variables can be injected allowlist-style with `env:`:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
workers:
|
|
33
|
+
verifier:
|
|
34
|
+
command: bash
|
|
35
|
+
env:
|
|
36
|
+
DATABASE_URL: postgres://localhost/app_test
|
|
37
|
+
CI: "1"
|
|
38
|
+
args:
|
|
39
|
+
- -lc
|
|
40
|
+
- npm test
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Values in `env:` must be scalars (turned into strings before they reach the subprocess) and variable names must be valid (letters, digits, underscore); otherwise `doctor` / `start` report an error while loading the config. **The CLI loading path of 2.0.0 and earlier dropped these two fields** (doctor reported the posture, but start ran with the default allowlist, and `env:` variables never reached the worker); since 2.0.1 they are passed through, with a CLI end-to-end regression (`tests/v2-run-cli.test.js`). API users who call `createShellAdapter` directly were not affected.
|
|
44
|
+
|
|
45
|
+
## Input and output
|
|
46
|
+
|
|
47
|
+
- Input: the `BUILDBEAT_INPUT` environment variable carries JSON (step / worker / candidate / failure summary and so on, per role; see the [Worker contract](05-worker-contract.en.md));
|
|
48
|
+
- Output: a step that needs a structured result (the reviewer and others) writes a JSON envelope to the path in `BUILDBEAT_OUTPUT`; codex can also write its last message with `-o` and have a wrapper script convert it;
|
|
49
|
+
- A pure command step (the verifier running tests) needs no envelope — the Runner reads back the exit code and logs as evidence.
|
|
50
|
+
|
|
51
|
+
## Result semantics
|
|
52
|
+
|
|
53
|
+
An adapter only reports facts: exitCode / signal / timedOut / spawnError / stdout / stderr / start and end time. The orchestrator writes the events; an adapter never touches kernel state. A timeout, a crash and a failure to start are recorded as `timeout` / `crashed` / the failure path respectively, each with an end-to-end test (`tests/v2-invariants.test.js`).
|
|
54
|
+
|
|
55
|
+
## Mock adapter
|
|
56
|
+
|
|
57
|
+
`createMockAdapter(script)`: gives each step a sequence of `"succeed"` / `"fail"` or `{behavior, envelope}`, for tests and evals; the behaviour cards are in [`evals/`](../../../evals/README.md).
|
|
58
|
+
|
|
59
|
+
## When to write a dedicated adapter
|
|
60
|
+
|
|
61
|
+
Only when the shell cannot express it (streaming interaction, a kept session) do you write a dedicated adapter; per the M3 ruling, connect everything through the shell first, and revisit only when a real pilot proves it is not enough.
|
|
62
|
+
|
|
63
|
+
## Live output
|
|
64
|
+
|
|
65
|
+
> Since 2.0.0-beta.4 (iteration 08).
|
|
66
|
+
When the orchestrator passes `liveDir` to the shell adapter, the subprocess's stdout/stderr are written straight to `<liveDir>/<step>-<attempt>.{stdout,stderr}.live` (the fds directly, without buffering in the parent), along with `live.json` (`step / attempt / worker / command / startedAt`). When the step returns, the adapter reads both streams back as `stdout` / `stderr` and deletes the live files — the result shape is unchanged and the evidence collector works as before. A custom adapter that wants `status` to show "last output N minutes ago" just produces files with the same names; without them `status` shows only the elapsed time.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Worker contract
|
|
2
|
+
|
|
3
|
+
[简体中文](05-worker-contract.md) | **English**
|
|
4
|
+
|
|
5
|
+
Authority: [`RFC-0003 §5`](../RFC-0003-workflow-policy.md) (Chinese; report B §8.1). A worker is a replaceable executor; the other half of the contract is always guaranteed physically by the Runner, never by the worker's good behaviour.
|
|
6
|
+
|
|
7
|
+
## General contract
|
|
8
|
+
|
|
9
|
+
- **Input**: the environment variable `BUILDBEAT_INPUT` (JSON): step, worker, run/work id, candidate (once pinned), allowed scope;
|
|
10
|
+
- **Output**: a step that needs a structured verdict writes a JSON envelope to the file named by `BUILDBEAT_OUTPUT`. The smallest valid example (matching the parser in `src/v2/runtime/orchestrator.js`; regression test `tests/v2-review-loop.test.js`):
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"status": "succeeded",
|
|
15
|
+
"findings": [
|
|
16
|
+
{"severity": "P1", "summary": "The date filter misses the end-date boundary, so records from that day are dropped."}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- `status`: `succeeded` | `failed` | `blocked`;
|
|
22
|
+
- `findings[]` (optional; omitted means an empty array): each entry **must** have a `severity` (`P0` | `P1` | `P2` | `P3`) and a string `summary`; other fields are ignored and not recorded. A finding's fingerprint = severity + a normalised hash of `summary`, so `summary` should be stable and repeatable, without timestamps or random ids;
|
|
23
|
+
- **Blocking semantics**: `P0` / `P1` block — `findings-blocking` routes to fix (with `reviewTriage: required` it first stops for a human to triage); `P2` / `P3` are only recorded as evidence, do not block and dispatch no fixer; a fingerprint that has been dismissed no longer blocks ([Approval guide](07-approval-guide.en.md));
|
|
24
|
+
- **A format error is not a candidate defect**: one extra layer of markdown fences around the envelope (```json … ```) is tolerated; any other format (not JSON, not an object, a finding without `summary`, a severity outside P0–P3) = `invalid-output`, which since 2.0.0-beta.5 the kernel classifies as a worker infrastructure failure (kind `infra`): no fixer, no failure fingerprint, no budget charged, stop at `WAITING_HUMAN`; once the worker or environment is fixed, `approve --transition resume-<step>` continues;
|
|
25
|
+
- **What a worker says is not evidence**: the Runner trusts only facts it reads back itself (exit code, logs, git state); see the [Evidence guide](06-evidence-guide.en.md).
|
|
26
|
+
|
|
27
|
+
## Discipline per role
|
|
28
|
+
|
|
29
|
+
| Role | Write access | Key points of the contract |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| planner | The work directory | Produces intent/plan drafts; accepting them is a human, digest-bound act |
|
|
32
|
+
| builder | The isolated worktree (within `allowedPaths`) | Changes must land as git commits; an out-of-scope write = the Run BLOCKs and no candidate is pinned |
|
|
33
|
+
| verifier | Runs commands only | Runs the real tests; the exit code is the verdict; writes no envelope |
|
|
34
|
+
| fixer | Same as builder | The input is the last review's `findings[]` (with fingerprints and adjudication status; fix only accepted / open); when it is entered from a failed verify, the input carries **no** failure summary — the failed command / exit code / stdout / stderr are in the main checkout at `.buildbeat/runtime/runs/<RUN>/logs/verify-<attempt>.log`, and the prompt must say to read it ([template](../../../templates/v2/envelope/prompts/fixer.md)); a generic "check it again" is not accepted |
|
|
35
|
+
| reviewer | **None** (`readonly: true`) | Fresh-context and read-only; produces structured findings; any write to the worktree is caught by the snapshot comparison and recorded as a failure (invariant 9) |
|
|
36
|
+
|
|
37
|
+
## Failure and budget
|
|
38
|
+
|
|
39
|
+
A failed step is retried carrying its **failure fingerprint** (command + exit code + error summary + diff digest); the same fingerprint twice in a row, or going over `maxAttemptsPerStep` / the budget, stops and hands over to a human. The worker does not (and cannot) decide on its own to "try once more".
|
|
40
|
+
|
|
41
|
+
**No fixer configured does not mean automatic fixing**: when a role (commonly `fixer`) is missing from `workers:` in the run config, a Run that reaches that step neither errors nor skips it; it stops at `WAITING_HUMAN` (`enter-fix`, reason `no adapter configured for worker fixer; attended handoff`) and waits for a person. To get "fix automatically after a failing test", configure `fixer` (usually the same command as builder, with a prompt that reads the failure from `BUILDBEAT_INPUT`); see the [Quickstart](01-quickstart.en.md).
|
|
42
|
+
|
|
43
|
+
## Practical tips
|
|
44
|
+
|
|
45
|
+
- A ready-made wrapper script and three prompts are in [`templates/v2/envelope/`](../../../templates/v2/envelope/worker.sh): `worker.sh <role> -- <tool command…>` takes care of "exit 75 if the tool is not on PATH, append `$BUILDBEAT_PROMPT` as the last argument, commit mechanically after writing steps, write stdout to `$BUILDBEAT_OUTPUT` for read-only steps"; to switch tools, change only the command after `--`. The deterministic first-run regression is `tests/v2-templates-firstrun.test.js`, and the wrapper's shell contract is `tests/envelope-worker.test.sh`.
|
|
46
|
+
|
|
47
|
+
- Reference `delivery/work/<id>/plan.md` explicitly in the prompt, so the worker's goal is the very file whose digest was approved;
|
|
48
|
+
- The builder's commit can be done mechanically by the wrapper script (as in the M4 pilots: codex only edits files, and `git commit` happens in the wrapper);
|
|
49
|
+
- The reviewer's prompt asks for "the envelope JSON only", written to `$BUILDBEAT_OUTPUT` with `-o` / a redirect.
|
|
50
|
+
|
|
51
|
+
## Iteration 08: what the input gained
|
|
52
|
+
|
|
53
|
+
- `BUILDBEAT_PROMPT` (an environment variable, a file path) and `input.envelope` (`promptRef / file / digest / vars`): the prompt declared by `envelope:` in the run config has had its variables substituted by the kernel and been written to disk; the worker simply runs `cat "$BUILDBEAT_PROMPT"` instead of doing its own `git show`.
|
|
54
|
+
- `input.lastReviewed` (readonly steps only): `{candidate, run, evidenceRef, range}` — the candidate the last review saw and the `range` to the current candidate; a reviewer may review only the increment, but **settled verdicts must not be reopened** (`anchor` is still there).
|
|
55
|
+
- `input.findings` (writing steps) and `input.anchor` (readonly steps) are unchanged.
|
|
56
|
+
|
|
57
|
+
## Owner-visible names are not the worker's call
|
|
58
|
+
|
|
59
|
+
> Since 2.0.0-beta.4 (iteration 08).
|
|
60
|
+
Builders and planners tend to name things along the way: domains, service names, environment names, auto-stop durations, window durations. **Any name or parameter the owner will later see or say out loud is not an implementation detail but a decision for the gate**: put it in the intent, or collect it on the gate's decision card with a recommended value and the reason, and apply it only after a human approves. Real incident: a service named after an internal term took the owner four rounds of questions before it was renamed to a business name they understood. Say so in the prompt, and have the reviewer's checklist record "introduced an unapproved visible name" as P2.
|