@haiyangbg/buildbeat 2.0.2 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +25 -301
  2. package/README.en.md +9 -22
  3. package/README.md +6 -19
  4. package/SKILL.md +169 -220
  5. package/bin/buildbeat.js +14 -2
  6. package/docs/CAPABILITY-MATRIX.md +13 -55
  7. package/docs/README.md +13 -14
  8. package/docs/RELEASING.md +10 -9
  9. package/docs/v2/RFC-0001-product-definition.md +2 -0
  10. package/docs/v2/RFC-0003-workflow-policy.md +2 -0
  11. package/docs/v2/guide/00-how-to-talk.md +3 -3
  12. package/docs/v2/guide/01-quickstart.en.md +163 -0
  13. package/docs/v2/guide/01-quickstart.md +15 -13
  14. package/docs/v2/guide/03-policy-guide.md +1 -1
  15. package/docs/v2/guide/06-evidence-guide.en.md +51 -0
  16. package/docs/v2/guide/06-evidence-guide.md +5 -3
  17. package/docs/v2/guide/07-approval-guide.en.md +115 -0
  18. package/docs/v2/guide/07-approval-guide.md +12 -10
  19. package/docs/v2/guide/10-recovery.en.md +83 -0
  20. package/docs/v2/guide/10-recovery.md +8 -6
  21. package/docs/v2/guide/11-session-handoff.en.md +2 -2
  22. package/docs/v2/guide/11-session-handoff.md +2 -2
  23. package/docs/v2/guide/README.md +4 -10
  24. package/example/.buildbeat/notify.yaml +13 -0
  25. package/example/.buildbeat/observe.yaml +31 -0
  26. package/example/AGENTS.md +67 -13
  27. package/example/BUILDBEAT.md +8 -11
  28. package/example/CLAUDE.md +1 -1
  29. package/example/README.md +17 -67
  30. package/example/delivery/envelope/prompts/builder.md +10 -0
  31. package/example/delivery/envelope/prompts/fixer.md +10 -0
  32. package/example/delivery/envelope/prompts/reviewer.md +13 -0
  33. package/example/delivery/envelope/worker.sh +70 -0
  34. package/example/delivery/work/WORK-EXPORT-DATE-FILTER/decisions.jsonl +3 -0
  35. package/example/delivery/work/WORK-EXPORT-DATE-FILTER/intent.md +24 -0
  36. package/example/delivery/work/WORK-EXPORT-DATE-FILTER/plan.md +20 -0
  37. package/example/delivery/work/WORK-EXPORT-DATE-FILTER/run-config.yaml +66 -0
  38. package/example/delivery/work/WORK-EXPORT-DATE-FILTER/runs/RUN-EXPORT-01/run-record.json +108 -0
  39. package/example/delivery/work/WORK-EXPORT-DATE-FILTER/workflow.yaml +44 -0
  40. package/example/gitignore.template +20 -0
  41. package/example/package.json +13 -0
  42. package/example/pm/decisions.md +3 -15
  43. package/example/src/export.js +25 -0
  44. package/example/src/ledger.js +16 -0
  45. package/example/tests/export.test.js +35 -0
  46. package/example//346/214/207/346/214/245/345/217/260.md +40 -0
  47. package/lessons.md +52 -71
  48. package/package.json +3 -7
  49. package/src/v2/cli/run.js +33 -23
  50. package/src/v2/engine/risk-preset.js +1 -1
  51. package/src/v2/runtime/notify.js +5 -5
  52. package/src/v2/runtime/overview.js +7 -7
  53. package/templates/ARCHITECTURE.md +1 -1
  54. package/templates/contracts/PROTOCOL.md +2 -10
  55. package/templates/gitignore.template +0 -3
  56. package/templates/pm/adr/README.md +1 -1
  57. package/templates/pm/decisions.md +4 -5
  58. package/templates/standards/CODE.md +1 -1
  59. package/templates/standards/DESIGN.md +1 -1
  60. package/templates/standards/REVIEW.md +2 -2
  61. package/templates/standards/STACK.md +2 -8
  62. package/templates/v2/AGENTS.md +18 -18
  63. package/templates/v2/BUILDBEAT.md +2 -3
  64. package/templates/v2/CLAUDE.md +1 -1
  65. package/templates/v2/run-config.example.yaml +1 -1
  66. package/templates/v2//346/214/207/346/214/245/345/217/260.md +6 -6
  67. package/bin/buildbeat-v2.js +0 -18
  68. package/bin/solobaton.js +0 -6
  69. package/docs/CHECKS.md +0 -326
  70. package/docs/CLI.md +0 -245
  71. package/docs/LEGACY-V1.16-MIGRATION.md +0 -54
  72. package/docs/v2/guide/08-migration-v1.md +0 -72
  73. package/example/.buildbeat/manifest.json +0 -45
  74. package/example/ARCHITECTURE.md +0 -39
  75. package/example/contracts/PROTOCOL.md +0 -38
  76. package/example/pm/NOW.md +0 -22
  77. package/example/pm/adr/ADR-0001-local-first-sqlite.md +0 -25
  78. package/example/pm/adr/README.md +0 -7
  79. package/example/pm/archive//344/270/200/346/234/237/evidence/gate1.md +0 -5
  80. package/example/pm/archive//344/270/200/346/234/237/evidence/gate2.md +0 -5
  81. package/example/pm/archive//344/270/200/346/234/237/evidence/gate3.md +0 -5
  82. package/example/pm/archive//344/270/200/346/234/237/evidence/gate4.md +0 -5
  83. package/example/pm/archive//344/270/200/346/234/237/evidence/implementation.md +0 -5
  84. package/example/pm/status//344/272/247/345/223/201.md +0 -20
  85. package/example/pm/status//345/205/250/346/240/210.md +0 -15
  86. package/example/pm/status//346/265/213/350/257/225.md +0 -15
  87. package/example/pm//344/270/200/346/234/237-/347/234/213/346/235/277.md +0 -97
  88. package/example/standards/CODE.md +0 -18
  89. package/example/standards/DESIGN.md +0 -34
  90. package/example/standards/REVIEW.md +0 -16
  91. package/example/standards/STACK.md +0 -31
  92. package/src/cli.js +0 -323
  93. package/src/constants.js +0 -202
  94. package/src/doctor.js +0 -267
  95. package/src/planner.js +0 -251
  96. package/src/project.js +0 -844
  97. package/src/upgrader.js +0 -1249
  98. package/src/v2/presets/risk/legacy-four-gates.yaml +0 -44
  99. package/src/writer.js +0 -534
  100. package/templates/.claude/agents/reviewer.md +0 -62
  101. package/templates/AGENTS.md +0 -85
  102. package/templates/BUILDBEAT.md +0 -13
  103. package/templates/CLAUDE.md +0 -7
  104. package/templates/pm/NOW.md +0 -26
  105. package/templates/pm/changes/README.md +0 -44
  106. package/templates/pm/status/README.md +0 -32
  107. package/templates/pm//345/275/223/346/234/237/347/234/213/346/235/277.md +0 -62
  108. package/templates/scripts/bus-check.sh +0 -1875
  109. package/templates/scripts/design-preview.sh +0 -44
  110. package/templates/scripts/drift-check.sh +0 -112
  111. package/templates/scripts/pre-commit.sh +0 -74
  112. package/templates/scripts/verify-status.sh +0 -105
  113. package/templates//346/214/207/346/214/245/345/217/260.md +0 -58
