@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.
- package/CHANGELOG.md +26 -0
- package/README.en.md +20 -124
- package/README.md +20 -124
- package/SKILL.md +73 -84
- package/docs/CAPABILITY-MATRIX.md +30 -39
- package/docs/MIGRATION.md +93 -0
- package/docs/README.md +8 -37
- package/docs/RELEASING.md +4 -3
- package/docs/v2/RFC-0001-product-definition.md +3 -0
- package/docs/v2/RFC-0002-domain-model.md +3 -0
- package/docs/v2/RFC-0003-workflow-policy.md +3 -0
- package/docs/v2/SPEC-0001-events-v1.md +3 -0
- package/docs/v2/guide/00-how-to-talk.en.md +12 -56
- package/docs/v2/guide/00-how-to-talk.md +12 -56
- package/docs/v2/guide/01-quickstart.en.md +42 -111
- package/docs/v2/guide/01-quickstart.md +42 -111
- package/docs/v2/guide/02-workflow-guide.en.md +13 -123
- package/docs/v2/guide/02-workflow-guide.md +14 -122
- package/docs/v2/guide/03-policy-guide.en.md +6 -50
- package/docs/v2/guide/03-policy-guide.md +6 -50
- package/docs/v2/guide/04-adapter-guide.en.md +7 -60
- package/docs/v2/guide/04-adapter-guide.md +7 -60
- package/docs/v2/guide/05-worker-contract.en.md +6 -53
- package/docs/v2/guide/05-worker-contract.md +7 -52
- package/docs/v2/guide/06-evidence-guide.en.md +8 -44
- package/docs/v2/guide/06-evidence-guide.md +9 -43
- package/docs/v2/guide/07-approval-guide.en.md +8 -110
- package/docs/v2/guide/07-approval-guide.md +8 -121
- package/docs/v2/guide/09-security-boundaries.en.md +13 -37
- package/docs/v2/guide/09-security-boundaries.md +13 -37
- package/docs/v2/guide/10-recovery.en.md +12 -96
- package/docs/v2/guide/10-recovery.md +12 -96
- package/docs/v2/guide/11-session-handoff.en.md +10 -75
- package/docs/v2/guide/11-session-handoff.md +10 -75
- package/docs/v2/guide/12-without-runtime.en.md +46 -0
- package/docs/v2/guide/12-without-runtime.md +46 -0
- package/docs/v2/guide/README.en.md +16 -36
- package/docs/v2/guide/README.md +16 -36
- package/example/AGENTS.md +15 -69
- package/example/README.md +8 -23
- package/example/delivery/envelope/prompts/builder.md +3 -1
- package/example/delivery/envelope/prompts/fixer.md +3 -1
- package/example/delivery/envelope/prompts/reviewer.md +3 -1
- package/example/delivery/work/WORK-EXPORT-DATE-FILTER/run-config.yaml +1 -1
- package/example//346/214/207/346/214/245/345/217/260.md +8 -40
- package/package.json +2 -1
- package/src/v2/adapters/notification-payload.js +40 -0
- package/src/v2/adapters/shell.js +3 -1
- package/src/v2/cli/run-config-check.js +129 -45
- package/src/v2/cli/run.js +853 -254
- package/src/v2/engine/reducer.js +2 -0
- package/src/v2/engine/risk-preset.js +14 -47
- package/src/v2/engine/workflow.js +177 -185
- package/src/v2/evidence/collector.js +12 -1
- package/src/v2/evidence/image.js +346 -0
- package/src/v2/policy/policy.js +219 -286
- package/src/v2/presets/software-delivery.yaml +1 -16
- package/src/v2/runtime/cache.js +22 -1
- package/src/v2/runtime/decisions.js +185 -31
- package/src/v2/runtime/findings.js +4 -1
- package/src/v2/runtime/notify.js +42 -36
- package/src/v2/runtime/orchestrator.js +811 -297
- package/src/v2/runtime/overview-repos.js +79 -0
- package/src/v2/runtime/overview.js +279 -47
- package/src/v2/runtime/release.js +233 -0
- package/src/v2/runtime/run-record.js +1 -0
- package/src/v2/workspace/workspace-manager.js +8 -0
- package/templates/v2/AGENTS.md +20 -73
- package/templates/v2/BUILDBEAT.md +2 -13
- package/templates/v2/envelope/prompts/builder.md +3 -1
- package/templates/v2/envelope/prompts/fixer.md +3 -1
- package/templates/v2/envelope/prompts/reviewer.md +3 -1
- package/templates/v2/run-config.example.yaml +9 -7
- package/templates/v2/work.example.md +16 -0
- package/templates/v2//346/214/207/346/214/245/345/217/260.md +8 -40
- package/docs/v2/skill/01-principles.md +0 -26
- package/docs/v2/skill/02-project-layout.md +0 -31
- package/docs/v2/skill/03-collaboration-rules.md +0 -45
- package/docs/v2/skill/04-rhythm-and-rituals.md +0 -104
- package/docs/v2/skill/05-red-lines.md +0 -13
- package/docs/v2/skill/06-bootstrap-and-takeover.md +0 -84
- package/docs/v2/skill/07-templates-and-lessons.md +0 -24
- package/example/.buildbeat/observe.yaml +0 -31
- package/src/v2/observe/observe-config.js +0 -194
- package/src/v2/observe/observe-reducer.js +0 -117
- package/src/v2/observe/observe.js +0 -427
- package/src/v2/presets/observe.yaml +0 -45
- package/src/v2/presets/policies/ui-render-gate.yaml +0 -13
- package/src/v2/presets/release-readback.yaml +0 -36
- package/src/v2/presets/risk/controlled.yaml +0 -39
- package/src/v2/presets/risk/fast.yaml +0 -19
- package/src/v2/presets/risk/release.yaml +0 -21
- package/src/v2/presets/risk/standard.yaml +0 -28
- package/templates/ARCHITECTURE.md +0 -50
- package/templates/contracts/PROTOCOL.md +0 -28
- package/templates/pm/adr/ADR-0000-template.md +0 -25
- package/templates/pm/adr/README.md +0 -15
- package/templates/pm/decisions.md +0 -11
- package/templates/standards/CODE.md +0 -23
- package/templates/standards/DESIGN.md +0 -36
- package/templates/standards/REVIEW.md +0 -20
- package/templates/standards/STACK.md +0 -31
|
@@ -1,60 +1,13 @@
|
|
|
1
1
|
# Worker contract
|
|
2
2
|
|
|
3
|
-
[简体中文](05-worker-contract.md)
|
|
3
|
+
[简体中文](05-worker-contract.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Builders and fixers commit changes inside the allowed scope. Verifiers run real acceptance commands. Reviewers independently inspect the pinned candidate without changing the worktree.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Structured output: {"status":"succeeded","findings":[{"severity":"P2","summary":"specific issue"}]}. severity is P0–P3 and summary is a string; P0/P1 routes to repair. A changed worktree after a read-only step blocks its result.
|
|
8
8
|
|
|
9
|
-
|
|
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`):
|
|
9
|
+
Review input includes adjudication anchors and an available lastReviewed incremental range. Fix input includes current findings and adjudications. Prompts may use these facts but cannot approve decisions.
|
|
11
10
|
|
|
12
|
-
|
|
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
|
-
```
|
|
11
|
+
Use exit 75 for missing tools, unavailable backends or an unusable execution environment. Do not change business code to hide an environment failure. Do not print credentials. The runner reads candidate, exit status and evidence itself.
|
|
20
12
|
|
|
21
|
-
|
|
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.
|
|
13
|
+
`BUILDBEAT_INPUT.workArtifact` carries the selected repository-relative `ref` and accepted `digest`. Read that artifact; use work.md first and legacy intent/plan only when work.md is absent.
|
|
@@ -1,60 +1,15 @@
|
|
|
1
1
|
# Worker 合同
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](05-worker-contract.en.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
builder/fixer 只修改允许的范围并提交候选;verifier 运行真实验收命令;reviewer 独立、只读,检查固定候选。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
结构化输出:`{"status":"succeeded","findings":[{"severity": "P1", "summary": "具体问题"}]}`。severity 为 P0–P3,summary 为字符串;`P0` / `P1` 阻断并触发修复。只读步骤前后工作树变化会阻断结果。
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- **输出**:需要结构化结论的步把 JSON 信封写到 `BUILDBEAT_OUTPUT` 指向的文件。最小合法示例(与 `src/v2/runtime/orchestrator.js` 的解析器一致,回归测试 `tests/v2-review-loop.test.js`):
|
|
9
|
+
reviewer 输入包含历史裁决 anchor 和可用的 lastReviewed 增量范围;fixer 输入包含本轮问题及裁决状态。提示词可以使用它们,不能自行批准。
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
{
|
|
14
|
-
"status": "succeeded",
|
|
15
|
-
"findings": [
|
|
16
|
-
{"severity": "P1", "summary": "日期筛选未覆盖结束日期边界,导致当天记录被遗漏。"}
|
|
17
|
-
]
|
|
18
|
-
}
|
|
19
|
-
```
|
|
11
|
+
环境缺工具、后端不可用、沙箱禁止必要操作等用 exit 75;不要通过修改业务代码掩盖环境问题。日志不得主动输出凭据。实际候选、退出码和证据都由内核回读,自述不能替代事实。
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
- `findings[]`(可省,省略等于空数组):每条**必须**有 `severity`(`P0` | `P1` | `P2` | `P3`)和字符串 `summary`;其他字段被忽略、不落账。finding 指纹 = 严重度 + `summary` 规范化 hash,所以 `summary` 要稳定、可复述,不要带时间戳或随机 id;
|
|
23
|
-
- **阻断语义**:`P0` / `P1` 阻断——`findings-blocking` 路由进 fix(`reviewTriage: required` 时先停人分诊);`P2` / `P3` 只落 Evidence,不阻断、不派 fixer;已 `dismiss` 的同指纹不再阻断([Approval 指南](07-approval-guide.md));
|
|
24
|
-
- **格式错误不是候选缺陷**:信封外多裹一层 markdown 代码栏(```json … ```)可容忍;其余任何格式(非 JSON、不是对象、finding 缺 `summary`、severity 不在 P0–P3)= `invalid-output`,2.0.0-beta.5 起内核判为 worker 基础设施故障(kind `infra`):不派 fixer、不记失败指纹、不扣预算,停 `WAITING_HUMAN`;人修好 worker 或环境后 `approve --transition resume-<step>` 续跑;
|
|
25
|
-
- **Worker 说的不算证据**:Runner 只相信自己回读的事实(退出码、日志、git 状态);见 [Evidence 指南](06-evidence-guide.md)。
|
|
13
|
+
无效信封记为 `invalid-output`,属于环境故障,不能据此要求修改业务代码。
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
| 角色 | 写权限 | 合同要点 |
|
|
30
|
-
|---|---|---|
|
|
31
|
-
| planner | 工作项目录 | 产出 intent/plan 草稿;接受与否是人的 digest 绑定动作 |
|
|
32
|
-
| builder | 隔离 worktree(`allowedPaths` 内) | 改动必须落成 git 提交;越界写入 = Run BLOCK,不固定 candidate |
|
|
33
|
-
| verifier | 只跑命令 | 跑真实测试;退出码就是结论,不写信封 |
|
|
34
|
-
| fixer | 同 builder | 输入是上一轮 review 的 `findings[]`(含指纹与裁决状态,只修 accepted / open);verify 失败进来时输入里**没有**失败摘要,失败命令 / 退出码 / stdout / stderr 在主仓 `.buildbeat/runtime/runs/<RUN>/logs/verify-<attempt>.log`,prompt 要写明去读([模板](../../../templates/v2/envelope/prompts/fixer.md));不接受泛化的"再检查一下" |
|
|
35
|
-
| reviewer | **无**(`readonly: true`) | fresh-context 只读;产出结构化 findings;任何工作树写入由快照比对捕获并按失败落账(不变量 9) |
|
|
36
|
-
|
|
37
|
-
## 失败与预算
|
|
38
|
-
|
|
39
|
-
同一步失败会带着**失败指纹**(命令+退出码+错误摘要+diff digest)重试;连续同指纹或超 `maxAttemptsPerStep`/预算即停,转人工。Worker 不需要(也不能)自己决定"再试一次"。
|
|
40
|
-
|
|
41
|
-
**没配 fixer 不等于自动修复**:run 配置 `workers:` 里缺某个角色(常见是 `fixer`),Run 走到该步时不会报错也不会跳过,而是停 `WAITING_HUMAN`(`enter-fix`,理由 `no adapter configured for worker fixer; attended handoff`)等人接手。想要"测试失败后自动修",必须配置 `fixer`(通常与 builder 同一条命令,prompt 从 `BUILDBEAT_INPUT` 读失败摘要),见 [快速开始](01-quickstart.md)。
|
|
42
|
-
|
|
43
|
-
## 实践提示
|
|
44
|
-
|
|
45
|
-
- 现成的包装脚本与三份 prompt 在 [`templates/v2/envelope/`](../../../templates/v2/envelope/worker.sh):`worker.sh <role> -- <工具命令…>` 负责"工具不在 PATH 就 exit 75、把 `$BUILDBEAT_PROMPT` 追加为最后一个参数、写入步机械 commit、只读步把 stdout 落到 `$BUILDBEAT_OUTPUT`",换工具只改 `--` 后面的命令;确定性首跑回归见 `tests/v2-templates-firstrun.test.js`。
|
|
46
|
-
|
|
47
|
-
- prompt 里明确引用 `delivery/work/<id>/plan.md`,让 Worker 的目标与被批准的 digest 是同一份文件;
|
|
48
|
-
- builder 的提交动作可以由包装脚本机械执行(M4 试点即如此:codex 只改文件,`git commit` 在包装层);
|
|
49
|
-
- reviewer 的 prompt 要求"只输出信封 JSON",并用 `-o`/重定向落到 `$BUILDBEAT_OUTPUT`。
|
|
50
|
-
|
|
51
|
-
## 迭代 08:输入里多了什么
|
|
52
|
-
|
|
53
|
-
- `BUILDBEAT_PROMPT`(环境变量,文件路径)与 `input.envelope`(`promptRef / file / digest / vars`):run 配置 `envelope:` 声明的 prompt 已由内核替换变量并落盘,worker 直接 `cat "$BUILDBEAT_PROMPT"`,不再自己 `git show`。
|
|
54
|
-
- `input.lastReviewed`(仅 readonly 步):`{candidate, run, evidenceRef, range}`——上一次 review 看过的候选与到当前候选的 `range`;reviewer 可只审增量,但**已裁决结论不得翻案**(`anchor` 仍在)。
|
|
55
|
-
- `input.findings`(写入步)与 `input.anchor`(readonly 步)不变。
|
|
56
|
-
|
|
57
|
-
## 所有者可见命名不由 worker 决定
|
|
58
|
-
|
|
59
|
-
> 自 2.0.0-beta.4(迭代 08)起。
|
|
60
|
-
builder / planner 在实现中会顺手起名:域名、服务名、环境名、自停时长、窗口时长。**凡所有者以后要看见或念出来的名字与参数,不是实现细节,是门前决策项**:写进 intent,或攒进门前决策卡给推荐值与理由,人批后再落地。真实事故:一个按内部术语起的服务名让所有者连问四轮才改成他听得懂的业务名。prompt 里写明这条,reviewer 清单里把"引入了未经批准的可见命名"记为 P2。
|
|
15
|
+
`BUILDBEAT_INPUT.workArtifact` carries the selected repository-relative `ref` and accepted `digest`. Read that artifact; use work.md first and legacy intent/plan only when work.md is absent.
|
|
@@ -1,51 +1,15 @@
|
|
|
1
|
-
# Evidence
|
|
1
|
+
# Evidence and efficiency readings
|
|
2
2
|
|
|
3
|
-
[简体中文](06-evidence-guide.md)
|
|
3
|
+
[简体中文](06-evidence-guide.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Evidence is read from Git and actual execution: kind, candidate, log digest, outcome, grade, producer and timestamps. Passed, failed and unverified remain distinct. Evidence for an old candidate cannot approve a new one.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Historical L0–L4 grades remain readable; command evidence defaults to L2. A grade label does not prove production acceptance. Production observation is outside this product; release readback after the merge is described in [Work acceptance and decisions](07-approval-guide.en.md).
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
For UI delivery, set `requireScreenshot: true` in the run config. While verify runs, the environment variable `BUILDBEAT_SCREENSHOT_DIR` names a fresh empty directory outside the worktree; verify writes screenshots of the real render there as PNG, and each becomes screenshot evidence bound to the current candidate by its file digest. Only decodable PNG regular files count: the chunk layout (one IHDR first, the palette before the pixel data, consecutive IDAT, IEND last, no unknown critical chunk) with every CRC, a legal size, bit depth and palette, the size, values and placement of the ancillary chunks that change decoding or display (tRNS, gAMA, cHRM, sRGB, iCCP, sBIT, bKGD, hIST, pHYs), and pixel data that reconstructs row by row (interlaced images included; the declared size is checked before inflating, up to 256 MiB per image) with palette indices inside the palette. Text (compressed text included), time and other ancillary chunks are not checked; some decoders parse them, and such differences between decoders are outside this check. Empty, truncated or symlinked files and other formats such as jpg or webp are rejected with the reason named, even when another screenshot of the same verify passes. A cached verify and an approval re-check the files under the same rules (JPEG and WebP cannot be checked to the point of decoding without a dependency; headless browser screenshots are PNG by default). This proves a decodable image, not the right page, which the reviewer and the person deciding judge. A verify that succeeds without a screenshot counts as failed; the merge check uses the screenshots of the current candidate's latest passed verify and re-checks their file digests; reviewer input, the decision card and notifications list their paths and digests. The switch is frozen with the run, and a cached verify carries its source screenshots.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Raw logs live in .buildbeat/runtime/. Terminal run records, work decisions and adjudications live under delivery/work/. A digest is not a raw-log backup; retain logs according to project needs.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|---|---|
|
|
15
|
-
| `passed` | Zero exit code, no timeout, not killed by a signal |
|
|
16
|
-
| `failed` | Non-zero exit / timeout / signal |
|
|
17
|
-
| `unverified` | **Could not collect**: failed to start, data missing. Never means "no problem" |
|
|
13
|
+
Status includes costs, evidence completeness, approval waits, repair counts and durations. These are local derived readings, not token or monetary bills. Cached verification identifies its source and only reuses passed evidence with matching tree, command and envelope. Disable it for externally changing checks.
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## Grades L0–L4
|
|
22
|
-
|
|
23
|
-
`L0` self-report → `L1` static check → `L2` real local execution (the default grade for command readback) → `L3` post-deployment verification → `L4` production readback. Gates state their requirement with `minGrade` (for example a merge floor of L2; closing a production switch needs L4).
|
|
24
|
-
|
|
25
|
-
**Verification pyramid warning (the most expensive lesson of the thirty-round campaign)**: the marginal value of polishing a lower layer toward theoretical completeness is far below moving one layer closer to the real machine one step earlier. In practice a 7,400-line L3 suite was polished to its limit, while the four real release blockers (systemd parsing behaviour, deployment/service identity split, probe budgets calibrated against a stand-in, TLS ref format) **were all structurally invisible to L3** and were found in one evening at L4. Rule of thumb: **move up a layer as soon as the simulated layer has zero real defect classes left; do not chase theoretical completeness**. Similarly, re-running the full verify when the candidate touched only a part is pure repetition; caching or trimming by content hash belongs to the envelope layer (the campaign measured 25 → 13 minutes) and the kernel does not do it for you: cache correctness depends on assumptions about a stable environment, which the envelope owner carries.
|
|
26
|
-
|
|
27
|
-
## Preflight is not evidence
|
|
28
|
-
|
|
29
|
-
`buildbeat preflight --config <run-config> --step <id>` dry-runs one step's worker command directly in the main checkout: no worktree, no ledger, no evidence of any kind (the output carries a `PREFLIGHT (dry signal, never evidence)` banner and the environment variable `BUILDBEAT_PREFLIGHT=1`). Its purpose is a minute-scale loop that reaches the first failure boundary before entering a Run (in the campaign every harness defect cost a whole Run round; preflight mode dismantled them in one evening). **Anything preflight finds counts only once a Run reproduces it.**
|
|
30
|
-
|
|
31
|
-
## Candidate scope
|
|
32
|
-
|
|
33
|
-
Evidence is bound to the candidate through `subject`: the merge gate counts only the current candidate's evidence; records from old candidates or old review rounds are not mixed in (a real-incident regression, see the `fix-loop` eval).
|
|
34
|
-
|
|
35
|
-
## observe: production joins the evidence plane (v0)
|
|
36
|
-
|
|
37
|
-
Frozen in [`RFC-0003 §8`](../RFC-0003-workflow-policy.md) (Chinese), implemented in M5 (`src/v2/observe/`):
|
|
38
|
-
|
|
39
|
-
- **Providers** (project probes such as drift checks and live status) produce records under the same Evidence Contract into a separate observe ledger (same chain verification, `.buildbeat/runtime/observe/`); a broken probe is `unverified` (default severity warn), never silent;
|
|
40
|
-
- **Three bands** (thresholds configurable, order fixed): `log` records only → `diagnose` triggers a read-only diagnostic command and produces `diagnosis` evidence → `intent` writes an Intent **draft** into the Git plane at `delivery/observe/intents/` (never executed automatically);
|
|
41
|
-
- **Human triage**: `observe triage --action fix_now|schedule|dismiss`. After `fix_now` a human carries it into a software-delivery Run and the loop closes; `dismiss` feeds the bands back so the same fingerprint is not queued again until its severity rises (alert-fatigue protection); the triage outcome is written into the draft file itself and survives deleting the runtime (invariant 23);
|
|
42
|
-
- **Scheduling boundary in v0**: the `schedule` field is parsed and recorded but there is no built-in scheduler; periodic runs are the host's cron calling `observe run` repeatedly.
|
|
43
|
-
|
|
44
|
-
## Completeness
|
|
45
|
-
|
|
46
|
-
`buildbeat metrics` prints evidence completeness (steps with evidence / steps that should have it); the M4/M5 exit line is ≥ 95%, the pilots measured 100%.
|
|
47
|
-
|
|
48
|
-
## Live readings are not evidence
|
|
49
|
-
|
|
50
|
-
> Since 2.0.0-beta.4 (iteration 08).
|
|
51
|
-
While a step runs, the Shell Adapter streams the worker's stdout/stderr to `.buildbeat/runtime/runs/<RUN>/<step>-<n>.{stdout,stderr}.live` and keeps a `live.json` (command, start time). These are **readings**: `status` uses them to answer "is it still moving, for how long, when was the last output", and they are reclaimed as soon as the step ends; the evidence log is still produced by readback, and the digest still binds the final log. Durations likewise: per-step elapsed time and the repository's historical median (`typical step duration` in `metrics`) are derived from ledger timestamps and enter neither the ledger nor the run-record.
|
|
15
|
+
check --step creates no formal run evidence; the run must reproduce it. Live output and elapsed time are not acceptance verdicts. Compare completion duration, human interventions, repeated work and recovery outcomes; scripted regressions do not establish real AI speed.
|
|
@@ -1,51 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 证据与效率读数
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](06-evidence-guide.en.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
证据来自 Git 和真实执行:种类、候选、日志摘要、结果、等级、产生者及起止时间。通过、失败、未验证分开;旧候选的证据不能给新候选放行。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
保留 L0–L4 历史等级,默认命令证据为 L2。等级标签不自动证明生产验收;项目必须提供实际运行环境的证据。生产探针已移出主产品;合并后的上线回读见[工作确认与决策](07-approval-guide.md)。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
UI 交付在 run-config 写 `requireScreenshot: true`。verify 运行时,环境变量 `BUILDBEAT_SCREENSHOT_DIR` 指向工作树外一个新的空目录;verify 把真渲染截图以 PNG 写进去,每张记为绑定当前候选的截图证据(文件摘要)。只接受可解码的 PNG 普通文件:块的排列(唯一的 IHDR 在首、调色板在像素数据前、IDAT 连续、IEND 收尾、无未知关键块)与各块校验和、合法的尺寸位深和调色板、影响解码与显示的附加块(tRNS、gAMA、cHRM、sRGB、iCCP、sBIT、bKGD、hIST、pHYs)的长度、取值与位置、解压后的像素数据逐行可重建(含隔行图,按头部声明的大小先核对再解压,单张上限 256 MiB)且调色板索引不越界;文本(含压缩文本)、时间等其他附加块不做校验,个别解码器会解析它们,这类解码器之间的差异不在本检查范围内;空文件、截断、符号链接与 jpg、webp 等其他格式会被拒并写明原因,即使同一次 verify 另有截图通过也照样记下。复用缓存的 verify 与批准时按同一规则重新核对文件(JPEG/WebP 无法在不引入依赖的前提下校验到可解码,常用无头浏览器截图默认就是 PNG)。这证明图片可解码,不证明画面正确,画面由 reviewer 与决定人判断。verify 成功却没有截图按失败处理;合并检查以当前候选最近一次通过的 verify 留下的截图为准,并重新核对文件摘要;reviewer 输入、待决定卡和通知列出截图路径与摘要。开关随 Run 冻结,复用缓存的 verify 一并沿用来源截图。
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
原始日志在 .buildbeat/runtime/;终态 run-record、工作决定和审查裁决在 delivery/work/。摘要不是原始日志备份,按项目要求保留后者。
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|---|---|
|
|
15
|
-
| `passed` | 命令零退出、未超时、未被信号杀死 |
|
|
16
|
-
| `failed` | 非零退出 / 超时 / 信号 |
|
|
17
|
-
| `unverified` | **采不到**:无法启动、数据缺失。永远不是"没问题" |
|
|
13
|
+
status 展示工作成本、证据完整性、审批等待、修复次数和耗时。它们是本地派生读数,不是 Token/金额账单。验证缓存明确标注来源,只复用成功且匹配的树、命令、信封;依赖外部变化的检查不启用。
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
check --step 不进入正式台账,其结果必须在 Run 中重现。实时输出和耗时也不是验收结论。
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`L0` 自述 → `L1` 静态检查 → `L2` 本地真实执行(命令回读默认档)→ `L3` 部署后验证 → `L4` 生产实测。门用 `minGrade` 提要求(如 merge 底线 L2;生产切换收口要 L4)。
|
|
24
|
-
|
|
25
|
-
**验证金字塔警示(三十轮战役最贵一课)**:低层验证追理论完备的边际价值远低于早一层真机。实战中 7400 行 L3 套件打磨到极限,而 4 个真正的上线阻断(systemd 解析行为、部署/服务身份分裂、探针预算按替身标定、TLS ref 格式)**全部是 L3 结构性测不到的**,一晚 L4 找齐。经验法则:**模拟层"真缺陷类清零"即升层,不追理论完备**。同理,verify 全量重跑在候选只动局部时是纯重复——按内容哈希缓存/裁剪属于信封层优化(战役实测 25→13 分钟),内核不代做:缓存正确性依赖环境稳定假设,由信封所有者自己承担。
|
|
26
|
-
|
|
27
|
-
## 预检 ≠ 证据
|
|
28
|
-
|
|
29
|
-
`buildbeat preflight --config <run-config> --step <id>`:在主 checkout 直接干跑某步的 worker 命令——无 worktree、无台账、不落任何证据(输出自带 `PREFLIGHT (dry signal, never evidence)` 横幅,环境变量 `BUILDBEAT_PREFLIGHT=1`)。用途是分钟级循环打到首个失败边界再进 Run(战役里 harness 缺陷每个要烧一整轮 Run,预检模式一晚拆完);**预检发现的任何东西必须由 Run 复现才算数**。
|
|
30
|
-
|
|
31
|
-
## 候选作用域
|
|
32
|
-
|
|
33
|
-
证据以 `subject` 绑定候选:merge 门只统计当前 candidate 的证据,旧候选/旧 review 轮次的记录不混入(真实事故回归,见 evals `fix-loop`)。
|
|
34
|
-
|
|
35
|
-
## observe:把生产也纳入证据面(v0)
|
|
36
|
-
|
|
37
|
-
[`RFC-0003 §8`](../RFC-0003-workflow-policy.md) 冻结、M5 实现(`src/v2/observe/`):
|
|
38
|
-
|
|
39
|
-
- **Provider**(drift-check / live-status 等项目探针)按同一 Evidence Contract 产出记录,进独立 observe 台账(同一链校验机制,`.buildbeat/runtime/observe/`);探针挂了=`unverified`(severity 默认 warn),绝不静默;
|
|
40
|
-
- **bands 三层**(阈值可配,层级固定):`log` 只落账 → `diagnose` 触发只读诊断命令、产出 `diagnosis` 证据 → `intent` 把 Intent **草稿**写进 Git 面 `delivery/observe/intents/`(绝不自动执行);
|
|
41
|
-
- **人分诊**:`observe triage --action fix_now|schedule|dismiss`。`fix_now` 之后由人把它带进 software-delivery Run,闭环成立;`dismiss` 回调 bands——同指纹在严重度升级前不再入队(防告警疲劳);分诊终态写在草稿文件里,删 runtime 不丢(不变量 23);
|
|
42
|
-
- **调度 v0 边界**:`schedule` 字段解析落账但不内置调度器;周期运行=宿主 cron 反复调 `observe run`。
|
|
43
|
-
|
|
44
|
-
## 完整率
|
|
45
|
-
|
|
46
|
-
`buildbeat metrics` 输出证据完整率(有证据的步/应有证据的步);M4/M5 退出线 ≥95%,试点实测 100%。
|
|
47
|
-
|
|
48
|
-
## 运行中的读数 ≠ 证据
|
|
49
|
-
|
|
50
|
-
> 自 2.0.0-beta.4(迭代 08)起。
|
|
51
|
-
Shell Adapter 在步运行期间把 worker 的 stdout/stderr 实时流到 `.buildbeat/runtime/runs/<RUN>/<step>-<n>.{stdout,stderr}.live`,并留 `live.json`(命令、开始时间)。它们是**读数**:`status` 拿来回答"还在动吗、动了多久、最后一次输出是什么时候",步一结束就收回;证据日志仍由回读生成、digest 仍绑最终日志。耗时同理——每步耗时、同仓历史中位数(`metrics` 的 `typical step duration`)都从台账时间戳推导,不进台账、不进 run-record。
|
|
17
|
+
减法的验证比较完成耗时、人工介入、重复执行与恢复结果;脚本回归不能外推真实 AI 速度。
|
|
@@ -1,117 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Work acceptance and decisions
|
|
2
2
|
|
|
3
|
-
[简体中文](07-approval-guide.md)
|
|
3
|
+
[简体中文](07-approval-guide.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
New work has one work.md. accept binds its current digest but does not start execution; run continues within the user's authorization. Legacy intent/plan/spec acceptance records remain readable.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Status presents the transition, candidate, plan digest, evidence, reasons and next action. decide --action approve|reject must name the run and transition. Approval re-reads the candidate and frozen safeguards; changed subjects, dirty trees, missing evidence or unacceptable reviews cannot be stamped. Continue nonterminal decisions with run. Final approval means merge-ready only.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Use decide --action accept|dismiss --work ... --fingerprint ... for findings. Exact-fingerprint dismissals persist; similarity detects nonconverging reviews and never substitutes for adjudication.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Normal repairs continue automatically. Repeated issues, increased blockers, infrastructure failures and exhausted budgets create exception decisions. One budget grant covers its repair/reverification/review plan and can be replayed. The driving session cannot approve on behalf of the user without authorization.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
buildbeat inbox --repo . # every Run waiting for a human: transition, candidate, digest, reason
|
|
15
|
-
buildbeat status --repo . --run RUN-X # the full derived view of one Run (steps, evidence, findings)
|
|
16
|
-
buildbeat approve --repo . --run RUN-X --transition enter-wait-merge --by <name> --config <run-config>
|
|
17
|
-
buildbeat reject --repo . --run RUN-X --reason "<why>" --by <name>
|
|
18
|
-
buildbeat accept --repo . --work WORK-X --artifact plan --by <name> # artifact acceptance (digest-bound)
|
|
19
|
-
```
|
|
13
|
+
Merging and releasing stay human actions. After a release, run `buildbeat release --config <config> [--note <text>]`: it checks that the Work's succeeded candidate is contained in `--ref` (default: the current HEAD), runs the read-only readback command from the run config's `release:` section in the main checkout (same shape, environment allowlist and redaction as a worker), and appends the outcome, exit code, commit, output digest and a redacted tail to `delivery/work/<ID>/releases.jsonl`. Once the latest readback has passed, close the window with `decide --repo . --work <ID> --action close --result <text>`, which records a close-work decision bound to that readback; a failed readback cannot close it. A Work whose run configs have no `release:` section ends at the merge, and status says nothing is left to do.
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## The safety semantics of approve (all tested)
|
|
24
|
-
|
|
25
|
-
1. **The transition must match** the pending request;
|
|
26
|
-
2. **Reality is re-read before stamping**: if the pending snapshot and reality disagree (the candidate moved, the plan changed), the approval is refused and a refresh is required; nothing is stamped;
|
|
27
|
-
3. **Transition gates are re-checked at the instant of stamping**: if merge-evidence-floor / ui-render-gate do not PASS right then, the approval is refused;
|
|
28
|
-
4. Approving a final decision (a pending request of the final-decision kind) means `RUN_TERMINAL SUCCEEDED` plus compacting the run-record into the Git plane.
|
|
29
|
-
|
|
30
|
-
## Five words and what each means (approving is not executing)
|
|
31
|
-
|
|
32
|
-
Sessions and documents used to mix "accept / approve / resume / succeeded / merged". The vocabulary is fixed as follows:
|
|
33
|
-
|
|
34
|
-
| Word | Command | Meaning | Is not |
|
|
35
|
-
|---|---|---|---|
|
|
36
|
-
| **Accept** | `accept --artifact intent\|plan`, or `--artifact intent,plan` for both at once | A human endorses each artifact's digest, one record per artifact; editing an artifact makes only its acceptance `stale` | Starting work; it creates no Run |
|
|
37
|
-
| **Approve a transition** | `approve --transition <t>` | Lets the Run take **this one** transition: `enter-fix` (release the fixer after triage or after a review that is not converging, kind `review-not-converging`; when the review budget is spent, this one approval also grants the next review round, and so does answering it with a hand fix via `resume --adopt <sha>`), `resume-<step>` (run the step once more after a budget or infra stop; records `BUDGET_EXTENDED`; successful build / verify / fix attempts are not charged, and a budget stop that hits both the run and the work review cap is lifted by one approval), `enter-review` (one more review round after the Work-level cap), `enter-apply-readback` ("I have done it" on the release lane) | Approving any other transition; after a non-terminal transition is approved the Run **does not move by itself**: `resume --config <run-config>` continues it (the `next:` line printed by `approve` says so) |
|
|
38
|
-
| **Merge decision** (final approval) | `approve --transition enter-wait-merge` | The candidate is fit to merge: candidate + planDigest + evidenceDigest all hold at this instant; the Run reaches the terminal state `SUCCEEDED` and the run-record is compacted into the Git plane | Code merged, pushed or deployed: those three remain your actions outside the Runner, always |
|
|
39
|
-
| **Run SUCCEEDED** | — | The Run stopped where it should and the evidence is complete | The Work is finished. `overview` shows `MERGED` only after reading back that the candidate is on the current branch |
|
|
40
|
-
| **Reject** | `reject --reason` | The Run ends (`FAILED`, reason recorded) | The artifacts are invalidated; the acceptance state of intent/plan does not change |
|
|
41
|
-
|
|
42
|
-
Likewise, `fix_now` on an observe draft is only acceptance; a human starts the Run. Protected actions are listed under [Security boundaries](09-security-boundaries.md) (Chinese).
|
|
43
|
-
|
|
44
|
-
After `start --attempt new` numbers a Run, `resume --config <run-config.yaml>` resumes the family’s only non-terminal Run and prints its ID. Use `--run <RUN-ID>` to select the configured Run itself or `<family>-NN` explicitly (at least two digits). An existing ledger for the exact configured ID takes precedence. Multiple non-terminal Runs are listed with a request to select one using `--run`; if none remain, the error reports the latest ID and terminal status, or states that no ledgers were found.
|
|
45
|
-
|
|
46
|
-
## Risk presets decide where humans approve
|
|
47
|
-
|
|
48
|
-
`fast`: Merge only; `standard`: Plan + Merge; `controlled`: Intent + Plan + Merge + Release. A pending request must carry the findings summary and the reason, which prevents the "rubber stamp" decay; human waiting time goes into `metrics`.
|
|
49
|
-
|
|
50
|
-
## The finding triage gate and anchored review
|
|
51
|
-
|
|
52
|
-
> Since 2.0.0-beta.3.
|
|
53
|
-
The biggest structural lesson of the thirty-round deployment campaign: **a finding is a prescription, not a fact**. A memoryless fresh reviewer writes contradictory prescriptions and overturns designs that were accepted long ago; routing a fixer automatically turns that oscillation straight into cost. Two mechanisms work together:
|
|
54
|
-
|
|
55
|
-
1. **The triage gate**: with `reviewTriage: required` in the run config, P0/P1 findings from review no longer dispatch a fixer automatically; the Run stops at `WAITING_HUMAN` (kind `finding-triage`) and the pending reason lists every finding fingerprint. A human adjudicates first, then `approve --transition enter-fix` releases the fixer (or `reject` ends the Run).
|
|
56
|
-
2. **The adjudication ledger**: every finding lands in the Git plane at `delivery/work/<id>/review-findings.jsonl` (fingerprint = hash of severity plus normalized text):
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
buildbeat findings list --repo . --work WORK-X
|
|
60
|
-
buildbeat findings adjudicate --repo . --work WORK-X --fingerprint <fp> --action dismiss --by <name> --note "<why>"
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
After `dismiss`, the same fingerprint no longer blocks (raising it again is recorded visibly as `RE-RAISED`, but does not restart the loop); **a severity upgrade is a new fingerprint and blocks again on its own**: noise is suppressed, real signal is not, the same principle as observe's dismiss feedback.
|
|
64
|
-
3. **Anchor injection**: the reviewer (a readonly step) receives `anchor` in `BUILDBEAT_INPUT`, the full table of past findings and adjudications, and the envelope prompt should tell the reviewer that adjudicated conclusions must not be overturned; writing steps such as the fixer receive `findings` (last round's findings with their adjudication state), and the fixer repairs only accepted/open ones instead of guessing.
|
|
65
|
-
|
|
66
|
-
Adjudication memory lives in the Git plane; deleting the runtime does not lose it (covered by the same tests as invariant 23).
|
|
67
|
-
|
|
68
|
-
## Waiting must be able to find a person
|
|
69
|
-
|
|
70
|
-
> Since 2.0.0-beta.4 (iteration 08).
|
|
71
|
-
In the pilot workspace, 32 of 58 Runs were cancelled, most of them after hanging in `WAITING_HUMAN` for a full day; the average human wait was 7 to 12 hours. The cause was not slow people but **nobody knowing something was waiting for them**. Three things go together:
|
|
72
|
-
|
|
73
|
-
1. **What to say next**: `status` and `inbox` print the copyable command right after each wait (`approve` / `reject`, plus `findings list|adjudicate` during triage); `inbox` groups by Work and shows how long each has waited. The `--repo` in that output is a relative path inside the project and the placeholder `<repo-path>` outside it: a machine-local absolute path never enters the output.
|
|
74
|
-
2. **A new Run of the same Work supersedes the old wait**: on `start`, older Runs of the same Work that are still waiting are recorded as `SUPERSEDED` (terminal, compacted into a run-record), and the new Run's `RUN_CREATED.data.supersedes` records the lineage; the inbox keeps only live waits. Write `supersede: off` in the run config if you do not want this. RUNNING Runs are unaffected (active lock); old Runs locked by another process are skipped and reported.
|
|
75
|
-
3. **Outbound notifications**: `.buildbeat/notify.yaml` in the Git plane:
|
|
76
|
-
|
|
77
|
-
```yaml
|
|
78
|
-
kind: notify
|
|
79
|
-
version: 1
|
|
80
|
-
channels:
|
|
81
|
-
- id: owner
|
|
82
|
-
type: dingtalk # or webhook
|
|
83
|
-
urlEnv: BUILDBEAT_NOTIFY_URL # the URL comes only from an environment variable; a literal url is refused
|
|
84
|
-
events:
|
|
85
|
-
- HUMAN_REQUESTED
|
|
86
|
-
- RUN_TERMINAL
|
|
87
|
-
- STALLED
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
The CLI sends when a Run stops for a human or reaches a terminal state; subscribing to `STALLED` makes `start`/`resume` spawn a detached `watch` process that watches for silent worker output (threshold `stallAfterMs`, default 15 minutes). A failed send is only logged to `runs/<RUN>/notify.log` and the screen and **never affects the Run**; the payload carries identifiers, the reason, the candidate SHA and the next command, with zero logs and zero candidate content. A DingTalk custom robot needs its keyword configured (default `BuildBeat`). `doctor` reports whether the channel and its environment variable are in place.
|
|
91
|
-
|
|
92
|
-
Notification is not an approval channel: decisions are still made only through the CLI, with digest binding unchanged.
|
|
93
|
-
|
|
94
|
-
## From "waiting for me" to "where are we": overview
|
|
95
|
-
|
|
96
|
-
> Since 2.0.0-beta.4 (iteration 08).
|
|
97
|
-
`inbox` only knows which Run waits for a human; `buildbeat overview --repo .` answers per Work "how far, whose move next": whether intent/plan are accepted (edited after acceptance means `stale`), the latest Run's state and candidate, whether the candidate is merged into the current branch, the number of unadjudicated P0/P1 findings, whether `env-facts.md` exists, each row with its next command. After the runtime is deleted, the Git-plane run-records fill in. A session runs it first, then answers "where are we".
|
|
98
|
-
|
|
99
|
-
**Stage truth corrections (iteration 09)**: once a candidate is merged into the current branch the Work is `MERGED`, even if the latest Run is CANCELLED (a pilot login Run was cancelled over a budget problem while its candidate was already in production, and overview reported `STOPPED_CANCELLED` and urged a retry); a Work whose `release-readback` lane closed successfully shows `RELEASED` instead of "nothing to merge"; merged / released / closed Works no longer report unadjudicated finding counts. Each Work in `overview` also carries a `cost:` line (see the Work-level budgets in the [Workflow guide](02-workflow-guide.md), Chinese).
|
|
100
|
-
|
|
101
|
-
## You fixed it yourself: `resume --adopt`
|
|
102
|
-
|
|
103
|
-
> Since 2.0.0-beta.5 (iteration 09).
|
|
104
|
-
When a Run stops at `enter-fix` / `resume-fix`, the driving session or a person has often already fixed the problem in the Run's worktree and committed it. Approving at that point dispatches a fixer with nothing to do and runs verify once more (a pilot frontend Run reached its 5th verify and 3rd fix this way). Use instead:
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
buildbeat resume --config <run-config.yaml> --run <RUN-ID> --adopt <sha> --by <name>
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
The kernel reads the worktree back: the tree must be clean and HEAD must be exactly `<sha>` (7-character prefix or longer), otherwise it refuses; then it records `CANDIDATE_PINNED` with a human actor (`adopted: true`), records `DECISION_RECORDED` with that commit as subject (`adopted`, `resumeAt`), and continues from verify (the step after a successful fix in the preset). The ledger shows who supplied this candidate. Adoption is not accepted at the merge decision.
|
|
111
|
-
|
|
112
|
-
`doctor` now also prints whether intent / plan exist and are accepted in this repository's `delivery/work/<ID>/`, and for every policy requiring `artifact.accepted` it previews where `start` will stop; before that, doctor passed twice while start was blocked by "plan not mirrored into the sub-repository".
|
|
113
|
-
|
|
114
|
-
## Visible names are gate decisions
|
|
115
|
-
|
|
116
|
-
> Since 2.0.0-beta.4 (iteration 08).
|
|
117
|
-
The `BATCH_AT_GATE` tier of the three approval tiers explicitly includes domain names, service names, environment names, auto-stop durations, window durations: **names and parameters the owner will later see or say out loud**. Names a worker picks in passing never reach the ledger; the planner lists them in the intent with recommended values, and the human approves them in one go.
|
|
15
|
+
Notifications report waiting, termination and stalls; they do not accept approvals. Webhook/DingTalk URLs come only from environment variables, and delivery failure does not affect the run. The watcher is an internal feedback mechanism.
|
|
@@ -1,128 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 工作确认与决策
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](07-approval-guide.en.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
新工作只有一份 work.md。accept 绑定文件当前摘要,不会自动开工;用户授权继续后 run 推进。旧 intent/plan/spec 接受记录兼容读取。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
status 的决定卡包含 transition、候选、计划摘要、证据、原因和下一步。decide --action approve|reject 必须指向该 Run 与转换。批准前重新回读候选与冻结校验;变化、脏树、缺证据或不合格审查不能盖章。非终态批准后用 run 续跑,最终批准只表示具备合并条件。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
审查问题通过 decide --action accept|dismiss --work ... --fingerprint ... 裁决。精确指纹的驳回持续生效;换说法的相似性只用于停止不收敛循环,不代替人的裁决。
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
正常修复自动继续;重复问题、阻断数增加、环境故障或预算耗尽才形成例外决定。预算批准一次放行对应修复、重验和再审;扩额记录可以重放,驾驶会话不能自行冒充用户批准。
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
buildbeat inbox --repo . # 所有等人的 Run:transition、candidate、digest、理由
|
|
15
|
-
buildbeat status --repo . --run RUN-X # 单个 Run 的完整派生视图(步、证据、findings)
|
|
16
|
-
buildbeat approve --repo . --run RUN-X --transition enter-wait-merge --by <名字> --config <run-config>
|
|
17
|
-
buildbeat reject --repo . --run RUN-X --reason "<为什么>" --by <名字>
|
|
18
|
-
buildbeat accept --repo . --work WORK-X --artifact plan --by <名字> # 工件接受(digest 绑定)
|
|
19
|
-
```
|
|
13
|
+
合并和上线仍由人完成。上线后运行 `buildbeat release --config <config> [--note <text>]`:它确认该 Work 已成功 Run 的候选包含在 `--ref`(默认当前 HEAD)中,在主检出执行 run-config `release:` 段配置的只读回读命令(形状、环境白名单与脱敏同 worker),把结果、退出码、提交、输出摘要和末尾若干行追加到 `delivery/work/<ID>/releases.jsonl`。最近一次回读通过后,用 `decide --repo . --work <ID> --action close --result <text>` 关窗,写入绑定该回读的 close-work 决定;回读失败时不能关窗。run 配置都没有 `release:` 段的 Work 到合并为止,status 提示没有剩余必做步骤。
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## approve 的安全语义(都有测试)
|
|
24
|
-
|
|
25
|
-
1. **transition 必须匹配**当前待批项;
|
|
26
|
-
2. **盖章前重读实况**:待批快照与实况不一致(候选又动了、计划改了)→ 拒绝并要求刷新,不落章;
|
|
27
|
-
3. **transition 门在盖章瞬间 re-check**:merge-evidence-floor / ui-render-gate 等此刻不 PASS → 拒绝;
|
|
28
|
-
4. 终局决定(final-decision 类待批)批准即 `RUN_TERMINAL SUCCEEDED` + 压实 run-record 进 Git 面。
|
|
29
|
-
|
|
30
|
-
## 五个词各指什么(批准 ≠ 执行)
|
|
31
|
-
|
|
32
|
-
会话与文档里"接受 / 批准 / 恢复 / 成功 / 合并"混用过,统一如下:
|
|
33
|
-
|
|
34
|
-
| 词 | 命令 | 含义 | 不等于 |
|
|
35
|
-
|---|---|---|---|
|
|
36
|
-
| **接受**(accept) | `accept --artifact intent\|plan`,或 `--artifact intent,plan` 一次接受两份 | 每份工件的 digest 各自被人认可、各记一行;改过哪份哪份 `stale` | 开工;不产生任何 Run |
|
|
37
|
-
| **批准某转换**(approve) | `approve --transition <t>` | 允许 Run 走**这一条** transition:`enter-fix`(分诊后或 review 不收敛时放行 fixer;附带 grants 时还放行重验后的下一轮 review)、`resume-<step>`(预算耗尽后扩额,或 infra 退款后重试)、`enter-review`(Work 级 review 上限后再审一轮)、`enter-apply-readback`(上线车道"我做完了") | 批准了别的转换;非终态转换批准后 Run **不会自己动**,要 `resume --config <run-config>` 续跑(`approve` 输出的 `next:` 行会写明) |
|
|
38
|
-
| **合并决定**(最终批准) | `approve --transition enter-wait-merge` | 候选已具备合并条件:candidate + planDigest + evidenceDigest 此刻全部成立;Run 进终态 `SUCCEEDED`,run-record 压进 Git 面 | 代码已合并、已 push、已部署——这三件永远是你在 Runner 之外的动作 |
|
|
39
|
-
| **Run SUCCEEDED** | — | Run 停在了它该停的地方,证据齐 | Work 完成。`overview` 只有回读到候选在当前分支上才显示 `MERGED` |
|
|
40
|
-
| **拒绝**(reject) | `reject --reason` | Run 终止(`FAILED`,理由入账) | 工件失效;intent/plan 的接受状态不变 |
|
|
41
|
-
|
|
42
|
-
同理 observe 草稿的 `fix_now` 只是接受,Run 由人发起。保护动作见 [安全边界](09-security-boundaries.md)。
|
|
43
|
-
|
|
44
|
-
使用 `start --attempt new` 自动编号时,`resume --config <run-config.yaml>` 会续跑该家族唯一未终态的 Run,并打印选中的 ID;也可用 `--run <RUN-ID>` 显式指定配置中的 Run 本身或 `<家族>-NN`(数字至少两位)。配置本身已有台账时优先使用该精确 ID。多个未终态 Run 会列出候选并要求用 `--run` 选择;没有未终态 Run 会报告最新一次的 ID 和终态,没有台账则明确说明。
|
|
45
|
-
|
|
46
|
-
## 人批点由 Risk Preset 决定
|
|
47
|
-
|
|
48
|
-
`fast` 仅 Merge;`standard` Plan+Merge;`controlled` Intent+Plan+Merge+Release。待批项强制携带 findings 摘要与理由——防"秒批"退化;人批等待时长进 `metrics`。
|
|
49
|
-
|
|
50
|
-
## 预算停车:成功不扣次数,一轮一问
|
|
51
|
-
|
|
52
|
-
- 非只读步(build / verify / fix)成功的 attempt 不扣 `maxAttempts`,只读步仍消耗次数,review 按轮计费。`STEP_FINISHED.free: true` 与 `BUDGET_CONSUMED.amount: 0` 记录退款;没有新字段的旧台账保持原有回放结果。
|
|
53
|
-
- 真失败到顶仍停 `resume-<step>`,同指纹两次仍停,release 预设的 `maxAttempts: 1` 仍有效。infra 故障仍不扣次数。review 轮数只受 Work 级 `reviewRoundsPerWork`(默认 6)约束,只作兜底。
|
|
54
|
-
- review 发现阻断问题时,若下一轮会超过 Run 或 Work 上限,立即停 `enter-fix`,在花费修复、重验之前问一次。有分诊时 kind 为 `finding-triage`,无分诊时为 `budget`。批准表示「修复 + 重新验证 + 再审一轮」;拒绝结束本 Run,由人按现有证据决定是否合并。
|
|
55
|
-
- 上限之内只在 review 不收敛时停 `enter-fix`:同一个问题在修过之后又出现(同指纹,或换了说法但引用同一组文件、描述相近;已 dismiss 的不算),或本轮阻断数多于上一轮。kind 为 `review-not-converging`(有分诊时仍是 `finding-triage`,理由里多一条不收敛),请求不带 grants,批准不动预算。阻断 finding 都是新的、数量不多于上一轮时自动继续。见 [Workflow 指南](02-workflow-guide.md) 的「按收敛止损」。
|
|
56
|
-
- 每一次预算停车(`enter-fix`、`resume-<step>`、Work 级 `enter-review`)都在请求上记可选 `grants`,列出下一次执行会撞到的 Run/Work 上限;批准一次即同时放行两层,不再连问两次。`resume` 校验批准仍有效后逐条落 `BUDGET_EXTENDED`,并把整份放行计划钉在第一条上:放行落到一半进程被杀,再次 `resume` 按钉住的计划补齐剩余项,不从已被抬高的状态重算。过期批准、新请求不继承旧 grants。会话在 worktree 里手修并用 `resume --adopt <sha>` 回答该请求时,候选虽换成新提交,仍继承请求上的 grants(grants 属于这一轮,不属于某个候选;计划变了则不继承),修完重验后直接进入下一轮 review,不再二次停车。
|
|
57
|
-
- 防止自定义 workflow 的成功循环失控:同一步总 attempt 达到有效上限(配置预算 + 人批扩额)的 **3 倍**后,在下一次执行前仍以 kind `budget` 兜底停人。成功/infra 退款不增加兜底上限;批准扩额会提高它。
|
|
58
|
-
|
|
59
|
-
停车首行显示已用次数(或 review 轮数)、有效上限与真失败次数,并保留 `budget` 标识供度量使用。`status` 的 attempt 序号和 `overview` 的成本累计仍表示实际运行量,不是失败次数;`doctor` 展示配置上限。
|
|
60
|
-
|
|
61
|
-
## 发现分诊门与锚定审查
|
|
62
|
-
|
|
63
|
-
> 自 2.0.0-beta.3(beta.3)起。
|
|
64
|
-
来自三十轮部署战役最大的结构性教训:**finding 是处方不是事实**,无记忆 fresh reviewer 会开出互斥处方并翻案早已接受的设计,自动路由 fixer 让振荡直接烧钱。两个机制配套:
|
|
65
|
-
|
|
66
|
-
1. **分诊门**:run 配置 `reviewTriage: required` 后,review 产出 P0/P1 finding 不再自动派 fixer,而是停 `WAITING_HUMAN`(kind `finding-triage`),待批理由逐条列出 finding 指纹。人先裁决、再 `approve --transition enter-fix` 放行(或 `reject` 终止 Run)。
|
|
67
|
-
2. **裁决台账**:finding 全部落 Git 面 `delivery/work/<id>/review-findings.jsonl`(指纹 = 严重度+正文规范化 hash):
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
buildbeat findings list --repo . --work WORK-X
|
|
71
|
-
buildbeat findings adjudicate --repo . --work WORK-X --fingerprint <fp> --action dismiss --by <名字> --note "<为什么>"
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
`dismiss` 后同指纹不再阻断(重提会以 `RE-RAISED` 记账可见,但不重启循环);**严重度升级=新指纹,自动重新阻断**——压噪不压真信号,与 observe 的 dismiss 回调同一原则。
|
|
75
|
-
3. **锚定注入**:Reviewer(readonly 步)的 `BUILDBEAT_INPUT` 带 `anchor`(历史 finding+裁决全表),信封 prompt 应告知 reviewer"已裁决的结论不得翻案";fixer 等写入步的 input 带 `findings`(上一轮 review 的 finding 及其裁决状态)——fixer 只修 accepted/open,不猜。
|
|
76
|
-
|
|
77
|
-
裁决记忆在 Git 面,删 runtime 不丢(不变量 23 同款测试覆盖)。
|
|
78
|
-
|
|
79
|
-
## 等待要能找到人
|
|
80
|
-
|
|
81
|
-
> 自 2.0.0-beta.4(迭代 08)起。
|
|
82
|
-
试点工作区 58 个 Run 里 32 个被取消,多数是在 `WAITING_HUMAN` 挂满一天后批量清掉;人批平均等 7~12 小时。原因不是人慢,是**没人知道有东西等他**。三件事配套:
|
|
83
|
-
|
|
84
|
-
1. **下一句该说什么**:`status` 与 `inbox` 在每个等待后面直接给出可复制的命令(`approve` / `reject`,分诊时加 `findings list|adjudicate`);`inbox` 按 Work 分组并显示已等待时长。输出里的 `--repo` 只在项目内给相对路径,项目外给 `<repo-path>` 占位——本机绝对路径永不进输出。
|
|
85
|
-
2. **同 Work 新 Run 取代旧等待**:`start` 时同一 Work 下仍在等待的旧 Run 记 `SUPERSEDED`(终态、压成 run-record),新 Run 的 `RUN_CREATED.data.supersedes` 记血统;inbox 只剩活的等待。不想要这个行为就在 run 配置写 `supersede: off`。RUNNING 的 Run 不受影响(active 锁),被别的进程锁住的旧 Run 跳过并明示。
|
|
86
|
-
3. **通知出站**:Git 面 `.buildbeat/notify.yaml`:
|
|
87
|
-
|
|
88
|
-
```yaml
|
|
89
|
-
kind: notify
|
|
90
|
-
version: 1
|
|
91
|
-
channels:
|
|
92
|
-
- id: owner
|
|
93
|
-
type: dingtalk # 或 webhook
|
|
94
|
-
urlEnv: BUILDBEAT_NOTIFY_URL # URL 只能来自环境变量;写 url 直接拒绝
|
|
95
|
-
events:
|
|
96
|
-
- HUMAN_REQUESTED
|
|
97
|
-
- RUN_TERMINAL
|
|
98
|
-
- STALLED
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Run 停在人批或到终态时由 CLI 出站;订阅 `STALLED` 时 `start`/`resume` 会派一个脱离的 `watch` 进程盯 worker 输出静默(阈值 `stallAfterMs`,默认 15 分钟)。发送失败只记 `runs/<RUN>/notify.log` 与屏幕,**永不影响 Run**;载荷只有标识、原因、候选 SHA 与下一句命令,零日志零候选内容。钉钉自定义机器人需配置关键词(默认 `BuildBeat`)。`doctor` 报告通道与环境变量是否就位。
|
|
102
|
-
|
|
103
|
-
通知不是审批通道:拍板仍只能在 CLI 完成,digest 绑定不变。
|
|
104
|
-
|
|
105
|
-
## 从「等我批」到「到哪了」:overview
|
|
106
|
-
|
|
107
|
-
> 自 2.0.0-beta.4(迭代 08)起。
|
|
108
|
-
`inbox` 只知道哪个 Run 在等人;`buildbeat overview --repo .` 按 Work 回答「走到哪、下一步该谁」——intent/plan 是否被接受(接受后改过即 `stale`)、最新 Run 状态与候选、候选是否已合入当前分支、未裁决 P0/P1 数、是否有 `env-facts.md`,每行附下一句命令。运行时被删后由 Git 面 run-record 补足。会话开场先跑它,再回答用户「当前进度」。
|
|
109
|
-
|
|
110
|
-
**阶段判定的真相修正(迭代 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 级预算)。
|
|
111
|
-
|
|
112
|
-
## 你自己改好了:`resume --adopt`
|
|
113
|
-
|
|
114
|
-
> 自 2.0.0-beta.5(迭代 09)起。
|
|
115
|
-
Run 停在 `enter-fix` / `resume-fix` 时,驾驶会话或人常常已经在 Run 的 worktree 里把问题修掉并提交了。此时再 `approve` 会派一个无事可做的 fixer,再多跑一次 verify(试点一条前端 Run 因此跑到 verify 第 5 次、fix 第 3 次)。改用:
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
buildbeat resume --config <run-config.yaml> --run <RUN-ID> --adopt <sha> --by <名字>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
内核回读 worktree:树必须干净、HEAD 必须就是 `<sha>`(前缀 7 位起),否则拒绝;然后以人为 actor 落 `CANDIDATE_PINNED`(`adopted: true`)、以该提交为 subject 记 `DECISION_RECORDED`(`adopted`、`resumeAt`),并从 verify 继续(预设里 fix 成功后的下一步)。台账里看得出这一版候选是谁供的。合并决定处不接受 adopt。
|
|
122
|
-
|
|
123
|
-
`doctor` 现在还打印本仓 `delivery/work/<ID>/` 里 intent / plan 的存在与接受状态,并对每条要求 `artifact.accepted` 的 policy 预告"start 会停在哪一步"——此前两次 doctor 通过、start 却被"plan 未镜像到子仓"挡住。
|
|
124
|
-
|
|
125
|
-
## 可见命名是门前决策项
|
|
126
|
-
|
|
127
|
-
> 自 2.0.0-beta.4(迭代 08)起。
|
|
128
|
-
审批三级里 `BATCH_AT_GATE` 明确包含:域名、服务名、环境名、自停时长、窗口时长等**所有者以后要看见或念出来的名字与参数**。worker 顺手定的名字进不了台账;planner 在 intent 里列出并给推荐值,人一次批。
|
|
15
|
+
通知只报告待批、结束或疑似卡顿,不能接收审批;webhook/钉钉 URL 仅从环境变量读取,发送失败不影响 Run。看门进程是运行反馈的内部实现。
|