@haiyangbg/buildbeat 0.0.0 → 1.21.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 (80) hide show
  1. package/CHANGELOG.md +304 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +290 -0
  4. package/README.md +285 -4
  5. package/SKILL.md +334 -0
  6. package/bin/buildbeat.js +5 -0
  7. package/bin/solobaton.js +6 -0
  8. package/docs/CAPABILITY-MATRIX.md +50 -0
  9. package/docs/CHECKS.md +326 -0
  10. package/docs/CLI-PILOT-2026-08-23.md +25 -0
  11. package/docs/CLI-STRATEGY-2026-08.md +55 -0
  12. package/docs/CLI.md +233 -0
  13. package/docs/EXECUTION-PLAN.md +487 -0
  14. package/docs/LEGACY-V1.16-MIGRATION.md +54 -0
  15. package/docs/PHASE1-PILOT-2026-08-24.md +32 -0
  16. package/docs/PHASE2-BUILDBEAT-PILOT-2026-08-25.md +75 -0
  17. package/docs/PHASE2-PILOT-2026-08-25.md +88 -0
  18. package/docs/PHASE2-PILOT-PREFLIGHT-2026-08-25.md +42 -0
  19. package/docs/PHASE4-STABILITY-AUDIT-2026-08-25.md +35 -0
  20. package/docs/PHASE4-V1.20-PILOT-2026-08-25.md +56 -0
  21. package/docs/RELEASING.md +117 -0
  22. package/docs/ROADMAP.md +872 -0
  23. package/docs/WP4.3-RELEASE-EVIDENCE-2026-08-25.md +73 -0
  24. package/example/.buildbeat/manifest.json +45 -0
  25. package/example/AGENTS.md +19 -0
  26. package/example/ARCHITECTURE.md +39 -0
  27. package/example/BUILDBEAT.md +17 -0
  28. package/example/CLAUDE.md +7 -0
  29. package/example/README.md +75 -0
  30. package/example/contracts/PROTOCOL.md +38 -0
  31. package/example/pm/NOW.md +22 -0
  32. package/example/pm/adr/ADR-0001-local-first-sqlite.md +25 -0
  33. package/example/pm/adr/README.md +7 -0
  34. package/example/pm/archive//344/270/200/346/234/237/evidence/gate1.md +5 -0
  35. package/example/pm/archive//344/270/200/346/234/237/evidence/gate2.md +5 -0
  36. package/example/pm/archive//344/270/200/346/234/237/evidence/gate3.md +5 -0
  37. package/example/pm/archive//344/270/200/346/234/237/evidence/gate4.md +5 -0
  38. package/example/pm/archive//344/270/200/346/234/237/evidence/implementation.md +5 -0
  39. package/example/pm/decisions.md +20 -0
  40. package/example/pm/status//344/272/247/345/223/201.md +20 -0
  41. package/example/pm/status//345/205/250/346/240/210.md +15 -0
  42. package/example/pm/status//346/265/213/350/257/225.md +15 -0
  43. package/example/pm//344/270/200/346/234/237-/347/234/213/346/235/277.md +97 -0
  44. package/example/standards/CODE.md +18 -0
  45. package/example/standards/DESIGN.md +34 -0
  46. package/example/standards/REVIEW.md +16 -0
  47. package/example/standards/STACK.md +31 -0
  48. package/lessons.md +127 -0
  49. package/package.json +48 -7
  50. package/src/cli.js +323 -0
  51. package/src/constants.js +199 -0
  52. package/src/doctor.js +267 -0
  53. package/src/planner.js +251 -0
  54. package/src/project.js +839 -0
  55. package/src/upgrader.js +1249 -0
  56. package/src/writer.js +534 -0
  57. package/templates/.claude/agents/reviewer.md +62 -0
  58. package/templates/AGENTS.md +85 -0
  59. package/templates/ARCHITECTURE.md +50 -0
  60. package/templates/BUILDBEAT.md +13 -0
  61. package/templates/CLAUDE.md +7 -0
  62. package/templates/contracts/PROTOCOL.md +32 -0
  63. package/templates/gitignore.template +19 -0
  64. package/templates/pm/NOW.md +26 -0
  65. package/templates/pm/adr/ADR-0000-template.md +25 -0
  66. package/templates/pm/adr/README.md +15 -0
  67. package/templates/pm/changes/README.md +44 -0
  68. package/templates/pm/decisions.md +12 -0
  69. package/templates/pm/status/README.md +32 -0
  70. package/templates/pm//345/275/223/346/234/237/347/234/213/346/235/277.md +62 -0
  71. package/templates/scripts/bus-check.sh +1850 -0
  72. package/templates/scripts/design-preview.sh +44 -0
  73. package/templates/scripts/drift-check.sh +112 -0
  74. package/templates/scripts/pre-commit.sh +74 -0
  75. package/templates/scripts/verify-status.sh +105 -0
  76. package/templates/standards/CODE.md +23 -0
  77. package/templates/standards/DESIGN.md +36 -0
  78. package/templates/standards/REVIEW.md +20 -0
  79. package/templates/standards/STACK.md +37 -0
  80. package/templates//346/214/207/346/214/245/345/217/260.md +58 -0
