@haiyangbg/buildbeat 3.3.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.en.md +20 -124
  3. package/README.md +20 -124
  4. package/SKILL.md +73 -84
  5. package/docs/CAPABILITY-MATRIX.md +30 -39
  6. package/docs/MIGRATION.md +93 -0
  7. package/docs/README.md +8 -37
  8. package/docs/RELEASING.md +4 -3
  9. package/docs/v2/RFC-0001-product-definition.md +3 -0
  10. package/docs/v2/RFC-0002-domain-model.md +3 -0
  11. package/docs/v2/RFC-0003-workflow-policy.md +3 -0
  12. package/docs/v2/SPEC-0001-events-v1.md +3 -0
  13. package/docs/v2/guide/00-how-to-talk.en.md +12 -56
  14. package/docs/v2/guide/00-how-to-talk.md +12 -56
  15. package/docs/v2/guide/01-quickstart.en.md +42 -111
  16. package/docs/v2/guide/01-quickstart.md +42 -111
  17. package/docs/v2/guide/02-workflow-guide.en.md +13 -123
  18. package/docs/v2/guide/02-workflow-guide.md +14 -122
  19. package/docs/v2/guide/03-policy-guide.en.md +6 -50
  20. package/docs/v2/guide/03-policy-guide.md +6 -50
  21. package/docs/v2/guide/04-adapter-guide.en.md +7 -60
  22. package/docs/v2/guide/04-adapter-guide.md +7 -60
  23. package/docs/v2/guide/05-worker-contract.en.md +6 -53
  24. package/docs/v2/guide/05-worker-contract.md +7 -52
  25. package/docs/v2/guide/06-evidence-guide.en.md +8 -44
  26. package/docs/v2/guide/06-evidence-guide.md +9 -43
  27. package/docs/v2/guide/07-approval-guide.en.md +8 -110
  28. package/docs/v2/guide/07-approval-guide.md +8 -121
  29. package/docs/v2/guide/09-security-boundaries.en.md +13 -37
  30. package/docs/v2/guide/09-security-boundaries.md +13 -37
  31. package/docs/v2/guide/10-recovery.en.md +12 -96
  32. package/docs/v2/guide/10-recovery.md +12 -96
  33. package/docs/v2/guide/11-session-handoff.en.md +10 -75
  34. package/docs/v2/guide/11-session-handoff.md +10 -75
  35. package/docs/v2/guide/12-without-runtime.en.md +46 -0
  36. package/docs/v2/guide/12-without-runtime.md +46 -0
  37. package/docs/v2/guide/README.en.md +16 -36
  38. package/docs/v2/guide/README.md +16 -36
  39. package/example/AGENTS.md +15 -69
  40. package/example/README.md +8 -23
  41. package/example/delivery/envelope/prompts/builder.md +3 -1
  42. package/example/delivery/envelope/prompts/fixer.md +3 -1
  43. package/example/delivery/envelope/prompts/reviewer.md +3 -1
  44. package/example/delivery/work/WORK-EXPORT-DATE-FILTER/run-config.yaml +1 -1
  45. package/example//346/214/207/346/214/245/345/217/260.md +8 -40
  46. package/package.json +2 -1
  47. package/src/v2/adapters/notification-payload.js +40 -0
  48. package/src/v2/adapters/shell.js +3 -1
  49. package/src/v2/cli/run-config-check.js +129 -45
  50. package/src/v2/cli/run.js +853 -254
  51. package/src/v2/engine/reducer.js +2 -0
  52. package/src/v2/engine/risk-preset.js +14 -47
  53. package/src/v2/engine/workflow.js +177 -185
  54. package/src/v2/evidence/collector.js +12 -1
  55. package/src/v2/evidence/image.js +346 -0
  56. package/src/v2/policy/policy.js +219 -286
  57. package/src/v2/presets/software-delivery.yaml +1 -16
  58. package/src/v2/runtime/cache.js +22 -1
  59. package/src/v2/runtime/decisions.js +185 -31
  60. package/src/v2/runtime/findings.js +4 -1
  61. package/src/v2/runtime/notify.js +42 -36
  62. package/src/v2/runtime/orchestrator.js +811 -297
  63. package/src/v2/runtime/overview-repos.js +79 -0
  64. package/src/v2/runtime/overview.js +279 -47
  65. package/src/v2/runtime/release.js +233 -0
  66. package/src/v2/runtime/run-record.js +1 -0
  67. package/src/v2/workspace/workspace-manager.js +8 -0
  68. package/templates/v2/AGENTS.md +20 -73
  69. package/templates/v2/BUILDBEAT.md +2 -13
  70. package/templates/v2/envelope/prompts/builder.md +3 -1
  71. package/templates/v2/envelope/prompts/fixer.md +3 -1
  72. package/templates/v2/envelope/prompts/reviewer.md +3 -1
  73. package/templates/v2/run-config.example.yaml +9 -7
  74. package/templates/v2/work.example.md +16 -0
  75. package/templates/v2//346/214/207/346/214/245/345/217/260.md +8 -40
  76. package/docs/v2/skill/01-principles.md +0 -26
  77. package/docs/v2/skill/02-project-layout.md +0 -31
  78. package/docs/v2/skill/03-collaboration-rules.md +0 -45
  79. package/docs/v2/skill/04-rhythm-and-rituals.md +0 -104
  80. package/docs/v2/skill/05-red-lines.md +0 -13
  81. package/docs/v2/skill/06-bootstrap-and-takeover.md +0 -84
  82. package/docs/v2/skill/07-templates-and-lessons.md +0 -24
  83. package/example/.buildbeat/observe.yaml +0 -31
  84. package/src/v2/observe/observe-config.js +0 -194
  85. package/src/v2/observe/observe-reducer.js +0 -117
  86. package/src/v2/observe/observe.js +0 -427
  87. package/src/v2/presets/observe.yaml +0 -45
  88. package/src/v2/presets/policies/ui-render-gate.yaml +0 -13
  89. package/src/v2/presets/release-readback.yaml +0 -36
  90. package/src/v2/presets/risk/controlled.yaml +0 -39
  91. package/src/v2/presets/risk/fast.yaml +0 -19
  92. package/src/v2/presets/risk/release.yaml +0 -21
  93. package/src/v2/presets/risk/standard.yaml +0 -28
  94. package/templates/ARCHITECTURE.md +0 -50
  95. package/templates/contracts/PROTOCOL.md +0 -28
  96. package/templates/pm/adr/ADR-0000-template.md +0 -25
  97. package/templates/pm/adr/README.md +0 -15
  98. package/templates/pm/decisions.md +0 -11
  99. package/templates/standards/CODE.md +0 -23
  100. package/templates/standards/DESIGN.md +0 -36
  101. package/templates/standards/REVIEW.md +0 -20
  102. package/templates/standards/STACK.md +0 -31