package/docs/CHECKS.md DELETED
@@ -1,326 +0,0 @@
1
- # BuildBeat file-bus check specification
2
-
3
- Status: **BuildBeat 1.20 / WP3.4 implementation baseline** · normative bus-check schema: `1` · canonical scoped package metadata is `@haiyangbg/buildbeat@1.20.0`; legacy `solobaton@1.16.3` remains the read-only v0 distribution. CLI output/manifest schema 2 and mechanical upgrade are specified separately in [`CLI.md`](CLI.md); the genuine version-increment and real multi-repository refresh evidence is archived in [`PHASE4-V1.20-PILOT-2026-08-25.md`](PHASE4-V1.20-PILOT-2026-08-25.md). None of those facts changes this bus-check schema or proves registry publication.
4
-
5
- This document is the single semantic source for `templates/scripts/bus-check.sh` and the same-directory scripts it orchestrates. It defines what a result means, not merely how output is colored. `SKILL.md`, board templates, fixtures, and script tests must use the same tokens and finding codes.
6
-
7
- The Node CLI does not reimplement these synchronous file-bus checks. `doctor` keeps its existing inspection taxonomy; the relationship is documented in the appendix.
8
-
9
- ## 1. Authority and evidence boundary
10
-
11
- The check layers are:
12
-
13
- | Layer | Authority | Boundary |
14
- |---|---|---|
15
- | `bus-check.sh` | one synchronous report for file-bus, Gate, evidence, reference, stack, standards, and ADR findings | may aggregate sibling scripts, but must not invent facts they did not return |
16
- | `verify-status.sh` | configured L3-suite state | an unconfigured suite is `unverified`, never green evidence; `--run` exits non-zero when any configured suite fails |
17
- | `drift-check.sh` | configured production-fact comparison | missing adapters, failed queries, or truncated data are `unverified`, never “no drift” |
18
- | `pre-commit.sh` | consumes `bus-check --strict` plus commit-local guards | a local hook is not server-side enforcement and can be absent on another clone |
19
- | `doctor` | scaffold/install inspection | does not approve Gates or duplicate the bus result taxonomy |
20
-
21
- The scripts may confirm only repository-visible facts and configured adapter results. They cannot prove that documentation matches arbitrary source code, that a human really approved a Gate, that a deployment is healthy, or that an unscanned path is clean. Those limits must appear as `unverified` findings or coverage reasons.
22
-
23
- ## 2. File-bus invariants
24
-
25
- The eight invariant IDs are stable. New implementations extend the registry rather than renumbering it.
26
-
27
- | ID | Normative rule | Machine-verifiable part | Must remain `unverified` without extra evidence | Implementation route |
28
- |---|---|---|---|---|
29
- | INV-1 | `pm/NOW.md` points to exactly one valid current board | NOW exists; one parseable current-period line and one board pointer; target is a live regular file under `pm/`, not `pm/archive/` | whether the named period is semantically the real current priority | current script partially checks existence/rot; Phase 1 adds stable codes |
30
- | INV-2 | current board, status, and actual work state do not contradict one another | parseable work-package state; resolvable candidate hashes; configured L3 freshness; explicit contradictions between machine tokens | arbitrary prose status, uncommitted work, product truth, or live runtime state with no adapter | `bus-check` + `verify-status`; unresolved scope emits `sync.unverified` |
31
- | INV-3 | a completed work package has evidence | every `✅完成` work-package block has one non-empty `**证据**:` line; local paths exist; hashes resolve | external URLs, screenshots not present locally, human statements, or commands whose output was not persisted | Phase 1 `evidence.missing`; external-only evidence also emits `sync.unverified` |
32
- | INV-4 | a cross-boundary change is reflected in the contract | configured provider-path hints, contract file existence/version token, and project adapters when present | a generic script cannot infer all API/schema/public-behavior changes from arbitrary code | pre-commit hint + contract section; absence of a hint is never proof of synchronization |
33
- | INV-5 | a passed Gate is traceable | Gate token parses; referenced decision/evidence path or hash is syntactically valid and locally resolvable | whether the named person actually approved or the evidence is sufficient | Phase 1 `gate.pass_untraceable`; human confirmation remains authoritative |
34
- | INV-6 | a Gate marked `n/a` has a reason | exact `n/a` token and a non-placeholder `理由:` value on the same line | whether the reason is substantively correct for the project | Phase 1 `gate.na_without_reason`; later project-type checks may warn |
35
- | INV-7 | pointers and references resolve safely | scoped Markdown/local references stay inside the root; regular-file targets exist; Git hashes resolve in the meta repo or discovered subrepos | remote-link availability or semantic correctness of an anchor | Phase 1 `ref.broken`; network is not used |
36
- | INV-8 | an incomplete check is exposed | scan truncation, skipped directories, missing tools/adapters, permission errors, and failed child checks set incomplete coverage | anything outside the observed scope | `sync.unverified` or a narrower `*.unverified` code; never a confirmed all-clear |
37
-
38
- INV-2 and INV-4 are intentionally not reducible to a single green boolean. When only part of an invariant is observable, report the confirmed subfact and the remaining `unverified` scope separately.
39
-
40
- ## 3. Machine-readable tokens
41
-
42
- ### 3.1 Gate state lines
43
-
44
- The board contains one list item for each fixed Gate. The canonical form is:
45
-
46
- ```md
47
- - Gate1: pending
48
- - Gate2: n/a | 理由: `本期无 UI 或交互面`
49
- - Gate3: passed | 决策: `pm/decisions.md:42` | 证据: `pm/archive/一期/evidence/gate3.md`
50
- - Gate4: blocked | 理由: `尚无获批发布窗口`
51
- ```
52
-
53
- Parser rules:
54
-
55
- 1. Accept optional leading whitespace, then the exact list marker `- `, `Gate1` through `Gate4`, one colon, and one lowercase state: `pending`, `passed`, `blocked`, or `n/a`.
56
- 2. Each Gate appears exactly once. Missing lines in a legacy board produce `gate.line_missing` at `warning`; duplicates or an unknown state are protocol `error` findings.
57
- 3. `n/a` requires a same-line `理由:` field whose backticked value is non-empty and contains no canonical `<...>` placeholder. Otherwise emit `gate.na_without_reason` at `conflict`.
58
- 4. `passed` should provide at least one `决策:` or `证据:` field. A present `决策:` must be exactly `pm/decisions.md:<positive-line-number>` and that line must exist as a dated decision-table row; naming the ledger file alone is not enough. A missing or invalid trace emits `gate.pass_untraceable` at `warning`.
59
- 5. Gate2 `n/a` is compared only with positive UI signals: a regular `standards/DESIGN.md`, an `index.html`, a known UI package dependency, or a browser-extension UI manifest. A detected signal emits `gate.na_inconsistent` at `warning`; no signal is merely inconclusive and is never proof that the project has no UI.
60
- 6. `blocked` should carry `理由:`. Phase 1 may warn when it is absent, but this is not a strict blocker until a stable finding code is added here.
61
- 7. Natural-language Gate tables may remain for readers, but only these four canonical lines drive machine conclusions.
62
-
63
- ### 3.2 Work-package evidence lines
64
-
65
- Within each `### WP-...` block, the canonical state and evidence fields are:
66
-
67
- ```md
68
- - **状态**: ✅完成
69
- - **证据**: `pm/archive/一期/evidence/report.md` · candidate `deadbee1`
70
- ```
71
-
72
- The parser scopes a block from its `### WP-...` heading to the next heading of the same or higher level. A block containing `**状态**:` and `✅完成` must contain exactly one non-empty `**证据**:` line. Missing, duplicate, placeholder-only, or locally invalid evidence emits `evidence.missing` at `conflict`.
73
-
74
- Machine-verifiable reference forms are:
75
-
76
- - a backticked repository-relative regular-file path, optionally followed by `:<positive-line-number>`;
77
- - a backticked 7–40 character lowercase hexadecimal Git token containing at least one letter and one digit, resolvable in the meta repo or a discovered subrepo;
78
- - an `https://` reference, which is recorded but remains `unverified` unless a project adapter supplies a verified result.
79
-
80
- Paths must not be absolute, contain traversal segments, or resolve through a symlink outside the coordination root. A valid local Gate/work-package evidence path outside `pm/archive/<期>/evidence/` remains traceable but emits `evidence.outside_archive` at `warning`; Git hashes and remote URLs have no local archive-location claim. A command name or prose claim by itself is not machine-verifiable evidence; retain it for humans and emit `sync.unverified` when no local evidence token exists.
81
-
82
- ### 3.3 Scoped reference scan
83
-
84
- Phase 1 checks Markdown links and backticked `.md` paths in `pm/NOW.md`, the current board, and the latest three dated rows of `pm/decisions.md`. It also checks paths/hashes on canonical Gate and evidence lines. The three-row decision window keeps the live synchronization guard from retroactively blocking on historical paths that were intentionally archived; the full repository linker remains a separate source-checkout gate in `tests/check_docs.py`.
85
-
86
- Canonical Gate/evidence paths are repository-root relative and may not contain traversal segments. For scoped legacy prose, the scanner accepts an existing source-file-relative path first, including `../` or `./` segments only when the resolved regular file remains inside the coordination root, then an existing repository-root path (and bare contract filenames under `contracts/`). Absolute paths, paths that resolve outside the root, wildcards/placeholders, overlong table fragments, and prose-only tokens are never treated as valid local evidence. Wildcards/placeholders/prose are ignored rather than mislabeled as broken regular-file references; a real path-shaped token that stays in scope but does not resolve emits `ref.broken`.
87
-
88
- ### 3.4 Optional standards
89
-
90
- `standards/STACK.md`, `CODE.md`, `REVIEW.md`, and UI-only `DESIGN.md` are independent, project-owned optional files. If none exists, `bus-check` emits no standards finding. Every file that does exist must contain exactly once:
91
-
92
- ```md
93
- > **Optional**: ...
94
- > **AI write boundary**: ...
95
- > **Status**: Draft
96
- ```
97
-
98
- Status is exactly `Draft` or `Confirmed`. A Draft is structurally valid but emits `standards.unconfirmed` at `unverified`; it never becomes green by implication. A Confirmed file must contain no `<...>` placeholder.
99
-
100
- Each present file contains at least one stable, unique Rule ID whose prefix matches the filename, for example `STACK-MUST-001`, `CODE-SHOULD-002`, or `DESIGN-MAY-003`. Levels are `MUST / SHOULD / MAY`; the numeric suffix is exactly three digits. Missing metadata, illegal/duplicate/wrong-prefix Rule IDs, a placeholder in a Confirmed file, or a broken repository-local backticked reference emits one `standards.invalid` error for that file. Remote references are not fetched. This structural check never infers machine values from the standards prose; a structurally valid Confirmed STACK enters the separate observation check below.
101
-
102
- ### 3.5 Confirmed STACK observable baseline
103
-
104
- Only a structurally valid `standards/STACK.md` with `Status: Confirmed` enters stack observation. Draft or structurally invalid files retain their existing standards finding and are not compared, so an unconfirmed declaration cannot manufacture a drift conclusion. Absence of STACK remains a legal zero-finding state.
105
-
106
- The Confirmed file contains exactly one v1 comment block:
107
-
108
- ```md
109
- <!-- buildbeat-stack-baseline:v1
110
- nodeConstraint=22
111
- nodeConstraint=>=22 <23
112
- lockfileKind=package-lock.json
113
- dockerFromImage=node:22-alpine
114
- -->
115
- ```
116
-
117
- The three keys are exact and each must occur at least once. Repeating a key declares a set. Values are trimmed, case-sensitive strings of at most 200 characters; exact duplicates collapse. A dimension with no applicable source uses one sole `n/a` value, never `n/a` plus another value. A missing/duplicated/malformed block, an unknown or missing key, or an ambiguous `n/a` set emits `stack.unverified`, not `standards.invalid`: the standards document is structurally readable, but its observable baseline is not. A canonical `<...>` placeholder in any Confirmed standard remains the earlier `standards.invalid` structural error, so the observation step is skipped.
118
-
119
- The scanner recursively observes regular files below the coordination root, pruning `.git`, `.claude`, `.codex`, canonical `.buildbeat`, legacy `.solobaton`, dependency, coverage, build, and generated-output directories. New STACK files use `buildbeat-stack-baseline:v1`; the parser also accepts exactly one legacy `solobaton-stack-baseline:v1` block. The default bound is 200 relevant files/symlinks and can be lowered or raised with positive-integer `BUS_STACK_MAX`. It compares these exact sets:
120
-
121
- | Dimension | Observed source | Normalization |
122
- |---|---|---|
123
- | Node constraints | every `.nvmrc`; every string `package.json` `engines.node` | one trimmed non-empty `.nvmrc` line; JSON value preserved after outer trim |
124
- | lockfile kinds | `package-lock.json`, `npm-shrinkwrap.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lock`, `bun.lockb` | basename only; duplicate kinds collapse |
125
- | Docker FROM images | `Dockerfile` and `Dockerfile.*` FROM instructions | optional `--platform=...` removed; image token preserved; stage aliases ignored |
126
-
127
- Observed and declared non-empty sets must match exactly. A declared `n/a` plus an observed source, or two non-empty sets that differ, emits `stack.drift` at `conflict`. A declared non-`n/a` set with no observable source emits `stack.unverified` rather than drift. Invalid JSON, a non-string Node engine, an empty/multi-line `.nvmrc`, an unreadable file, an unresolved variable in a Docker image token, missing Python needed for JSON parsing, an over-limit scan, a find/permission error, a relevant file symlink, or an unpruned directory symlink also leaves the affected scope `stack.unverified`. A definite mismatch and an incomplete remaining scope may emit both codes.
128
-
129
- The check is report-only: it never edits STACK, version files, package manifests, lockfiles, or Dockerfiles. Findings name dimensions but do not echo raw source/config values into JSON. Matching only confirms these three observed dimensions inside the scanned regular-file scope; it says nothing about frameworks, databases, deployment health, CI, licensing, remote repositories not present below the root, or semantic compatibility.
130
-
131
- ### 3.6 Explicit multi-repository version joins
132
-
133
- Multi-repository drift is checked only through one explicit project-owned map in `contracts/PROTOCOL.md`; directory names, architecture prose, package metadata, and live output are never guessed into version equality:
134
-
135
- ```md
136
- <!-- buildbeat-multirepo-map:v1
137
- repo=service-web|contract=contracts/PROTOCOL.md|deployment=web
138
- repo=service-api|contract=contracts/api.md|deployment=n/a
139
- -->
140
- ```
141
-
142
- Each non-empty row has exactly `repo=<path>|contract=<path>|deployment=<app-or-n/a>` with no surrounding whitespace. `repo` is a safe coordination-root-relative path expected to match the existing `SUBREPOS` discovery (independent Git repositories one or two levels below the root). `contract` is a safe `contracts/**/*.md` path containing exactly one `契约快照对应版本` line with one backticked release token. `deployment` is either an app key in the same-directory `bus-baseline.json` used by `drift-check.sh`, or the exact value `n/a` when that repository has no deployment source.
143
-
144
- The repository version comes only from the first non-`Unreleased` H2 in `<repo>/CHANGELOG.md`. Accepted headings are Keep-a-Changelog forms such as `## [1.2.3] - 2026-08-25` or `## v1.2`; accepted source tokens have two or three numeric components plus optional SemVer prerelease/build suffixes. One leading `v`/`V` is ignored for equality. Free-form release prose, package versions, Git tags, commit hashes, and a later convenient heading are not substituted for an invalid head source.
145
-
146
- For each mapped repository, every successfully observed source is compared pairwise. A definite mismatch emits `sync.multirepo_drift` at `conflict` and identifies the repository, `CHANGELOG.md`, mapped contract file, and `bus-baseline.json#apps.<app>.imageTag` fact source. A missing/out-of-root repository or source, invalid/duplicate/empty map, unparseable version, missing `jq`, missing app, missing deployment baseline, mapped repository outside the bounded discovery, or discovered repository absent from the map emits `sync.unverified`; a present source skipped for symlink or permission safety emits `sync.scan_truncated` under §3.8. A definite mismatch and an incomplete third source may emit both.
147
-
148
- No discovered repositories plus no map is a legal zero-finding state. A present map is the expected inventory, so a mapped-but-undiscovered repository remains explicitly unverified even when no nested repository was found. `deployment=n/a` compares CHANGELOG and contract only. This check is read-only and compares a local deployment baseline; it does not query production or prove that the baseline is current. `live-status.sh` and `drift-check.sh` retain those separate authority boundaries.
149
-
150
- ### 3.7 Optional ADRs
151
-
152
- ADR files are named `pm/adr/ADR-NNNN-*.md`. The directory and all ADR files are optional; absence emits no ADR finding. Every present ADR contains exactly one canonical status line:
153
-
154
- ```md
155
- - Status: Proposed
156
- ```
157
-
158
- The only legal values are `Proposed`, `Accepted`, `Rejected`, and `Superseded`. Missing, duplicated, or unknown status emits `adr.status_invalid` at `error`.
159
-
160
- A Superseded ADR must contain exactly one root-relative target such as:
161
-
162
- ```md
163
- - Superseded by: `pm/adr/ADR-0002-new-choice.md`
164
- ```
165
-
166
- The target must exist, must itself have a legal status, and the chain must terminate without self-reference or cycles. Otherwise emit `adr.superseded_broken` at `conflict`. The script checks link integrity only; it cannot prove the architectural decision is correct or genuinely approved.
167
-
168
- ### 3.8 Mechanical scan boundaries
169
-
170
- `sync.scan_truncated` is the common non-blocking result when a relevant local source is present or a bounded scan started, but the checker deliberately did not inspect the entire scope. Its message contains one stable reason token and its `path` names the skipped repository-relative source when known:
171
-
172
- | Reason | Condition | Operator response |
173
- |---|---|---|
174
- | `reason=limit` | scoped-reference or STACK observation count exceeds `BUS_REF_MAX` / `BUS_STACK_MAX` | inspect what was omitted; raise the relevant limit only when the larger scope is intentional, then rerun |
175
- | `reason=symlink` | a relevant coordination, evidence, standards, ADR, STACK, contract, repository, or deployment-baseline path traverses an in-root symbolic link | independently inspect the target or materialize the required fact as an in-root regular file; the checker does not follow it |
176
- | `reason=permission` | the current process cannot read a relevant file, search a mapped repository directory, or complete filesystem traversal | restore only the minimum read/search access needed for the check, or provide a readable evidence artifact, then rerun |
177
-
178
- This finding means “coverage stopped here,” not “the source is missing” and not “the source is valid.” A completed-work evidence reference that resolves only through a symlink or unreadable file therefore remains unverified and does not additionally become `evidence.missing`; an absent or unsafe path still follows its narrower missing/broken rule. Domain findings may coexist: for example, a Confirmed STACK scan can emit both `sync.scan_truncated` for the exact mechanical boundary and `stack.unverified` for the affected comparison dimensions.
179
-
180
- Intentional exclusions such as `.git`, `node_modules`, build output, and vendor directories do not each produce findings because those trees are outside the declared observation scope. Raw OS error text and temporary absolute paths are not copied into JSON; an unlocatable traversal failure uses `path="."`. Any `sync.scan_truncated` sets `coverage.complete=false` but does not block strict mode. It must be reported in handoff evidence and cannot be converted into an all-clear by exit 0.
181
-
182
- ## 4. Result levels
183
-
184
- Every finding has exactly one level:
185
-
186
- | Level | Meaning | Strict by default |
187
- |---|---|---|
188
- | `confirmed` | a positive or neutral fact was directly observed | no |
189
- | `warning` | a risk or traceability weakness exists, but no invariant conflict is established | no |
190
- | `unverified` | the tool cannot reliably decide within the observed scope | no; must stay visible |
191
- | `conflict` | a project declaration contradicts an observed fact or omits support required by an invariant | yes |
192
- | `error` | protocol structure is malformed or the requested check cannot produce a trustworthy report | yes |
193
-
194
- Levels are not a cosmetic mapping from legacy emoji or from `error/warning/info`. The implementation must construct the semantic finding first, then render human or JSON output from that same finding.
195
-
196
- `confirmed` never means “the whole project is healthy.” If `coverage.complete` is false, the report cannot print or encode an unqualified all-clear.
197
-
198
- ## 5. Finding-code registry
199
-
200
- Namespaces are reserved as follows:
201
-
202
- | Namespace | Scope |
203
- |---|---|
204
- | `sync.` | NOW/status/L3/scan/remote and general coordination state |
205
- | `gate.` | four-Gate tokens and traceability |
206
- | `evidence.` | work-package evidence requirements |
207
- | `contract.` | cross-boundary contract synchronization |
208
- | `ref.` | local path, Markdown, and Git reference integrity |
209
- | `stack.` | optional `STACK.md` observation and drift |
210
- | `standards.` | optional standards structure/rules |
211
- | `adr.` | optional ADR structure and supersession links |
212
-
213
- The initial registry is:
214
-
215
- | Code | Level | Condition | Phase |
216
- |---|---|---|---|
217
- | `sync.now_bloated` | `conflict` | NOW/status live coordination layer exceeds the configured rot limits | legacy behavior; code in Phase 1 |
218
- | `sync.ghost_hash` | `conflict` | a canonical status hash cannot resolve in any known repo | legacy behavior; code in Phase 1 |
219
- | `sync.production_drift` | `conflict` | configured drift adapter confirms a changed production fact | legacy behavior; code in Phase 1 |
220
- | `sync.multirepo_drift` | `conflict` | explicitly mapped CHANGELOG, contract, and/or deployment-baseline versions disagree | Phase 3 / WP3.3 |
221
- | `sync.l3_stale` | `warning` | configured suite evidence is absent, unreadable, or older than `BUS_L3_MAX_AGE_DAYS` (default `7`) | Phase 1 |
222
- | `sync.l3_unconfigured` | `unverified` | no real L3 suite is configured | Phase 1 |
223
- | `sync.scan_truncated` | `unverified` | limit, permission, or symlink boundary leaves relevant declared scope unchecked | Phase 1; consolidated path/reason handling in Phase 3 / WP3.4 |
224
- | `sync.unverified` | `unverified` | a material check boundary has no narrower registered code | Phase 1 |
225
- | `gate.line_missing` | `warning` | a legacy board lacks one or more canonical Gate lines | Phase 1 |
226
- | `gate.invalid` | `error` | a Gate state line is duplicated, malformed, or uses an unknown state | Phase 1 |
227
- | `gate.na_without_reason` | `conflict` | `n/a` lacks a valid same-line reason | Phase 1 |
228
- | `gate.na_inconsistent` | `warning` | Gate2 is `n/a` while a positive UI signal is present | Phase 3 / WP3.2 |
229
- | `gate.pass_untraceable` | `warning` | `passed` lacks a resolvable decision/evidence reference | Phase 1 |
230
- | `evidence.missing` | `conflict` | a completed work package lacks one valid canonical evidence line | Phase 1 |
231
- | `evidence.outside_archive` | `warning` | a valid local Gate/work-package evidence path is outside `pm/archive/<期>/evidence/` | Phase 3 / WP3.2 |
232
- | `ref.broken` | `conflict` | a scoped local path/hash reference is syntactically unsafe or does not resolve | Phase 1 |
233
- | `standards.invalid` | `error` | a present optional standard has invalid metadata, Rule IDs, confirmed placeholders, or local references | Phase 2-A |
234
- | `standards.unconfirmed` | `unverified` | a present optional standard is structurally valid but remains Draft | Phase 2-A |
235
- | `stack.drift` | `conflict` | a Confirmed v1 STACK baseline contradicts an observed Node, lockfile, or Docker FROM set | Phase 2-B / WP2.5 |
236
- | `stack.unverified` | `unverified` | a Confirmed STACK baseline or relevant observation scope cannot be compared reliably | Phase 2-B / WP2.5 |
237
- | `adr.status_invalid` | `error` | a present ADR lacks exactly one legal canonical Status | Phase 2-A |
238
- | `adr.superseded_broken` | `conflict` | a Superseded ADR points to a missing/invalid ADR or creates a self-reference/cycle | Phase 2-A |
239
-
240
- New codes require this document, positive/negative fixtures, human rendering, JSON rendering, and strict behavior to change together. A code must not silently change level between releases; that is a user-visible compatibility change.
241
-
242
- ## 6. JSON report schema
243
-
244
- `bus-check --format=json` emits one JSON document to stdout and no human dashboard text. Diagnostics that prevent a report go to stderr.
245
-
246
- ```json
247
- {
248
- "schemaVersion": 1,
249
- "command": "bus-check",
250
- "ok": false,
251
- "target": ".",
252
- "findings": [
253
- {
254
- "code": "gate.na_without_reason",
255
- "level": "conflict",
256
- "message": "Gate2 is n/a without a non-placeholder reason.",
257
- "path": "pm/一期-看板.md"
258
- }
259
- ],
260
- "summary": {
261
- "confirmed": 0,
262
- "warning": 0,
263
- "unverified": 0,
264
- "conflict": 1,
265
- "error": 0
266
- },
267
- "coverage": {
268
- "complete": true,
269
- "reasons": []
270
- },
271
- "strict": {
272
- "enabled": true,
273
- "blocked": true
274
- }
275
- }
276
- ```
277
-
278
- Schema rules:
279
-
280
- 1. `target` and `path` are normalized repository-relative display paths; fixture output must not contain temporary absolute paths.
281
- 2. Findings are ordered by the registry/check order, then path, so repeated runs on unchanged facts are byte-stable apart from messages whose documented fact changed.
282
- 3. `ok` is true only when there is no `conflict` or `error`. Warnings and explicit unverified coverage do not change `ok`, but remain in the report.
283
- 4. `coverage.complete` is false when any relevant scope was truncated, skipped, unavailable, or failed. `reasons` contains stable finding codes, not secrets or raw command output.
284
- 5. Counts equal the findings array exactly. Unknown levels/codes are schema errors in fixtures and consumers.
285
- 6. JSON never includes credential values, environment values, arbitrary source contents, private messages, or live configuration values.
286
-
287
- Exit behavior:
288
-
289
- | Invocation | Exit 0 | Exit 1 | Exit 2 |
290
- |---|---|---|---|
291
- | default human or `--format=json` | a report was produced, even with findings | not used for findings | invalid arguments or failure to produce a trustworthy report |
292
- | `--strict` with either format | no `conflict`/`error` | at least one `conflict`/`error` | invalid arguments or failure to produce a trustworthy report |
293
-
294
- `warning` and `unverified` never block strict by implication. Promoting either level into the strict set requires an explicit code-level change in this specification and the changelog.
295
-
296
- ## 7. Current versus planned implementation
297
-
298
- The current worktree candidate implements Phase 1, the Phase 2-A structural checks, WP2.5 STACK observation, WP3.2 Gate/evidence joins, WP3.3 explicit multi-repository version joins, and WP3.4 consolidated mechanical-boundary reporting from one finding collection: human rendering, schema 1 JSON, strict blocking, canonical Gate/evidence parsing, scoped reference scanning, L3 machine findings, explicit incomplete coverage, the three legacy strict checks, optional standards metadata/Rule-ID validation, exact Node/lockfile/Docker baseline comparison, ADR status/supersession integrity, mapped CHANGELOG/contract/deployment-baseline comparison, and precise `limit`/`symlink`/`permission` reasons. Default human and JSON report modes still return exit 0 after producing a trustworthy report; `--strict` returns exit 1 only for `conflict` or `error`.
299
-
300
- Fixtures now execute both renderings. They compare finding codes, registered levels, counts, relative paths, coverage reasons, and strict status; retained human text assertions protect operator-facing compatibility. Matching, definite-conflict, and missing-observation STACK fixtures lock the WP2.5 result boundary. Runtime-generated nested Git repositories cover WP3.3 matching, definite drift, spaces in repository paths, and unmapped coverage without checking nested `.git` metadata into this source repository. WP3.4 additionally covers reference-limit, symlinked evidence, and permission-denied evidence paths, including non-blocking strict behavior, incomplete coverage, and exact relative paths; the Shell suite currently has 221 assertions. WP1.6 has also completed the earlier example, active multi-repo projection, and real single-repo code-tree projection documented in [`PHASE1-PILOT-2026-08-24.md`](PHASE1-PILOT-2026-08-24.md). Current Phase 3 evidence remains disposable local fixtures, not a refreshed real-project pilot. Without separate release authorization and installed-project migration evidence, this remains an Unreleased candidate rather than released behavior.
301
-
302
- WP2.5 does not extend beyond its three explicit regular-file dimensions. WP3.2–WP3.4 add separately mapped checks and honest boundary reporting, but a green STACK or multi-repository comparison still cannot be extrapolated into deployment health, semantic contract correctness, human approval, or any path outside the declared observation scope.
303
-
304
- ## 8. Three retained compatibility files
305
-
306
- These files stay for distinct consumers and do not create a second semantic authority:
307
-
308
- | File | Purpose | Schema 2 ownership |
309
- |---|---|---|
310
- | `CLAUDE.md` | thin compatibility pointer to `AGENTS.md` | `replace-if-unmodified` |
311
- | `指挥台.md` | one-page human operator card | `replace-if-unmodified` |
312
- | `.claude/agents/reviewer.md` | tool-specific read-only reviewer increment | `replace-if-unmodified` |
313
-
314
- Project facts remain in AGENTS/NOW/board/contracts/status/evidence. These three files may point to those facts but must not copy them into competing SSOTs.
315
-
316
- ## Appendix: doctor comparison
317
-
318
- `doctor` keeps `error / warning / info` and `ok = no error`; it does not migrate to the five bus levels in Phase 1.
319
-
320
- | Doctor level | Closest display concept | Caveat |
321
- |---|---|---|
322
- | `error` | `error` | only within scaffold/install inspection |
323
- | `warning` | `warning` | may include incomplete setup such as pending placeholders |
324
- | `info` | `confirmed` when it reports an observed fact | not every info message is a project-wide confirmation |
325
-
326
- No automated Gate or release decision may be made by mechanically converting doctor levels into bus-check levels.
package/docs/CLI.md DELETED
@@ -1,245 +0,0 @@
1
- # BuildBeat CLI lifecycle contract
2
-
3
- Status: **BuildBeat `2.0.2` scoped distribution (stable `latest`; the v1 lifecycle CLI `buildbeat` is unchanged, the v2 delivery runtime `buildbeat-v2` ships alongside it — see `docs/v2/guide/`)** · previous independently verified stable `2.0.1` · canonical package `@haiyangbg/buildbeat` · canonical executable `buildbeat` · legacy package `solobaton@1.16.3` remains the independently verified read-only v0 · Node.js 20+ · zero third-party runtime dependencies. The 1.21 release keeps the verified 1.20 lifecycle command and safety boundaries, and adds the standard domain-response contract to the Skill and managed scaffold. The genuine lifecycle version-increment pilot remains archived in [`PHASE4-V1.20-PILOT-2026-08-25.md`](PHASE4-V1.20-PILOT-2026-08-25.md); exact 1.21 registry and supply-chain evidence is archived in [`V1.21-RELEASE-EVIDENCE-2026-08-25.md`](V1.21-RELEASE-EVIDENCE-2026-08-25.md).
4
-
5
- The CLI does not replace `SKILL.md`. The Skill owns code-aware reasoning, minimal questions, project semantics, and human Gates. The CLI owns deterministic inspection, scaffold mechanics, manifest/hash bookkeeping, and bounded mechanical upgrade in the current scoped distribution. Synchronous file-bus checks remain authoritative in the project-local scripts specified by [`CHECKS.md`](CHECKS.md).
6
-
7
- This document is the contract for the **v1 lifecycle CLI `buildbeat`** only. The v2 delivery runtime `buildbeat-v2` in the same package has its own surface (below) documented in [`v2/guide/`](v2/guide/README.md). The bilingual [`CAPABILITY-MATRIX.md`](CAPABILITY-MATRIX.md) is the compact authority for what Skill-only, the legacy npm v0, the v1 lifecycle CLI, and the v2 runtime can each do. v1 command details and safety semantics remain authoritative in this document.
8
-
9
- ## Two executables, two jobs
10
-
11
- | Executable | Job | Does not do |
12
- |---|---|---|
13
- | `buildbeat` | Inspect (`doctor`), scaffold (`init` / `adopt`), and mechanically upgrade (`upgrade`) the **v1 file-bus skeleton** (`pm/`, `contracts/`, `scripts/`); `version` | Does not create `delivery/work/`, run-configs, or Runs; `buildbeat doctor` does not check a v2 run-config (that is `buildbeat-v2 doctor --config`); `buildbeat upgrade` does not migrate Work state |
14
- | `buildbeat-v2` | Run and query the **v2 delivery loop**: `accept`, `start`, `resume`, `status`, `inbox`, `overview`, `approve`, `reject`, `findings`, `doctor`, `preflight`, `events`, `replay`, `metrics`, `stop`, `gc`, `watch`, `observe` — run `buildbeat-v2` with no arguments for the usage text | Does not scaffold the v1 file bus; never merges, pushes, deploys, or publishes (invariant 20) |
15
-
16
- A project may use either or both: pure v2 projects have no `pm/NOW.md` and never run `bus-check`; v1 projects that adopt v2 freeze the file bus read-only ([`v2/guide/08-migration-v1.md`](v2/guide/08-migration-v1.md)).
17
-
18
- ## Command boundary and phased availability
19
-
20
- The canonical scoped-package commands are:
21
-
22
- ```bash
23
- npm view @haiyangbg/buildbeat@latest version
24
- npx --yes --package=@haiyangbg/buildbeat@latest buildbeat doctor /path/to/project
25
- npx --yes --package=@haiyangbg/buildbeat@latest buildbeat init /path/to/project --dry-run
26
- npx --yes --package=@haiyangbg/buildbeat@latest buildbeat adopt /path/to/project --dry-run
27
- npx --yes --package=@haiyangbg/buildbeat@latest buildbeat upgrade /path/to/project --dry-run
28
- ```
29
-
30
- Record the version returned by `npm view` and substitute that exact version for `@latest` when the invocation must be reproducible. Before registry publication is independently verified, the same lifecycle can only be evaluated from a locked repository checkout via `node bin/buildbeat.js`; `node bin/solobaton.js` remains a compatibility alias. The old `solobaton@latest` package remains frozen on read-only v0 and is not the write-capable distribution.
31
-
32
- - `doctor` reads an existing project and reports installation state, layout, version marker, required files, unresolved canonical placeholders, hooks, and capability dependencies.
33
- - `init/adopt --dry-run` inspect a target and emit the complete default/compact plan with zero writes.
34
- - `init/adopt` without `--dry-run` apply only after all blockers are absent and interactive confirmation or explicit `--yes` is present.
35
- - `upgrade --dry-run` plans a schema-2-only mechanical version transition; apply is fail-closed on unresolved conflict.
36
- - `--json` returns a versioned JSON document for agents and CI.
37
- - `diff` and `uninstall` remain reserved and disabled. Workflow commands remain in Skill/project scripts.
38
-
39
- The locked-checkout equivalent is:
40
-
41
- ```bash
42
- node bin/buildbeat.js init /path/to/project --dry-run --json
43
- node bin/buildbeat.js init /path/to/project # plan + interactive confirmation
44
- node bin/buildbeat.js adopt /path/to/project --yes # non-interactive only after plan approval
45
- node bin/buildbeat.js upgrade /path/to/project --dry-run --json
46
- node bin/buildbeat.js upgrade /path/to/project # apply only when the complete plan is ready
47
- ```
48
-
49
- `--yes` skips only the `init/adopt` prompt; `--dry-run --yes` is invalid. If no interactive terminal is available, an apply call without `--yes` returns `confirmation_required` and performs zero writes. `upgrade` has no `--yes`; naming the write command is the explicit apply request, and `--force`/`--major` acknowledge only their documented narrow boundaries. JSON apply keeps the machine result on stdout and prints the pre-write human plan on stderr.
50
-
51
- Wave 1 has bounded BuildBeat real-directory, Git, hook, evidence-commit, and Gate3 closure in [`PHASE2-BUILDBEAT-PILOT-2026-08-25.md`](PHASE2-BUILDBEAT-PILOT-2026-08-25.md). The `v1.16 → v1.20` schema 2 upgrade and real multi-repository refresh are archived in [`PHASE4-V1.20-PILOT-2026-08-25.md`](PHASE4-V1.20-PILOT-2026-08-25.md). None of these project tests substitute for npm artifact verification.
52
-
53
- The target command whitelist is intentionally small:
54
-
55
- | Milestone | Enabled main commands | Boundary |
56
- |---|---|---|
57
- | legacy `solobaton@1.16.3` | `doctor`, `init --dry-run`, `adopt --dry-run`, `version` | independently verified read-only v0; deprecated distribution ID after scoped migration |
58
- | BuildBeat `1.21.0` | `doctor`, `init`, `adopt`, `upgrade`, `version` | unchanged Phase 0–3 command set; writes remain bounded by the transaction and human-Gate contracts below |
59
- | BuildBeat `2.0.0` | same `buildbeat` command set; second executable `buildbeat-v2` (run / overview / inbox / status / approve / observe / gc …) | `buildbeat` lifecycle and safety boundaries unchanged; `buildbeat-v2` is the v2 runtime documented in [`v2/guide/README.md`](v2/guide/README.md) and never merges, pushes, deploys or publishes (invariant 20); the managed scaffold version stays `v1.21` (templates unchanged), so `upgrade` reports up-to-date for 1.21 scaffolds; the manifest `cliVersion` is a record, not an upgrade trigger |
60
- | BuildBeat `2.0.1` | same command sets | patch: run-config `inheritEnv` / `env:` now reach the Shell Adapter through the CLI loader; `templates/v2/` gains `run-config.example.yaml`, `envelope/worker.sh` and prompts; documentation aligned with the parser (worker envelope `severity` + `summary`, P0/P1 block) and with the stable channel; no v1 lifecycle or scaffold change (`v1.21`) |
61
- | BuildBeat `2.0.2` | same command sets | patch: the npm tarball no longer carries historical documents (release evidence, iteration logs, pilots, plans); current docs still ship; `tests/pack-firstrun.test.sh` guards both; no runtime, v1 lifecycle or scaffold change (`v1.21`) |
62
-
63
- `help`, `--help`, and `--version` are meta entry points. `diff` and `uninstall` stay reserved and return `command_not_available`; `gate`, `adr`, `standards`, `check`, and other workflow commands are outside the approved CLI scope. HELP text and regression tests must lock this boundary.
64
-
65
- Exit codes:
66
-
67
- | Code | Meaning |
68
- |---:|---|
69
- | `0` | The inspection/plan completed and has no blocker-level finding |
70
- | `1` | The command completed, but the project has an error or lifecycle blocker |
71
- | `2` | Usage/confirmation error, or a reserved command that the running version does not support |
72
-
73
- ## CLI package lifecycle is not project lifecycle
74
-
75
- The public npm package gives the executable a conventional, reversible distribution path:
76
-
77
- | Intent | Command | Project effect |
78
- |---|---|---|
79
- | Current one-off run | `npx --yes --package=@haiyangbg/buildbeat@latest buildbeat doctor <project>` | Runs the registry version without a persistent global installation |
80
- | Resolve an exact version | `npm view @haiyangbg/buildbeat@latest version` | Records the version to substitute for `@latest` in a reproducible invocation |
81
- | Install or update the global CLI | `npm install --global @haiyangbg/buildbeat@latest` | Replaces only the globally installed package and executables |
82
- | Remove the global CLI | `npm uninstall --global @haiyangbg/buildbeat` | Removes only the global package and executables |
83
-
84
- Package-manager operations never create, update, or remove a project's scaffold. `buildbeat upgrade` is a separate schema-2-only project lifecycle; `buildbeat uninstall` and its legacy alias remain disabled. Removing an `npx` cache is also outside BuildBeat's project lifecycle.
85
-
86
- ## Skill and CLI responsibilities
87
-
88
- | Layer | Owns | Must not claim |
89
- |---|---|---|
90
- | `SKILL.md` | code-aware inspection, minimal human questions, project-specific reasoning, Gate semantics | deterministic installation state or safe file ownership by itself |
91
- | CLI | bounded filesystem inspection, plans, manifest/hash handling, repeatable lifecycle mechanics | product judgment, contract discovery, Agent runtime, or automatic Gate approval |
92
- | Scaffold files | project facts, decisions, contracts, status, evidence | that template defaults are current project facts |
93
- | Shell guardrails | deterministic local checks | server-side enforcement or live production truth without project adapters |
94
-
95
- An AI-assisted bootstrap should consume CLI JSON as evidence, inspect the code for facts the CLI cannot infer, ask only the remaining simple questions, and obtain the existing one-screen confirmation before any write-capable release is allowed to apply a plan.
96
-
97
- ## File ownership policies
98
-
99
- The manifest records the installed baseline hash of every lifecycle-managed path. “Managed” never means “overwrite regardless of local edits.” Policy validity is schema-specific:
100
-
101
- - schema 1 remains readable and may contain the historical `three-way-only` value;
102
- - schema 2 may contain only `replace-if-unmodified`, `project-owned`, and `merge-only`;
103
- - new plans and writes must never emit `three-way-only`.
104
-
105
- | Schema 2 policy | Examples | Write/upgrade rule |
106
- |---|---|---|
107
- | `replace-if-unmodified` | `AGENTS.md`, `BUILDBEAT.md`, operator card, `CLAUDE.md` pointer, reviewer, unconfigured managed scripts | Replace only when the current hash still equals the installed baseline; otherwise report a conflict. `--force` may replace this class after an explicit warning. |
108
- | `project-owned` | architecture, contract, boards, decisions, status, configured `verify-status.sh` | Initial scaffold may create a non-colliding template. Upgrade never creates, replaces, or deletes it; provide migration instructions or a patch candidate instead. `--force` does not override this rule. |
109
- | `merge-only` | the owned `.gitignore` fragment | Modify only the uniquely marked fragment. Never replace the host file; missing, duplicated, or locally changed markers are conflicts. |
110
-
111
- The `.gitignore` host file is represented by `integrations.gitignore`, not duplicated in `files`. Hooks remain outside CLI writes. The compact layout maps the five scripts, operator card, and version marker into `pm/`; root `AGENTS.md`, `CLAUDE.md`, `.claude/agents/`, architecture, contracts, and coordination records keep their established locations.
112
-
113
- ## Manifest contract
114
-
115
- The canonical manifest path is `.buildbeat/manifest.json`. Doctor continues to read the legacy `.solobaton/manifest.json` path, but new scaffolds never create it. Schema 1 is the read-only compatibility shape already recognized by v0:
116
-
117
- ```json
118
- {
119
- "schemaVersion": 1,
120
- "scaffoldVersion": "v1.16",
121
- "cliVersion": "1.16.3",
122
- "layout": "default",
123
- "installedAt": "2026-08-22T00:00:00.000Z",
124
- "files": {
125
- "scripts/bus-check.sh": {
126
- "policy": "replace-if-unmodified",
127
- "baselineSha256": "<64 lowercase hex characters>"
128
- }
129
- },
130
- "integrations": {
131
- "gitignore": null,
132
- "hooks": null
133
- }
134
- }
135
- ```
136
-
137
- Schema 2 is the first write-capable shape targeted by Wave 1:
138
-
139
- ```json
140
- {
141
- "schemaVersion": 2,
142
- "scaffoldVersion": "v1.21",
143
- "cliVersion": "2.0.2",
144
- "layout": "default",
145
- "installedAt": "2026-08-24T00:00:00.000Z",
146
- "files": {
147
- "AGENTS.md": {
148
- "policy": "replace-if-unmodified",
149
- "baselineSha256": "<64 lowercase hex characters>"
150
- },
151
- "contracts/PROTOCOL.md": {
152
- "policy": "project-owned",
153
- "baselineSha256": "<64 lowercase hex characters>"
154
- }
155
- },
156
- "integrations": {
157
- "gitignore": {
158
- "path": ".gitignore",
159
- "beginMarker": "# >>> buildbeat managed >>>",
160
- "endMarker": "# <<< buildbeat managed <<<",
161
- "baselineSha256": "<SHA-256 of the exact owned fragment bytes, including markers>"
162
- },
163
- "hooks": null
164
- }
165
- }
166
- ```
167
-
168
- Rules common to both schemas:
169
-
170
- 1. Only the documented top-level and nested fields are accepted. `installedAt` is a canonical UTC timestamp with milliseconds.
171
- 2. Paths are normalized POSIX repository-relative paths, cannot be absolute, cannot contain `.`/`..` traversal segments, cannot traverse symlinks, and must stay inside the target root.
172
- 3. Every file record contains exactly `policy` and a 64-character lowercase hexadecimal `baselineSha256`. The hash describes the exact bytes initially installed or last mechanically upgraded by the CLI, not the current bytes after project edits.
173
- 4. `files` records actual scaffold paths. It excludes both `.buildbeat/manifest.json` and the legacy `.solobaton/manifest.json`, excludes the source-only `gitignore.template`, and excludes the `.gitignore` host file represented under `integrations`.
174
- 5. No credential, environment value, file content, inferred private architecture fact, account identifier, or remote state enters the manifest.
175
- 6. Unknown schema versions and malformed known schemas fail closed. Validation uses a schema-specific policy set: schema 1 continues accepting its historical policies, while schema 2 rejects `three-way-only`.
176
- 7. A missing manifest means a legacy/unmanaged install. `doctor` may inspect it, but `upgrade` must not guess ownership or synthesize a baseline without an explicit adoption decision.
177
- 8. The manifest itself does not make a project healthy; unresolved placeholders, checks, evidence, and human Gates remain separate.
178
-
179
- Schema 2 integration rules:
180
-
181
- 1. `integrations` contains exactly `gitignore` and `hooks`; `hooks` is `null` because hook installation stays a documented Skill/manual step.
182
- 2. `gitignore` is either `null` when no fragment was written, or the exact four-field object shown above. Marker strings are fixed constants, distinct, non-empty, and must occur exactly once in the host file before an automated fragment update.
183
- 3. `baselineSha256` covers the exact UTF-8 fragment bytes from the first byte of `beginMarker` through the final line ending after `endMarker`. A changed fragment is a conflict; `--force` may replace only that fragment, never the rest of `.gitignore`.
184
- 4. The manifest is written last. If it is absent after an interrupted write, later commands classify the target as partial/mixed and refuse to infer ownership.
185
-
186
- ## Scaffold write transaction (Wave 1 / BuildBeat 1.20)
187
-
188
- A write-capable `init` or `adopt` must:
189
-
190
- 1. inspect without following symlinks and show the complete plan before any mutation; `--dry-run` performs this path and never asks for confirmation;
191
- 2. refuse mixed/partial/already-installed targets, unsafe paths, and every destination collision. Wave 1 has no project-file `--force`;
192
- 3. when the target root itself contains `.git`, require `git status --porcelain=v1 --untracked-files=all` to be empty. A parent repository does not substitute for a target-root repository;
193
- 4. copy the bundled template tree for the selected layout while excluding `standards/` and `pm/adr/` by the shared optional-prefix constant;
194
- 5. render only deterministic values—project name, the invocation's local calendar date, scaffold version, and layout (including compact-layout script references). Preserve every remaining canonical placeholder, return its path/token in `pendingPlaceholders`, and tell the caller to continue with `SKILL.md` §8 or §8.5;
195
- 6. print the final plan and require interactive confirmation. `--yes` skips only this prompt; it does not bypass dirty-worktree, collision, ownership, or path checks;
196
- 7. write each new file through a temporary sibling on the target filesystem followed by an atomic rename. Record every file and directory created by this invocation;
197
- 8. merge only the fixed, uniquely marked BuildBeat fragment into `.gitignore`. A pre-existing BuildBeat or legacy Solobaton marker without matching ownership metadata blocks the write. Do not install or change hooks;
198
- 9. write schema 2 manifest last, after all scaffold files and the integration fragment are durable; and
199
- 10. return the written paths, deterministic replacements, pending placeholders, manifest path, and next Skill/manual action. `doctor` may be run immediately, but pending placeholders are expected warnings until the AI rendering step finishes. A newly created target with no root `.git` must also keep the honest `git.not_initialized` finding until the Skill/manual path initializes Git; the CLI never does that itself. `bus-check` becomes a completion check only after Git and project facts are ready.
200
-
201
- The write-capable JSON envelope increments `OUTPUT_SCHEMA_VERSION` from 1 to 2 and adds `writesPerformed`, `writtenPaths`, `manifestPath`, `nextAction`, `renderedPlaceholders`, and `pendingPlaceholders`. Each deterministic replacement is `{path, token, value}`; each unresolved entry is `{path, tokens}`. Dry-run returns the same replacement/pending inventory with `writesPerformed: false`, while successful apply returns the actual written paths with `writesPerformed: true`. The changelog identifies this output-schema change. Existing v0 doctor/plan fields keep their meaning.
202
-
203
- In-process rollback is mandatory. On any failure before manifest completion, remove only the files and empty directories created by this invocation and restore an existing `.gitignore` to its exact pre-write bytes through an atomic replacement. Never delete or rewrite a path that predated the invocation. There is no persistent recovery journal: an abrupt process kill may require the user to inspect or restore the already-clean Git worktree, and the next CLI run must classify any manifest-less partial state as blocked rather than guessing.
204
-
205
- No command may initialize Git, add a remote, commit, push, install a package globally, or cross a human Gate without separate explicit authority.
206
-
207
- ## Mechanical upgrade and manual removal
208
-
209
- BuildBeat 1.20 implements `buildbeat upgrade [path] [--dry-run] [--json] [--force] [--major]`; the old executable remains only a compatibility alias. It is available only for one canonical BuildBeat installation, a valid canonical schema 2 manifest, and a clean target-root Git worktree. Schema 1, a missing/invalid/legacy manifest, a legacy marker namespace, or a mixed/partial installation is blocked; follow the [v1.16 legacy migration guide](LEGACY-V1.16-MIGRATION.md) for the manual-maintenance or explicitly approved re-baselining path instead of inferring ownership.
210
-
211
- The JSON plan contains only bounded metadata: version-gate state, paths, policies, actions, SHA-256 values, blockers, warnings, and conflict eligibility/resolution. It never returns template or project file contents. Any unresolved conflict keeps apply mode at zero writes. A ready apply rechecks every expected hash or absence before mutation, prints the plan, updates the manifest last, and performs in-process byte/mode rollback on failure.
212
-
213
- Version gates compare `manifest.scaffoldVersion` with the bundled `SCAFFOLD_VERSION`:
214
-
215
- - equal version → no template upgrade is needed;
216
- - installed version newer than the bundle → block; downgrade is not supported;
217
- - newer bundle in the same major → eligible for mechanical upgrade;
218
- - newer bundle in another major → block unless `--major` explicitly acknowledges the major transition.
219
-
220
- The planner evaluates the manifest baseline, current project bytes, and new bundled template without performing a three-way merge:
221
-
222
- | State | Default action |
223
- |---|---|
224
- | `replace-if-unmodified`, current hash equals baseline, new template changed | atomically replace and record the new baseline hash |
225
- | `replace-if-unmodified`, current file changed or is missing | conflict; leave it untouched |
226
- | `project-owned`, whether existing or newly introduced upstream | never write, replace, or delete; emit a migration note or patch candidate |
227
- | new `replace-if-unmodified` path with no collision | create it and add it to the manifest |
228
- | template removed upstream | report it; do not automatically delete the project path |
229
- | `.gitignore` fragment markers unique and fragment hash equals baseline | replace only the owned fragment and record its new hash |
230
- | `.gitignore` fragment missing, duplicated, or changed | conflict; leave the whole host file untouched |
231
-
232
- `--force` may overwrite a conflicting `replace-if-unmodified` path and may replace the owned `.gitignore` fragment between unique markers. It never touches `project-owned` paths, never replaces the `.gitignore` host file, and never turns an upstream deletion into automatic project-file deletion. Any conflict without `--force` makes apply mode fail closed after producing the complete conflict report.
233
-
234
- An apply run updates managed files atomically, updates the managed `BUILDBEAT.md` version marker only under the same ownership rule, writes the updated schema 2 manifest last, runs `doctor`, and points the user to `bus-check`. A failed run uses the same in-process rollback boundary as Wave 1. Conflict output explicitly recommends opening an AI session to compare the current file with the new template and perform a semantic merge when appropriate.
235
-
236
- There is no project `uninstall` engine in the approved command set. `buildbeat uninstall` and its legacy alias remain reserved `command_not_available` responses. A manual removal guide may use the manifest as an inventory, but it must instruct the user to compare hashes, remove only explicitly selected unchanged managed files, edit only the owned `.gitignore` fragment, preserve every `project-owned` path, and delete the manifest only after reviewing what remains. No recursive purge shortcut is permitted.
237
-
238
- ## Security and privacy boundary
239
-
240
- - The legacy npm v0 is read-only. BuildBeat 1.20 Wave 1/2 commands perform only the documented local writes and make no network request. Lifecycle mechanics use only bundled templates and local Git/filesystem facts; package-manager download or publication is outside the project-lifecycle command.
241
- - Project scans stop at four directory levels or 5,000 entries, skip common build/vendor directories, and never follow symlinks.
242
- - JSON output contains paths, counts, capability/version metadata, finding codes, bounded placeholder tokens, and one bounded project-name candidate from `package.json`, the first README heading, or the directory name. It does not emit arbitrary source contents, dependency values, environment values, or secrets.
243
- - Command availability and worktree checks execute only fixed `--version` or read-only Git calls with argument arrays and no shell interpolation.
244
-
245
- Package publication remains a separate release action. Every published version must be tied to one immutable Git tag, pass the repository and packed-artifact checks, be read back from the official registry, and pass a clean-directory install plus executable smoke test. See [`RELEASING.md`](RELEASING.md). Documentation must not claim that a new `npx` version is available before those registry checks pass.