package/docs/CHECKS.md ADDED
@@ -0,0 +1,326 @@
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.
@@ -0,0 +1,25 @@
1
+ # CLI v0 read-only pilot — 2026-08-23
2
+
3
+ > Historical evidence: this report predates the 2026-08-25 BuildBeat rename and tests only the published legacy `solobaton@1.16.3` distribution. It does not verify the canonical BuildBeat namespace.
4
+
5
+ ## Purpose and boundary
6
+
7
+ This pilot tested the public `solobaton@1.16.3` package against three existing projects. It exercised only `doctor --json` and `adopt --dry-run --json` from the official npm registry. It did not authorize or exercise project writes, installation, upgrade, uninstall, commit, push, merge, or deployment.
8
+
9
+ For every target, the Git `HEAD` and a digest of `git status --porcelain=v1 -z` were captured before and after both commands. All three pairs were identical. Every adoption response also reported `writesPerformed: false`.
10
+
11
+ ## Results
12
+
13
+ | Target | `doctor` | `adopt --dry-run` | Safety result |
14
+ |---|---|---|---|
15
+ | Solobaton source repository | Exit 1: no installed scaffold; missing hook warning | Ready to plan compact layout; one path collision warning | Git-visible state unchanged |
16
+ | AI foundation coordination repository | Exit 0: default layout `v1.14`; legacy manifest and placeholder warnings | Exit 1: existing installation blocked; 10 collisions reported | Git-visible state unchanged |
17
+ | ChickAI product repository | Exit 1: partial default layout; missing hook and placeholder warnings | Exit 1: partial installation blocked; 10 collisions reported | Git-visible state unchanged |
18
+
19
+ All scans completed without truncation or skipped symbolic links. The two larger brownfield targets exposed three to four nested repositories, five to seven deployment markers, tests, and UI signals without returning source contents or configuration values.
20
+
21
+ ## Decision
22
+
23
+ The pilot validates the current read-only value: the CLI distinguishes an uninstalled repository, a managed-looking legacy installation, and an ambiguous partial installation, and it fails closed rather than guessing ownership.
24
+
25
+ Write-capable `init`, `adopt`, `upgrade`, and `uninstall` remain disabled. One successful plan plus two ownership blockers is not sufficient evidence for safe writes. Any future write phase requires a separate approved milestone covering transactional rollback, manifest ownership, collision choices, hook chaining, `.gitignore` merging, legacy adoption, and destructive-operation tests. This is a product Gate, not an outstanding maintenance action from this pilot.
@@ -0,0 +1,55 @@
1
+ # CLI 策略调研:AI 工作流项目官方能力对照(2026-08-24)
2
+
3
+ > 目的:为 BuildBeat CLI 边界决策提供可复核的产品事实。
4
+ > 证据边界:仅核对文末列出的官方仓库文档与源码页面;“未见”只表示这些页面没有记录该能力,不外推为整个市场不存在。星数等易漂移指标不作为架构依据。
5
+ > 结论一句话:**三家都用 CLI 降低安装或更新成本,但命令面宽度并不相同;可迁移到 BuildBeat 的共同部分,是确定性脚手架/更新、明确的文件所有权,以及把项目语义留给 agent/Skill。**
6
+ > 本调研支持 2026-08-24 的“选择性解冻”决策,但不把竞品实现当作 BuildBeat 必须复制的功能清单;执行边界见 [`EXECUTION-PLAN.md`](EXECUTION-PLAN.md)。
7
+
8
+ ---
9
+
10
+ ## 1. 三家官方项目对照
11
+
12
+ | 维度 | GitHub Spec Kit | OpenSpec | BMAD-METHOD |
13
+ |---|---|---|---|
14
+ | 安装入口 | `specify init`(官方也给出持久安装与一次性运行方式) | `npm install -g @fission-ai/openspec@latest` → `openspec init` | `npx bmad-method install` |
15
+ | CLI 命令面(摘录) | `init`;integration 的 install/status/upgrade/uninstall/switch;extension/preset 等 | `init/update/doctor/context/list/show/validate/archive/schema/config` 等,明显不只是安装器 | 以 `install` 为统一入口,通过 action/channel/pin 参数处理首装与更新 |
16
+ | 确定性职责 | 生成共享模板和 agent integration;manifest/hash 感知安装、状态、升级与卸载 | 初始化项目、重新生成 tool 指令文件,同时承担结构校验和若干工作流生命周期命令 | 模块化安装、复用现有设置更新、渠道选择与版本门控 |
17
+ | 项目语义 | 规格/计划等用户产物不由 integration upgrade 改写;生成式工作主要由 agent 命令承载 | agent 命令/skills 承担生成式工作,但 CLI 也明确拥有 validate/archive 等确定性生命周期能力 | agent/workflow 承担方法语义,安装器负责落盘与版本选择 |
18
+ | 升级机制 | manifest 跟踪 managed 文件;本地改写会阻止 upgrade,除非 `--force`;规格、计划和源码不在该升级路径内 | `update` 按当前配置重新生成受管 skills/commands,并包含遗留目录迁移与清理逻辑 | Quick Update 复用设置;stable 的 patch/minor 可自动,major 默认拒绝,非交互接受需显式 pin |
19
+ | 三方合并 | 所核对官方页描述 hash/阻断/force,未记录三方合并 | 所核对 CLI 文档与 update 源码描述重新生成/迁移,未记录三方合并 | 所核对安装文档描述更新与版本门控,未记录三方合并 |
20
+ | 卸载 | 有 `integration uninstall`:删除 hash 未变文件、保留已改文件,`--force` 可覆盖保护 | 官方 CLI 摘要未列项目卸载命令;不能据此断言全项目没有任何清理路径 | 所核对安装文档未列独立卸载命令;不能据此断言其他位置不存在 |
21
+
22
+ 补充观察:AGENTS.md / SKILL.md 这类文件协议不要求项目运行时依赖专用 CLI,但大规模分发仍常借助平台 marketplace、安装命令或复制脚手架。这里能证明的是“分发入口有价值”,不能推出“语义必须进 CLI”。
23
+
24
+ ## 2. 可迁移的共同部分
25
+
26
+ 1. CLI 对“第一次落盘”和“以后怎么更新”有明确价值;
27
+ 2. 确定性受管文件应有 manifest/hash 或等价所有权边界;
28
+ 3. 用户产物与项目事实不应被机械升级猜测或覆盖;
29
+ 4. 发生本地改写时,停止、报告、显式 force 是已验证的安全模式;
30
+ 5. 生成式语义主要由 agent/Skill 承担,但竞品 CLI 仍可能包含校验和生命周期命令,因此不能写成“语义 100% 不在 CLI”。
31
+
32
+ 三家并不存在统一的命令面,也不存在“都没有卸载”的共同事实。BuildBeat 是否实现某项能力,应回到自身失败模式、替代路径和维护成本,而不是按竞品数量投票。
33
+
34
+ ## 3. 对 BuildBeat 的启示
35
+
36
+ 1. **文件所有权设计可直接借鉴 Spec Kit**:`replace-if-unmodified` 对应 hash 跟踪的 managed 文件;`project-owned` 对应升级路径明确排除的规格、计划与源码。
37
+ 2. **不做三方合并是成本取舍,不是市场定律**:官方对照页没有提供三方合并先例;BuildBeat 可先输出冲突报告,把语义合并交给现场 AI 会话,但必须通过自己的回归和试点验证效果。
38
+ 3. **SKILL-first 方向成立**:OpenSpec 和 Spec Kit 都会生成面向 agent 的 skills/commands,说明语义入口与确定性 CLI 可以分层。
39
+ 4. **不把 bus-check 复刻进 CLI**:这是 BuildBeat 已有无安装脚本层带来的产品取舍;OpenSpec 选择 CLI validate 不能反向证明它“没有脚本层”,也无需被复制。
40
+ 5. **`init` 真写是最短分发闭环**:三家的开始路径都由安装/初始化命令承接。BuildBeat 仍需用自己的新项目试点证明 README 一条命令能完成从落盘到 AI 渲染的全链路。
41
+
42
+ ## 4. 决策记录(2026-08-24)
43
+
44
+ - 推翻同日早先的"CLI 全冻结",修订为**选择性解冻**:
45
+ - **Wave 1**:`init`/`adopt` 真写入(哑脚手架:拷模板 + 填确定项,剩余占位符显式留给 AI 会话渲染;写 manifest 基线);
46
+ - **Wave 2**:`upgrade` 机械升级(hash 相等→替换;改过→停下报告 + `--force`;project-owned 永不碰;semver 门控);
47
+ - `three-way-only` 策略降级为“冲突报告 + AI 会话语义合并”,三方合并引擎从契约删除;
48
+ - **继续冻结**:BuildBeat 项目 uninstall 引擎(手册替代)、`gate/adr/standards` 等 CLI 命令、CLI 复刻 bus 级检查、中断恢复 journal(以“写前要求 git 工作区干净”替代,git 即回滚安全网)。Spec Kit 有 integration uninstall,不改变本产品的阶段性取舍。
49
+ - 分发补强:README 第一屏一条命令;打包 Claude plugin marketplace plugin。
50
+
51
+ ## 5. 官方来源(核对于 2026-08-24)
52
+
53
+ - Spec Kit:[Upgrade Guide](https://github.com/github/spec-kit/blob/main/docs/upgrade.md)、[Integration reference](https://github.com/github/spec-kit/blob/main/docs/reference/integrations.md)、[Core command reference](https://github.com/github/spec-kit/blob/main/docs/reference/core.md)
54
+ - OpenSpec:[CLI reference](https://github.com/Fission-AI/OpenSpec/blob/main/docs/cli.md)、[`src/core/update.ts`](https://github.com/Fission-AI/OpenSpec/blob/main/src/core/update.ts)
55
+ - BMAD-METHOD:[How to Install BMad](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/docs/how-to/install-bmad.md)