@@ -1,127 +1,17 @@
1
- # Workflow authoring guide
1
+ # Fixed delivery flow and configuration
2
2
 
3
- [简体中文](02-workflow-guide.md) | **English**
3
+ [简体中文](02-workflow-guide.md)
4
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.
5
+ The built-in graph is build → verify → review → wait-merge; a failed verifier or P0/P1 review routes through fix → verify. New configurations omit workflow, riskPreset and policies. Arbitrary workflows and rule languages are retired.
6
6
 
7
- ## Shape
7
+ - Configure builder/verifier/reviewer/fixer commands in workers and allowed changes in allowedPaths. A missing worker produces an attended handoff.
8
+ - reviewTriage defaults to off; required retains explicit adjudication before fixing.
9
+ - budgets.reviewRoundsPerWork defaults to 6 across all runs of a Work. Per-step maxAttempts remain available. Successful writing steps and infrastructure failures do not spend the failure budget; a total-attempt safeguard remains.
10
+ - Repeated issues, including restated findings, or an increasing blocker count stop the loop. Adjudication still uses exact fingerprints.
11
+ - stopAt inserts a human boundary at a fixed step. maxReviewSeverity: P3 raises the final review requirement; the default is P2.
12
+ - parallel: true allows different Works to run together; runs of one Work stay exclusive. Tests must not share fixed ports or external mutable state.
13
+ - Verification caching matches tree, worker command and envelope digest. Failed results and dirty trees are never reused. Keep caching off for checks dependent on external changes.
14
+ - requires checks command versions and probes; envelope supplies role prompts, variables and Git pins; redact scrubs final evidence logs, not live output.
15
+ - Configuration is validated as a whole before execution. YAML is a strict subset; use block lists/maps as in the template.
8
16
 
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
- Review rounds have a single cap: `budgets.reviewRoundsPerWork` (default 6), counted across every Run of this Work (see "Review rounds counted per Work" below). A review step without an explicit `maxAttempts` takes that value as its per-Run cap, so the per-Run cap never fires before the Work cap; at the cap the Run stops at `WAITING_HUMAN` with the reason stating that the budget is exhausted. `budgets.maxAttempts.review` in the run config still sets a separate per-Run cap. (The official preset used to carry the campaign charter's "at most 2 review rounds per Run", which made a Run that needed two or three ordinary fix rounds ask for an extension again and again, and two caps had to be lifted together.)
73
-
74
- **Stop on non-convergence**: below the cap, each time review reports blocking findings, before a fixer is dispatched, the kernel compares this round's P0/P1 findings with the earlier rounds of this Run. If the same problem comes back after it was already sent to a fixer (same fingerprint, or a restatement: the same referenced files with a close description, or a very close description; dismissed ones do not count), or this round has more blocking findings than the last one, the Run stops at `enter-fix` (kind `review-not-converging`) and the reason names the returning fingerprints (a restatement reads `<this fingerprint> restates round N <earlier fingerprint>`) or the two counts. "Close description" is used only for this convergence check; fingerprints and adjudications still match the exact text, Severity does not count (a P1 that comes back as P0 is the same problem), and the same description at moved line numbers in the same files matches at any length; a description shorter than 16 characters once files and numbers are removed matches only in those two exact ways. Approving means fix + re-verify + one more review round and leaves the budget alone; `findings adjudicate --action dismiss` first stops a finding that should not block. While every blocking finding is new and there are no more of them than last round, the loop continues without asking anyone. With `reviewTriage: required` the triage stop happens as before and carries the non-convergence reason; at the cap the stop is still `budget`, with the same reason added.
75
-
76
- 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).
77
-
78
- The run config can override the preset (run config > preset > global `maxAttemptsPerStep`):
79
-
80
- ```yaml
81
- budgets:
82
- maxAttempts:
83
- review: 3
84
- verify: 6
85
- reviewRoundsPerWork: 6 # review rounds counted across every Run of this Work (superseded ones included); see the overview guide
86
- ```
87
-
88
- `doctor` prints each step's effective limit and where it comes from (run config / workflow preset / default).
89
-
90
- **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` (N = 6 when unset) 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.
91
-
92
- ## Worktrees live inside the repository: keep `.buildbeat/` out of test collection
93
-
94
- > Since 2.0.0-beta.5 (iteration 09).
95
- 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:
96
-
97
- - vitest: `test.exclude: ['**/node_modules/**', '**/.buildbeat/**']`
98
- - jest: `testPathIgnorePatterns: ['/node_modules/', '/.buildbeat/']`
99
- - pytest: `norecursedirs = .buildbeat`
100
- - Maven / Gradle only collect `src/**` and are unaffected; point Playwright's `testDir` at a specific directory.
101
-
102
- 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).
103
-
104
- ## Infrastructure failures and candidate defects are counted apart
105
-
106
- > Since 2.0.0-beta.5 (iteration 09).
107
- 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.
108
-
109
- 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`.
110
-
111
- ## Run config sections added in iteration 08
112
-
113
- - **`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`.
114
- - **`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)).
115
- - **`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).
116
- - **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`).
117
- - **`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.
118
- - **`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`.
119
- - **Step `grade:`** — a workflow step can declare the grade of its command evidence (L0–L4, default L2).
120
-
121
- ## The release readback lane: `release-readback` + `riskPreset: release`
122
-
123
- 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.
124
-
125
- ## Change discipline
126
-
127
- 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`.
17
+ Official legacy delivery files retain their digest and safeguards. Custom/release workflows are rejected explicitly, never silently downgraded. See [migration](../../MIGRATION.md). Exclude .buildbeat/** from test discovery.
@@ -1,127 +1,19 @@
1
- # Workflow 编写指南
1
+ # 固定交付流程与配置
2
2
 
3
- **简体中文** | [English](02-workflow-guide.en.md)
3
+ [English](02-workflow-guide.en.md)
4
4
 
5
- 权威:[`RFC-0003 §2`](../RFC-0003-workflow-policy.md);实现:`src/v2/engine/workflow.js`。官方预设 [`software-delivery.yaml`](../../../src/v2/presets/software-delivery.yaml) 是最好的范本。
5
+ 内置流程:build → verify → review → wait-merge;verify 失败或 review 有 P0/P1 时进入 fix → verify。新配置省略 workflow、riskPreset 和 policies,不再编写自定义流程或规则语言。
6
6
 
7
- ## 形状
7
+ - `workers` 配置 builder/verifier/reviewer/fixer 的命令;`allowedPaths` 限定修改范围。缺 worker 时停人接管。
8
+ - `reviewTriage: off` 是默认值;确需先裁决再修复的项目可设 required。
9
+ - `budgets.reviewRoundsPerWork` 默认 6,跨 Work 全部 Run 累计;可给单步设置 maxAttempts。非只读步骤成功与环境故障不扣失败预算,仍有总尝试兜底。
10
+ - 同一问题修后重现(包含换措辞)或阻断数增加即停人;裁决仍按精确指纹。
11
+ - `stopAt` 可在固定流程的指定步骤前停人;`maxReviewSeverity: P3` 可提高最终审查要求,默认 P2。
12
+ - `parallel: true` 允许不同 Work 同时推进;同一 Work 互斥。端口、数据库等外部资源必须独立。
13
+ - `cache: {verify: tree}` 的含义是按代码树、worker 命令和信封摘要复用成功验证;YAML 实际写法见样板,非空行内集合不支持。失败和脏树不复用,依赖外部变化的测试不要启用。
14
+ - `requires` 支持命令版本和项目探针;`envelope` 支持角色提示词、变量与 Git pin;`redact` 只脱敏最终证据日志,实时输出可能含原始内容。
15
+ - 配置先整体校验,未知键、错误类型和拼写会一次报清。
8
16
 
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
- ```
17
+ 旧版官方 delivery 文件可以兼容读取,保留原摘要和限制;自定义/上线流程明确拒绝,不自动降级。见 [迁移指南](../../MIGRATION.md)。
28
18
 
29
- ## 规则(加载期 fail-closed 校验)
30
-
31
- 1. **步序即默认边**:`steps` 的书写顺序定义 happy path——每步 `succeeded` 默认走向下一步;不想进默认链的步(如 `fix`)放在末尾、只经显式转换进入。
32
- 2. **显式转换**:`transitions` 的 `on` 取 worker 结果(`succeeded` / `failed` / `findings-blocking`);显式边优先于默认边。
33
- 3. **`readonly: true`**:该步 Worker 的任何工作树写入都会让步骤按失败处理并落账——Reviewer 不改代码是不变量 9,靠 Runner 的前后快照比对强制,不靠 prompt 自觉。
34
- 4. **`optional` / `requiredWhen`**:可选步默认跳过;`requiredWhen: ui-delivery` 在 UI 交付时强制(配合 [ui-render-gate](03-policy-guide.md) 与不变量 22)。
35
- 5. **`terminal`**:列出的步是出口。加载器做**无出口环检测**——verify⇄fix 这类环必须存在能到 terminal 的路径,否则拒绝加载。
36
- 6. **无 worker 的步**(如 `wait-merge`)是纯等待/决定点,Runner 在这里产生 `HUMAN_REQUESTED` 或按 `stopAt` 停下。
37
-
38
- ## 与 run 配置的关系
39
-
40
- run 配置里 `entry` 可覆盖 workflow 的 `entry`(例如从 `build` 起步、跳过 intent/plan 步——digest 仍会绑进批准对象);`stopAt` 指定停点。workflow 文件整体做 sha256 → `RUN_CREATED.workflowDigest`,事后可证明当时跑的是哪份流程。
41
-
42
- **写错时的报错形态**:`start` / `resume` / `doctor` / `preflight` / `approve --config` 读 run 配置时先整体校验,做任何事之前**一次列出全部问题**:
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
- 校验范围:必填键(`repo` `work` `run` `workflow` `workers`);未知顶层键与 worker / envelope 的未知字段(给最接近的拼写);类型与取值(正整数、列表、`inheritEnv` 只能是 `true` / `false`);worker 名必须被工作流步骤用到;`stopAt` / `entry` 必须是工作流步骤;`work` / `run` 只允许字母、数字、`.` `_` `-`,写成数字要加引号。
52
-
53
- **并行 Run(`parallel: true`,默认关)**:默认一个仓库同时只驱动一个 Run,其他 Work 的 `start` 会被挡并提示在谁后面排队(真实事故:一个会话在另一个 Work 的 Run 后面等了 3 小时 23 分钟)。
54
- run 配置写 `parallel: true` 的 Work,可以与其他同样打开开关的 Work 同时驱动;同一 Work 的 Run 永远互斥;没打开的 Run 照旧独占整个仓库——它在跑时并行 Run 起不来,并行 Run 在跑时它也起不来。
55
- 打开前先确认:verify 不抢固定端口、不共用同一个数据库或其他外部状态,否则并行会互相打架。`doctor` 会打印当前模式;被杀进程留下的并行标记与锁一样按持有者自动回收。
56
-
57
- run 配置还可声明(beta.3,皆来自三十轮部署战役的真实事故):
58
-
59
- - **`requires:` 环境契约**——信封隐式依赖的二进制与最低版本,Run 启动前 fail-closed 全量核验,一次报清所有问题(真实事故:`rg` 只在某会话 vendored PATH、`/bin/bash` 3.2、新 shell 解析到 Node 14,各烧掉整轮 Run 才见真因):
60
-
61
- ```yaml
62
- requires:
63
- - command: bash
64
- min: 4
65
- - command: rg
66
- ```
67
-
68
- - **`reviewTriage: required` 发现分诊门**——review 的阻断性 finding 先停人分诊、再派 fixer(见 [Approval 指南](07-approval-guide.md))。
69
-
70
- ## review 轮数预算
71
-
72
- review 轮数只有一个上限:`budgets.reviewRoundsPerWork`(默认 6),跨本 Work 所有 Run 累计(见下文"按 Work 累计的 review 轮数")。review 步没有显式 `maxAttempts` 时,它在单个 Run 内的上限就取这个值,所以不会先于 Work 上限触发;到顶时停 `WAITING_HUMAN`,理由写明预算耗尽。仍可在 run 配置里写 `budgets.maxAttempts.review` 另设每 Run 上限。(官方预设曾自带战役章程的"每 Run 2 轮封顶",正常修两三轮的 Run 也要一次次批准扩额,两层上限还要合并放行。)
73
-
74
- **按收敛止损**:上限之内,review 每次发现阻断问题、派 fixer 之前,内核把本轮 P0/P1 与本 Run 之前各轮比较。只要同一个问题在修过之后又出现(指纹相同,或者 reviewer 换了说法:引用同一组文件且描述相近,或描述高度相近;已 dismiss 的不算),或者本轮阻断数多于上一轮,就停 `enter-fix`(kind `review-not-converging`),理由列出又出现的指纹(换了说法时写成「本轮指纹 restates round N 原指纹」)或前后两轮的数量。「描述相近」只用于这里的收敛判断,finding 指纹和裁决照旧按原文精确匹配;严重度不参与(P1 升到 P0 仍算同一问题),引用同一组文件、描述只差行号也算;描述去掉文件与数字后不足 16 个字符时只认这两种精确情况。批准 = 修复 + 重验 + 再审一轮,不动预算;也可以先 `findings adjudicate --action dismiss`,让不该阻断的 finding 不再阻断。阻断 finding 都是新的、数量也不多于上一轮时,自动继续,不问人。开了 `reviewTriage: required` 时照常停分诊,不收敛的理由并入同一条请求;到顶时仍是 `budget` 停车,同样附上不收敛的理由。
75
-
76
- 预算耗尽后停的那次 `resume-<step>`,**人批准即多给一次**:内核落一条 `BUDGET_EXTENDED`(台账事实,可重放),该步上限 +1 再跑;拒绝即终止 Run。此前批准只会让同一请求立刻回来(试点两条应用登录 Run 因此以 CANCELLED 收场,候选却已在生产)。
77
-
78
- run 配置可覆盖预设(run 配置 > 预设 > 全局 `maxAttemptsPerStep`):
79
-
80
- ```yaml
81
- budgets:
82
- maxAttempts:
83
- review: 3
84
- verify: 6
85
- reviewRoundsPerWork: 6 # 跨本 Work 所有 Run(含已作废)累计的 review 轮数上限,见 overview 指南
86
- ```
87
-
88
- `doctor` 打印每步生效的上限与来源(run config / workflow preset / default)。
89
-
90
- **按 Work 累计的 review 轮数(迭代 09)**:每 Run 的预算挡不住"每轮一个新 Run"——试点一个 Work 跑了 21 个 Run、9 轮 review,2 轮封顶从未触发。`budgets.reviewRoundsPerWork: N`(不写时 N = 6)让内核在 review 步起跑前统计本 Work **所有** Run(含已作废、含已压成 run-record 的)的 review 轮数,达到 N 即停 `WAITING_HUMAN`(kind `work-review-cap`,transition `enter-review`):批准即再审一轮(台账 `BUDGET_EXTENDED scope=work`),拒绝则按手头证据合并或关闭。`overview` 每个 Work 多一行 `cost: review rounds · findings · human waits · worker 时长`,run-record 也带 `cost` 块——"继续还是砍"之前先看这一行;intent 里的止损线(最多几个 Run / 几轮 review / 几小时)就对着它核。
91
-
92
- ## 工作树在仓内:把 `.buildbeat/` 排除出测试收集
93
-
94
- > 自 2.0.0-beta.5(迭代 09)起。
95
- Run 的隔离工作树在 `<repo>/.buildbeat/worktrees/<RUN>/`,运行时台账在 `<repo>/.buildbeat/runtime/`。两者都不入 git(模板 `.gitignore` 已排除;尊重 `.gitignore` 的工具如 `rg`、`gitleaks` 随之不再走进去),但**测试框架按文件系统收集用例**:试点合并后的主干 vitest 把残留工作树里旧候选的用例一起跑了,噪声直到 `gc` 才消失。在项目里加:
96
-
97
- - vitest:`test.exclude: ['**/node_modules/**', '**/.buildbeat/**']`
98
- - jest:`testPathIgnorePatterns: ['/node_modules/', '/.buildbeat/']`
99
- - pytest:`norecursedirs = .buildbeat`
100
- - Maven / Gradle 只收集 `src/**`,不受影响;Playwright 的 `testDir` 指到具体目录即可。
101
-
102
- `start` 被「another run is active」挡住时,CLI 现在打印持锁的 Run、它在哪一步、最后一次事件多久前,以及可复制的 `status` 命令。默认仍是一个仓库同时驱动一个 Run;自 3.2.0 起,测试互不抢端口/数据库的 Work 可在 run 配置里打开 `parallel: true` 并行驱动(见上文「并行 Run」)。
103
-
104
- ## 基础设施故障与候选缺陷分开算
105
-
106
- > 自 2.0.0-beta.5(迭代 09)起。
107
- worker 的超时、崩溃、非信封输出,以及 worker 主动以退出码 **75** 结束(约定:verify / 包装脚本发现环境不满足——命令不在 PATH、端口被占、后端 404、沙箱禁止监听——就 `exit 75`),内核一律判 `infra`:`STEP_FINISHED.data.infra = true`,不记失败指纹、不派 fixer、该步预算不扣(`steps[step].infraAttempts` 抵回),停 `WAITING_HUMAN`(kind `infra`)。人批准 `resume-<step>` 重跑,拒绝结束。其余非零退出仍是候选失败,走 `on: failed` 边。
108
-
109
- 没有转移边的失败结果(预设里 build、review、fix 的 `failed`)也不再终态,停 `resume-<step>` 交人决定。终态 FAILED 只剩 policy `BLOCK`。
110
-
111
- ## 迭代 08 新增的 run 配置段
112
-
113
- - **`envelope:`** —— `prompts:`(目录,相对 run 配置)+ `vars:`(`{vars.x}` 替换)+ 可选 `pin: <sha>`(从该提交读 prompt,冻结信封)。内核按 `<component>-<worker>.md` → `<worker>.md` 取 prompt,落到 `runs/<RUN>/prompts/<step>-<n>.md`,以 `BUILDBEAT_PROMPT`(路径)和 `input.envelope`(`promptRef / file / digest / vars`)交给 worker;worker args 里可用 `{prompt}` 与 `{vars.x}`。`RUN_CREATED` 记 `envelopeDigest`。
114
- - **`start --attempt new`** —— `run:` 写家族名(`RUN-X`),内核编成 `RUN-X-01/02…`(扫运行时面与 Git 面 run-record,删 runtime 也不撞号);同 Work 旧的等待自动作废([Approval 指南](07-approval-guide.md))。
115
- - **`cache:`** —— `verify: tree`:同 `HEAD^{tree}` + 同 worker 命令 + 同信封 digest 且**已通过**的 verify 复用证据(台账 `reused`,status 标 `(reused from RUN-X)`);失败、脏树不复用。verifier 依赖树外事物(远端、时间)的项目不要开。
116
- - **增量审查** —— readonly 步的 input 带 `lastReviewed {candidate, run, evidenceRef, range}`(同 Work 最近一次 review 的候选且为当前候选祖先);reviewer prompt 可要求只审 `range` 内 diff,锚定裁决照旧(`anchor`)。
117
- - **`redact:`** —— 正则列表,证据日志落盘前替换为 `<REDACTED>`;digest 绑脱敏后文本。实时流(`.live`)不脱敏、步结束即删。
118
- - **`requires:` 的 `probe:` 条目** —— `probe: <shell 命令>` + 可选 `expect: <正则>` + `name:`;退出码非 0 或输出不匹配即 fail-closed,与二进制版本项一次报清。把踩出来的环境事实(Redis ≥ 7、目标机 Python 版本、端口可达)写成 probe,下窗不重踩;叙述性事实放 `delivery/work/<ID>/env-facts.md`。
119
- - **step `grade:`** —— workflow 步骤可声明该步命令证据的等级(L0–L4,默认 L2)。
120
-
121
- ## 上线回读车道:`release-readback` + `riskPreset: release`
122
-
123
- 内核没有部署能力(不变量 20),生产动作永远是人的。这条车道只把动作前后的**回读**记成 L4 台账:`preflight`(动作前只读检查)→ 停 `enter-apply-readback`(人做动作)→ `apply-readback`(证明动作生效)→ `observe`(证明健康)→ `wait-close`(人关窗)。三个回读步全部 `readonly`、`grade: L4`、`maxAttempts 1`:任一步失败即停人批,没有 fix 边。风险预设 `release` 提供 `stopAt: apply-readback` 与关窗证据门(L4 命令证据)。worker 是任意回读脚本(curl 健康、读版本、比对配置指纹),退出码就是结论。试点项目上线那天的四十条手工 readback 提交,就是这条车道该做的事。
124
-
125
- ## 修改纪律
126
-
127
- 预设是产品的一部分:改 `software-delivery.yaml` 前先想清是不是项目差异——项目差异用自己的 workflow 文件(run 配置 `workflow:` 指过去),不改官方预设。schema additive-only,破坏性改法升 `version`。
19
+ 测试框架须排除 `.buildbeat/**`,避免收集隔离工作树中的测试。
@@ -1,55 +1,11 @@
1
- # Policy guide
1
+ # Built-in delivery checks
2
2
 
3
- [简体中文](03-policy-guide.md) | **English**
3
+ [简体中文](03-policy-guide.md)
4
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).
5
+ New runs use fixed checks: the work artifact is accepted at its current digest, and the final candidate has command evidence and an independent review. Missing or unverified facts never pass. The custom rule language, arbitrary hook points and UI-specific policies are retired.
6
6
 
7
- ## The shape of a policy
7
+ Safeguards are frozen in RUN_CREATED. Resume rejects changes. Approval reads the frozen checks even without a configuration argument. Editing the work artifact invalidates its acceptance and cannot reuse an earlier run approval.
8
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
- ```
9
+ Legacy fast/standard/controlled names only translate to the same acceptance and severity requirements. In particular, controlled's P3 floor cannot be weakened to P2. New projects use explicit options instead of profiles.
24
10
 
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.
11
+ Project-specific security, interface, UI and release requirements belong in project instructions and real verification commands. Branch protection and host isolation remain the responsibility of their platforms.
@@ -1,55 +1,11 @@
1
- # Policy 指南
1
+ # 内置交付校验
2
2
 
3
- **简体中文** | [English](03-policy-guide.en.md)
3
+ [English](03-policy-guide.en.md)
4
4
 
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)。
5
+ 新运行使用固定校验:工作说明已按当前内容接受;最终候选有真实命令证据和独立审查;缺失或无法核对的信息不能通过。规则语言、任意挂点和 UI 专属策略已退出产品。
6
6
 
7
- ## 一条 Policy 的形状
7
+ 校验条件随 Run 创建记录冻结。恢复时拒绝改变条件;最终批准从台账读取条件,即使没有传配置也不会省略。工作说明修改后旧接受失效,运行中的工作说明也不能换成新内容后沿用旧批准。
8
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 填步 id;transition 填 enter-<步>
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
- ```
9
+ 旧 fast/standard/controlled 名称只做兼容转换:保留原接受要求与严重度门槛。尤其 controlled 的 P3 门槛不能降为 P2。新项目只写所需明确选项,不再选择套餐。
24
10
 
25
- ## 8 个算子与三值逻辑
26
-
27
- | 算子 | 含义 |
28
- |---|---|
29
- | `all` / `any` / `not` | 组合子 |
30
- | `evidence.exists: {kind, minGrade}` | 存在指定种类、等级达标的证据 |
31
- | `artifact.accepted: {artifact}` | 工件(plan/intent/spec)已被 digest 绑定地接受 |
32
- | `attempts.lt: {step, max}` | 某步尝试次数未超上限 |
33
- | `budget.remaining: {kind}` | 预算仍有余量 |
34
- | `candidate.clean` | 存在已固定且干净的 candidate |
35
- | `human.approved: {transition}` | 存在未 stale 的对应批准 |
36
- | `finding.maxSeverity: {atMost}` | 未解决 findings 严重度不超过阈值 |
37
-
38
- 求值是**三值**的:`PASS` / `FAIL` / `UNVERIFIED`。取不到数据(证据缺失、无 candidate)永远是 `UNVERIFIED` 而不是通过——`UNVERIFIED` 在任何门上都不会被当作 `PASS`(GateResult 六值见 RFC-0003 §3.3)。
39
-
40
- **候选作用域**:待批对象带 candidate 时,`evidence.exists` 与 `finding.maxSeverity` 只统计该 candidate 的证据——被新一轮修复取代的旧 review findings 不会挡住已修好的候选(2026-08-28 真实事故的永久回归在 `tests/` 与 `evals/`)。
41
-
42
- ## 四类挂点
43
-
44
- - `pre`:步开始前(如 `plan-accepted` 挡在 build 前);
45
- - `post`:步结束后;
46
- - `transition`:状态转换瞬间(如 merge 决定盖章那一刻 re-check——批准命令会在盖章前重读实况,门不过则拒绝落章);
47
- - `action`:保护动作前(配合 [安全边界](09-security-boundaries.md))。
48
-
49
- ## 强制等级
50
-
51
- `ADVISORY` 只提示 Worker;`LOCAL_ENFORCED` 由 Runner/Workspace 物理执行(本地能保证的都用它);`SERVER_ENFORCED` 声明该门在服务端(分支保护/CI/部署平台)——Runner 落账但不能替服务端保证。诚实标注:本地挡不住的不要标 LOCAL。
52
-
53
- ## 接入
54
-
55
- run 配置 `policies:` 列表引用文件路径;risk 预设(`fast`/`standard`/`controlled`/`release`)自带一组策略与停点,`riskPreset:` 一行即可启用,再叠加项目自定义策略。
11
+ 项目的安全、接口、UI 和上线要求放在项目规范及真实验证命令中。服务端分支保护与宿主隔离仍由各自平台负责。
@@ -1,66 +1,13 @@
1
- # Adapter guide
1
+ # Worker integration
2
2
 
3
- [简体中文](04-adapter-guide.md) | **English**
3
+ [简体中文](04-adapter-guide.md)
4
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.
5
+ The Shell Adapter runs a configured AI CLI or script in the isolated worktree. The chosen tool owns its model, authentication and business capability. Recorded real AI worker evidence covers codex exec; other tools need their own validation.
6
6
 
7
- ## Shell adapter: every CLI is a worker
7
+ BUILDBEAT_INPUT carries work/step context, and BUILDBEAT_PROMPT points to the materialized prompt. Structured steps write BUILDBEAT_OUTPUT. The runner reads verifier exit codes and logs. The shipped worker.sh handles arguments, commits and envelopes.
8
8
 
9
- `workers.<role>` in the run config is exactly one shell adapter configuration:
9
+ The default environment allowlist is PATH HOME LANG LC_ALL TMPDIR TERM USER SHELL. env injects named values; inheritEnv explicitly enables inheritance. Files under HOME are outside this boundary.
10
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
- ```
11
+ Timeouts, crashes, invalid JSON and exit 75 are infrastructure failures: stop for a human without dispatching a fixer. Ordinary nonzero exits are candidate failures. Live stdout/stderr supports status and becomes evidence logs when the step finishes.
22
12
 
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.
13
+ The Mock Adapter validates deterministic protocol behavior, not real model capability.
@@ -1,66 +1,13 @@
1
- # Adapter 指南
1
+ # Worker 接入
2
2
 
3
- **简体中文** | [English](04-adapter-guide.en.md)
3
+ [English](04-adapter-guide.en.md)
4
4
 
5
- 权威:[`RFC-0002 §Adapter`](../RFC-0002-domain-model.md);实现:`src/v2/adapters/shell.js`(生产用)、`src/v2/adapters/mock.js`(测试用)。裁决 #5:厂商中立——不绑定任何 Agent 供应商。
5
+ Shell Adapter 执行配置的 AI CLI 或脚本;运行目录是隔离工作树。模型、鉴权和业务能力由所用工具提供。已有真实 AI worker 证据覆盖 codex exec;其他工具需要独立验证。
6
6
 
7
- ## Shell Adapter:一切 CLI 皆 Worker
7
+ 输入:BUILDBEAT_INPUT 包含工作、步骤、候选上下文;BUILDBEAT_PROMPT 指向本轮提示词。输出:结构化步骤写 BUILDBEAT_OUTPUT;verifier 的退出码和日志由内核读取。随包 worker.sh 负责参数、提交和信封传递。
8
8
 
9
- run 配置的 `workers.<角色>` 就是一份 Shell Adapter 配置:
9
+ 默认环境变量仅 PATH HOME LANG LC_ALL TMPDIR TERM USER SHELL;env 可指定注入,inheritEnv 明确放开继承。HOME 下的文件不在环境变量隔离范围内。
10
10
 
11
- ```yaml
12
- workers:
13
- builder:
14
- command: codex
15
- args:
16
- - exec
17
- - -s
18
- - workspace-write
19
- - <prompt 或脚本参数>
20
- timeoutMs: 900000
21
- ```
11
+ 超时、崩溃、无效 JSON 或 exit 75 表示环境故障,停人且不派 fixer。普通非零退出按候选失败处理。实时 stdout/stderr 供 status 读取,结束后转为证据日志。
22
12
 
23
- - 执行目录 = 该步的隔离 worktree(不是主检出);
24
- - `args` 支持模板:`{workspace}` `{step}` `{worker}`;
25
- - 已实证的 Worker:`codex exec`(M4 四个真实试点)、任意 bash 脚本;`claude -p` 同构可换。
26
-
27
- ## env 白名单(默认,能力移除的一部分)
28
-
29
- Worker 子进程默认**只**拿到 `PATH HOME LANG LC_ALL TMPDIR TERM USER SHELL`——宿主 shell 里的云凭据、token **环境变量**到不了 Worker(注意 `HOME` 在白名单里,凭据**文件**不在此边界之内,见 [安全边界](09-security-boundaries.md))。`inheritEnv: true` 可显式打开(doctor 会把它标为仅 ADVISORY 隔离);单个变量可用 `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
- `env:` 的值必须是标量(写进子进程前转成字符串),变量名须合法(字母、数字、下划线);不合法时 `doctor` / `start` 在加载配置时直接报错。**2.0.0 及更早的 CLI 加载路径会丢掉这两个字段**(doctor 报告了姿态,start 却按默认白名单跑,`env:` 的变量到不了 worker);2.0.1 起透传,并有 CLI 端到端回归(`tests/v2-run-cli.test.js`)。直接调用 `createShellAdapter` 的 API 用户不受此影响。
44
-
45
- ## 输入输出
46
-
47
- - 输入:`BUILDBEAT_INPUT` 环境变量携带 JSON(step/worker/candidate/失败摘要等,按角色见 [Worker 合同](05-worker-contract.md));
48
- - 输出:需要结构化结果的步(reviewer 等)从 `BUILDBEAT_OUTPUT` 指向的路径写 JSON 信封;codex 用 `-o` 落最后消息再由包装脚本转写也可以;
49
- - 纯命令步(verifier 跑测试)不需要信封——退出码与日志由 Runner 回读为证据。
50
-
51
- ## 结果语义
52
-
53
- Adapter 只报告事实:exitCode / signal / timedOut / spawnError / stdout / stderr / 起止时间。写事件的是 Orchestrator,Adapter 永不触碰内核状态。超时、崩溃、无法启动分别落 `timeout` / `crashed` / 失败路径,都有端到端测试(`tests/v2-invariants.test.js`)。
54
-
55
- ## Mock Adapter
56
-
57
- `createMockAdapter(script)`:按步给定 `"succeed"`/`"fail"` 或 `{behavior, envelope}` 序列,用于测试与 evals;行为卡见 [`evals/`](../../../evals/README.md)。
58
-
59
- ## 何时写专用 Adapter
60
-
61
- 只有当 Shell 表达不了(需要流式交互、会话保持)才写专用 Adapter;按 M3 裁决,先用 Shell 接一切,等真实试点证明不够再说。
62
-
63
- ## 实时输出
64
-
65
- > 自 2.0.0-beta.4(迭代 08)起。
66
- 编排器给 Shell Adapter 传 `liveDir` 时,子进程的 stdout/stderr 直接写到 `<liveDir>/<step>-<attempt>.{stdout,stderr}.live`(fd 直连,不经父进程缓冲),并写 `live.json`(`step / attempt / worker / command / startedAt`)。步返回后 Adapter 读回两份流作为 `stdout` / `stderr`,删掉实时文件——结果形状不变,证据收集器照旧。自写 Adapter 若想被 `status` 的"最后输出距今"识别,产出同名文件即可;不产出则 `status` 只显示已用时间。
13
+ Mock Adapter 只用于确定性回归,不能证明真实模型的业务能力。