@haiyangbg/buildbeat 0.0.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +296 -0
- package/LICENSE +21 -0
- package/README.en.md +288 -0
- package/README.md +283 -4
- package/SKILL.md +303 -0
- package/bin/buildbeat.js +5 -0
- package/bin/solobaton.js +6 -0
- package/docs/CAPABILITY-MATRIX.md +50 -0
- package/docs/CHECKS.md +326 -0
- package/docs/CLI-PILOT-2026-08-23.md +25 -0
- package/docs/CLI-STRATEGY-2026-08.md +55 -0
- package/docs/CLI.md +233 -0
- package/docs/EXECUTION-PLAN.md +487 -0
- package/docs/LEGACY-V1.16-MIGRATION.md +54 -0
- package/docs/PHASE1-PILOT-2026-08-24.md +32 -0
- package/docs/PHASE2-BUILDBEAT-PILOT-2026-08-25.md +75 -0
- package/docs/PHASE2-PILOT-2026-08-25.md +88 -0
- package/docs/PHASE2-PILOT-PREFLIGHT-2026-08-25.md +42 -0
- package/docs/PHASE4-STABILITY-AUDIT-2026-08-25.md +33 -0
- package/docs/PHASE4-V1.20-PILOT-2026-08-25.md +52 -0
- package/docs/RELEASING.md +117 -0
- package/docs/ROADMAP.md +873 -0
- package/example/.buildbeat/manifest.json +45 -0
- package/example/AGENTS.md +19 -0
- package/example/ARCHITECTURE.md +39 -0
- package/example/BUILDBEAT.md +17 -0
- package/example/CLAUDE.md +7 -0
- package/example/README.md +53 -0
- package/example/contracts/PROTOCOL.md +38 -0
- package/example/pm/NOW.md +22 -0
- package/example/pm/adr/ADR-0001-local-first-sqlite.md +25 -0
- package/example/pm/adr/README.md +7 -0
- package/example/pm/archive//344/270/200/346/234/237/evidence/gate1.md +5 -0
- package/example/pm/archive//344/270/200/346/234/237/evidence/gate2.md +5 -0
- package/example/pm/archive//344/270/200/346/234/237/evidence/gate3.md +5 -0
- package/example/pm/archive//344/270/200/346/234/237/evidence/gate4.md +5 -0
- package/example/pm/archive//344/270/200/346/234/237/evidence/implementation.md +5 -0
- package/example/pm/decisions.md +20 -0
- package/example/pm/status//344/272/247/345/223/201.md +20 -0
- package/example/pm/status//345/205/250/346/240/210.md +15 -0
- package/example/pm/status//346/265/213/350/257/225.md +15 -0
- package/example/pm//344/270/200/346/234/237-/347/234/213/346/235/277.md +97 -0
- package/example/standards/CODE.md +18 -0
- package/example/standards/DESIGN.md +34 -0
- package/example/standards/REVIEW.md +16 -0
- package/example/standards/STACK.md +31 -0
- package/lessons.md +119 -0
- package/package.json +48 -7
- package/src/cli.js +323 -0
- package/src/constants.js +199 -0
- package/src/doctor.js +267 -0
- package/src/planner.js +251 -0
- package/src/project.js +839 -0
- package/src/upgrader.js +1249 -0
- package/src/writer.js +534 -0
- package/templates/.claude/agents/reviewer.md +62 -0
- package/templates/AGENTS.md +64 -0
- package/templates/ARCHITECTURE.md +50 -0
- package/templates/BUILDBEAT.md +13 -0
- package/templates/CLAUDE.md +7 -0
- package/templates/contracts/PROTOCOL.md +32 -0
- package/templates/gitignore.template +19 -0
- package/templates/pm/NOW.md +26 -0
- package/templates/pm/adr/ADR-0000-template.md +25 -0
- package/templates/pm/adr/README.md +15 -0
- package/templates/pm/changes/README.md +44 -0
- package/templates/pm/decisions.md +12 -0
- package/templates/pm/status/README.md +32 -0
- package/templates/pm//345/275/223/346/234/237/347/234/213/346/235/277.md +62 -0
- package/templates/scripts/bus-check.sh +1850 -0
- package/templates/scripts/design-preview.sh +44 -0
- package/templates/scripts/drift-check.sh +112 -0
- package/templates/scripts/pre-commit.sh +74 -0
- package/templates/scripts/verify-status.sh +105 -0
- package/templates/standards/CODE.md +23 -0
- package/templates/standards/DESIGN.md +36 -0
- package/templates/standards/REVIEW.md +20 -0
- package/templates/standards/STACK.md +37 -0
- package/templates//346/214/207/346/214/245/345/217/260.md +35 -0
package/docs/CLI.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# BuildBeat CLI lifecycle contract
|
|
2
|
+
|
|
3
|
+
Status: **BuildBeat `1.20.0` scoped-distribution candidate** · 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.20 candidate merges Phase 0–3, including bounded `init/adopt` writes and schema-2-only mechanical `upgrade`; the genuine version-increment pilot is archived in [`PHASE4-V1.20-PILOT-2026-08-25.md`](PHASE4-V1.20-PILOT-2026-08-25.md), while exact registry publication remains separately gated by [`RELEASING.md`](RELEASING.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 source candidate. Synchronous file-bus checks remain authoritative in the project-local scripts specified by [`CHECKS.md`](CHECKS.md).
|
|
6
|
+
|
|
7
|
+
The bilingual [`CAPABILITY-MATRIX.md`](CAPABILITY-MATRIX.md) is the compact authority for what Skill-only, the legacy npm v0, and BuildBeat 1.20 can each do. Command details and safety semantics remain authoritative in this document.
|
|
8
|
+
|
|
9
|
+
## Command boundary and phased availability
|
|
10
|
+
|
|
11
|
+
The canonical scoped-package commands are:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm view @haiyangbg/buildbeat@latest version
|
|
15
|
+
npx --yes --package=@haiyangbg/buildbeat@latest buildbeat doctor /path/to/project
|
|
16
|
+
npx --yes --package=@haiyangbg/buildbeat@latest buildbeat init /path/to/project --dry-run
|
|
17
|
+
npx --yes --package=@haiyangbg/buildbeat@latest buildbeat adopt /path/to/project --dry-run
|
|
18
|
+
npx --yes --package=@haiyangbg/buildbeat@latest buildbeat upgrade /path/to/project --dry-run
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
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.
|
|
22
|
+
|
|
23
|
+
- `doctor` reads an existing project and reports installation state, layout, version marker, required files, unresolved canonical placeholders, hooks, and capability dependencies.
|
|
24
|
+
- `init/adopt --dry-run` inspect a target and emit the complete default/compact plan with zero writes.
|
|
25
|
+
- `init/adopt` without `--dry-run` apply only after all blockers are absent and interactive confirmation or explicit `--yes` is present.
|
|
26
|
+
- `upgrade --dry-run` plans a schema-2-only mechanical version transition; apply is fail-closed on unresolved conflict.
|
|
27
|
+
- `--json` returns a versioned JSON document for agents and CI.
|
|
28
|
+
- `diff` and `uninstall` remain reserved and disabled. Workflow commands remain in Skill/project scripts.
|
|
29
|
+
|
|
30
|
+
The locked-checkout equivalent is:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node bin/buildbeat.js init /path/to/project --dry-run --json
|
|
34
|
+
node bin/buildbeat.js init /path/to/project # plan + interactive confirmation
|
|
35
|
+
node bin/buildbeat.js adopt /path/to/project --yes # non-interactive only after plan approval
|
|
36
|
+
node bin/buildbeat.js upgrade /path/to/project --dry-run --json
|
|
37
|
+
node bin/buildbeat.js upgrade /path/to/project # apply only when the complete plan is ready
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`--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.
|
|
41
|
+
|
|
42
|
+
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.
|
|
43
|
+
|
|
44
|
+
The target command whitelist is intentionally small:
|
|
45
|
+
|
|
46
|
+
| Milestone | Enabled main commands | Boundary |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| legacy `solobaton@1.16.3` | `doctor`, `init --dry-run`, `adopt --dry-run`, `version` | independently verified read-only v0; deprecated distribution ID after scoped migration |
|
|
49
|
+
| BuildBeat `1.20.0` | `doctor`, `init`, `adopt`, `upgrade`, `version` | merged Phase 0–3 command set; writes remain bounded by the transaction and human-Gate contracts below |
|
|
50
|
+
|
|
51
|
+
`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.
|
|
52
|
+
|
|
53
|
+
Exit codes:
|
|
54
|
+
|
|
55
|
+
| Code | Meaning |
|
|
56
|
+
|---:|---|
|
|
57
|
+
| `0` | The inspection/plan completed and has no blocker-level finding |
|
|
58
|
+
| `1` | The command completed, but the project has an error or lifecycle blocker |
|
|
59
|
+
| `2` | Usage/confirmation error, or a reserved command that the running version does not support |
|
|
60
|
+
|
|
61
|
+
## CLI package lifecycle is not project lifecycle
|
|
62
|
+
|
|
63
|
+
The public npm package gives the executable a conventional, reversible distribution path:
|
|
64
|
+
|
|
65
|
+
| Intent | Command | Project effect |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| Current one-off run | `npx --yes --package=@haiyangbg/buildbeat@latest buildbeat doctor <project>` | Runs the registry version without a persistent global installation |
|
|
68
|
+
| Resolve an exact version | `npm view @haiyangbg/buildbeat@latest version` | Records the version to substitute for `@latest` in a reproducible invocation |
|
|
69
|
+
| Install or update the global CLI | `npm install --global @haiyangbg/buildbeat@latest` | Replaces only the globally installed package and executables |
|
|
70
|
+
| Remove the global CLI | `npm uninstall --global @haiyangbg/buildbeat` | Removes only the global package and executables |
|
|
71
|
+
|
|
72
|
+
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.
|
|
73
|
+
|
|
74
|
+
## Skill and CLI responsibilities
|
|
75
|
+
|
|
76
|
+
| Layer | Owns | Must not claim |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `SKILL.md` | code-aware inspection, minimal human questions, project-specific reasoning, Gate semantics | deterministic installation state or safe file ownership by itself |
|
|
79
|
+
| CLI | bounded filesystem inspection, plans, manifest/hash handling, repeatable lifecycle mechanics | product judgment, contract discovery, Agent runtime, or automatic Gate approval |
|
|
80
|
+
| Scaffold files | project facts, decisions, contracts, status, evidence | that template defaults are current project facts |
|
|
81
|
+
| Shell guardrails | deterministic local checks | server-side enforcement or live production truth without project adapters |
|
|
82
|
+
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
## File ownership policies
|
|
86
|
+
|
|
87
|
+
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:
|
|
88
|
+
|
|
89
|
+
- schema 1 remains readable and may contain the historical `three-way-only` value;
|
|
90
|
+
- schema 2 may contain only `replace-if-unmodified`, `project-owned`, and `merge-only`;
|
|
91
|
+
- new plans and writes must never emit `three-way-only`.
|
|
92
|
+
|
|
93
|
+
| Schema 2 policy | Examples | Write/upgrade rule |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `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. |
|
|
96
|
+
| `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. |
|
|
97
|
+
| `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. |
|
|
98
|
+
|
|
99
|
+
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.
|
|
100
|
+
|
|
101
|
+
## Manifest contract
|
|
102
|
+
|
|
103
|
+
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:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"schemaVersion": 1,
|
|
108
|
+
"scaffoldVersion": "v1.16",
|
|
109
|
+
"cliVersion": "1.16.3",
|
|
110
|
+
"layout": "default",
|
|
111
|
+
"installedAt": "2026-08-22T00:00:00.000Z",
|
|
112
|
+
"files": {
|
|
113
|
+
"scripts/bus-check.sh": {
|
|
114
|
+
"policy": "replace-if-unmodified",
|
|
115
|
+
"baselineSha256": "<64 lowercase hex characters>"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"integrations": {
|
|
119
|
+
"gitignore": null,
|
|
120
|
+
"hooks": null
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Schema 2 is the first write-capable shape targeted by Wave 1:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"schemaVersion": 2,
|
|
130
|
+
"scaffoldVersion": "v1.20",
|
|
131
|
+
"cliVersion": "1.20.0",
|
|
132
|
+
"layout": "default",
|
|
133
|
+
"installedAt": "2026-08-24T00:00:00.000Z",
|
|
134
|
+
"files": {
|
|
135
|
+
"AGENTS.md": {
|
|
136
|
+
"policy": "replace-if-unmodified",
|
|
137
|
+
"baselineSha256": "<64 lowercase hex characters>"
|
|
138
|
+
},
|
|
139
|
+
"contracts/PROTOCOL.md": {
|
|
140
|
+
"policy": "project-owned",
|
|
141
|
+
"baselineSha256": "<64 lowercase hex characters>"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"integrations": {
|
|
145
|
+
"gitignore": {
|
|
146
|
+
"path": ".gitignore",
|
|
147
|
+
"beginMarker": "# >>> buildbeat managed >>>",
|
|
148
|
+
"endMarker": "# <<< buildbeat managed <<<",
|
|
149
|
+
"baselineSha256": "<SHA-256 of the exact owned fragment bytes, including markers>"
|
|
150
|
+
},
|
|
151
|
+
"hooks": null
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Rules common to both schemas:
|
|
157
|
+
|
|
158
|
+
1. Only the documented top-level and nested fields are accepted. `installedAt` is a canonical UTC timestamp with milliseconds.
|
|
159
|
+
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.
|
|
160
|
+
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.
|
|
161
|
+
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`.
|
|
162
|
+
5. No credential, environment value, file content, inferred private architecture fact, account identifier, or remote state enters the manifest.
|
|
163
|
+
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`.
|
|
164
|
+
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.
|
|
165
|
+
8. The manifest itself does not make a project healthy; unresolved placeholders, checks, evidence, and human Gates remain separate.
|
|
166
|
+
|
|
167
|
+
Schema 2 integration rules:
|
|
168
|
+
|
|
169
|
+
1. `integrations` contains exactly `gitignore` and `hooks`; `hooks` is `null` because hook installation stays a documented Skill/manual step.
|
|
170
|
+
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.
|
|
171
|
+
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`.
|
|
172
|
+
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.
|
|
173
|
+
|
|
174
|
+
## Scaffold write transaction (Wave 1 / BuildBeat 1.20)
|
|
175
|
+
|
|
176
|
+
A write-capable `init` or `adopt` must:
|
|
177
|
+
|
|
178
|
+
1. inspect without following symlinks and show the complete plan before any mutation; `--dry-run` performs this path and never asks for confirmation;
|
|
179
|
+
2. refuse mixed/partial/already-installed targets, unsafe paths, and every destination collision. Wave 1 has no project-file `--force`;
|
|
180
|
+
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;
|
|
181
|
+
4. copy the bundled template tree for the selected layout while excluding `standards/` and `pm/adr/` by the shared optional-prefix constant;
|
|
182
|
+
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;
|
|
183
|
+
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;
|
|
184
|
+
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;
|
|
185
|
+
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;
|
|
186
|
+
9. write schema 2 manifest last, after all scaffold files and the integration fragment are durable; and
|
|
187
|
+
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.
|
|
188
|
+
|
|
189
|
+
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.
|
|
190
|
+
|
|
191
|
+
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.
|
|
192
|
+
|
|
193
|
+
No command may initialize Git, add a remote, commit, push, install a package globally, or cross a human Gate without separate explicit authority.
|
|
194
|
+
|
|
195
|
+
## Mechanical upgrade and manual removal
|
|
196
|
+
|
|
197
|
+
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.
|
|
198
|
+
|
|
199
|
+
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.
|
|
200
|
+
|
|
201
|
+
Version gates compare `manifest.scaffoldVersion` with the bundled `SCAFFOLD_VERSION`:
|
|
202
|
+
|
|
203
|
+
- equal version → no template upgrade is needed;
|
|
204
|
+
- installed version newer than the bundle → block; downgrade is not supported;
|
|
205
|
+
- newer bundle in the same major → eligible for mechanical upgrade;
|
|
206
|
+
- newer bundle in another major → block unless `--major` explicitly acknowledges the major transition.
|
|
207
|
+
|
|
208
|
+
The planner evaluates the manifest baseline, current project bytes, and new bundled template without performing a three-way merge:
|
|
209
|
+
|
|
210
|
+
| State | Default action |
|
|
211
|
+
|---|---|
|
|
212
|
+
| `replace-if-unmodified`, current hash equals baseline, new template changed | atomically replace and record the new baseline hash |
|
|
213
|
+
| `replace-if-unmodified`, current file changed or is missing | conflict; leave it untouched |
|
|
214
|
+
| `project-owned`, whether existing or newly introduced upstream | never write, replace, or delete; emit a migration note or patch candidate |
|
|
215
|
+
| new `replace-if-unmodified` path with no collision | create it and add it to the manifest |
|
|
216
|
+
| template removed upstream | report it; do not automatically delete the project path |
|
|
217
|
+
| `.gitignore` fragment markers unique and fragment hash equals baseline | replace only the owned fragment and record its new hash |
|
|
218
|
+
| `.gitignore` fragment missing, duplicated, or changed | conflict; leave the whole host file untouched |
|
|
219
|
+
|
|
220
|
+
`--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.
|
|
221
|
+
|
|
222
|
+
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.
|
|
223
|
+
|
|
224
|
+
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.
|
|
225
|
+
|
|
226
|
+
## Security and privacy boundary
|
|
227
|
+
|
|
228
|
+
- 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.
|
|
229
|
+
- Project scans stop at four directory levels or 5,000 entries, skip common build/vendor directories, and never follow symlinks.
|
|
230
|
+
- 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.
|
|
231
|
+
- Command availability and worktree checks execute only fixed `--version` or read-only Git calls with argument arrays and no shell interpolation.
|
|
232
|
+
|
|
233
|
+
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.
|