@kungfu-tech/buildchain 3.0.3-alpha.1 → 3.0.3-alpha.2

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.
@@ -3,11 +3,11 @@
3
3
  "contract": "kungfu-buildchain-public-surface-reverse-audit",
4
4
  "status": "passed",
5
5
  "summary": {
6
- "cliCommandCount": 107,
6
+ "cliCommandCount": 111,
7
7
  "workflowCount": 57,
8
8
  "actionCount": 6,
9
9
  "sitePageCount": 60,
10
- "docCommandRefCount": 311,
10
+ "docCommandRefCount": 315,
11
11
  "failureCount": 0
12
12
  },
13
13
  "enumerated": {
@@ -387,6 +387,16 @@
387
387
  "source": "bin/buildchain.mjs",
388
388
  "usage": "buildchain paper build [--cwd <dir>] [--execute] [--json]"
389
389
  },
390
+ {
391
+ "id": "paper-fleet-audit",
392
+ "source": "bin/buildchain.mjs",
393
+ "usage": "buildchain paper fleet audit [--root <dir>] [--offline] [--json]"
394
+ },
395
+ {
396
+ "id": "paper-fleet-update",
397
+ "source": "bin/buildchain.mjs",
398
+ "usage": "buildchain paper fleet update [--root <dir>] [--write] [--json]"
399
+ },
390
400
  {
391
401
  "id": "paper-migrate",
392
402
  "source": "bin/buildchain.mjs",
@@ -412,6 +422,16 @@
412
422
  "source": "bin/buildchain.mjs",
413
423
  "usage": "buildchain paper status [--cwd <dir>] [--json]"
414
424
  },
425
+ {
426
+ "id": "paper-work-start",
427
+ "source": "bin/buildchain.mjs",
428
+ "usage": "buildchain paper work start <topic> [--cwd <dir>] [--branch <branch>] [--execute] [--json]"
429
+ },
430
+ {
431
+ "id": "paper-work-submit",
432
+ "source": "bin/buildchain.mjs",
433
+ "usage": "buildchain paper work submit [--cwd <dir>] [--title <title>] [--body <body>] [--execute] [--json]"
434
+ },
415
435
  {
416
436
  "id": "portable-cache",
417
437
  "source": "bin/buildchain.mjs",
@@ -4012,6 +4032,26 @@
4012
4032
  "path": "docs/cli.md",
4013
4033
  "command": "buildchain paper build --json"
4014
4034
  },
4035
+ {
4036
+ "id": "paper-fleet-audit",
4037
+ "path": "docs/cli.md",
4038
+ "command": "buildchain paper fleet audit"
4039
+ },
4040
+ {
4041
+ "id": "paper-fleet-audit",
4042
+ "path": "docs/publication-artifacts.md",
4043
+ "command": "buildchain paper fleet audit"
4044
+ },
4045
+ {
4046
+ "id": "paper-fleet-update",
4047
+ "path": "docs/cli.md",
4048
+ "command": "buildchain paper fleet update"
4049
+ },
4050
+ {
4051
+ "id": "paper-fleet-update",
4052
+ "path": "docs/publication-artifacts.md",
4053
+ "command": "buildchain paper fleet update"
4054
+ },
4015
4055
  {
4016
4056
  "id": "paper-migrate",
4017
4057
  "path": "docs/cli.md",
@@ -4027,11 +4067,6 @@
4027
4067
  "path": "docs/publication-artifacts.md",
4028
4068
  "command": "buildchain paper preflight --offline"
4029
4069
  },
4030
- {
4031
- "id": "paper-preflight",
4032
- "path": "README.md",
4033
- "command": "buildchain paper preflight --offline"
4034
- },
4035
4070
  {
4036
4071
  "id": "paper-resume",
4037
4072
  "path": "docs/cli.md",
@@ -4058,9 +4093,14 @@
4058
4093
  "command": "buildchain paper status --json"
4059
4094
  },
4060
4095
  {
4061
- "id": "paper-status",
4062
- "path": "README.md",
4063
- "command": "buildchain paper status"
4096
+ "id": "paper-work-start",
4097
+ "path": "docs/cli.md",
4098
+ "command": "buildchain paper work start"
4099
+ },
4100
+ {
4101
+ "id": "paper-work-submit",
4102
+ "path": "docs/cli.md",
4103
+ "command": "buildchain paper work submit"
4064
4104
  },
4065
4105
  {
4066
4106
  "id": "paper",
@@ -4468,9 +4508,9 @@
4468
4508
  "cliRegistryPath": "dist/site/cli-registry.json",
4469
4509
  "workflowRegistryPath": "dist/site/workflow-registry.json",
4470
4510
  "pageRegistryPath": "dist/site/page-registry.json",
4471
- "cliRegistryDigest": "d7e8b86baa70f65617f2cafe24bb448d1ce56c3221c8b5a96217fe892b080520",
4511
+ "cliRegistryDigest": "48cb548303c6773a09257bca5699f0d5e84639a083c85e566446efb740d34578",
4472
4512
  "workflowRegistryDigest": "9500a8099e10c5328b6e051445dba3c18cf0eb31eec3655b6554b111417f9abd",
4473
- "pageRegistryDigest": "02f8a3c24c8fbb9840e04d0d5605d662a72c174e2f355078af8d8b24d9fe5fc9"
4513
+ "pageRegistryDigest": "76d2cc04d6bff33c9e8842eedee7e0a78506ba8a99f4c89a429c1c4e5042e90c"
4474
4514
  },
4475
4515
  "comparison": {
4476
4516
  "missingCliRegistry": [],
@@ -4483,8 +4523,26 @@
4483
4523
  "mode": "closed-world-enumerable",
4484
4524
  "scope": "Buildchain CLI usage/dispatch, reusable workflow inputs, action inputs, site pages, and documentation command references",
4485
4525
  "residualRisk": [
4486
- "Shell commands delegated through helper scripts are only counted when exposed through bin/buildchain.mjs usage or docs.",
4487
- "YAML parsing is limited to first-class action/workflow inputs, not arbitrary step environment variables."
4526
+ {
4527
+ "id": "public-surface-helper-command-enumeration",
4528
+ "definedBy": "https://kfd.libkungfu.dev/schemas/kfd-2/trust-taxonomy.schema.json#/$defs/residualRisk",
4529
+ "riskType": "natural-language-semantic-risk",
4530
+ "trustImpact": "downgrade-warning",
4531
+ "machineProvability": "not-machine-verifiable",
4532
+ "agentAction": "semantic-review-required",
4533
+ "owner": "Buildchain maintainers",
4534
+ "reason": "Shell commands delegated through helper scripts are counted only when exposed through bin/buildchain.mjs usage or documentation."
4535
+ },
4536
+ {
4537
+ "id": "public-surface-yaml-enumeration",
4538
+ "definedBy": "https://kfd.libkungfu.dev/schemas/kfd-2/trust-taxonomy.schema.json#/$defs/residualRisk",
4539
+ "riskType": "natural-language-semantic-risk",
4540
+ "trustImpact": "downgrade-warning",
4541
+ "machineProvability": "not-machine-verifiable",
4542
+ "agentAction": "semantic-review-required",
4543
+ "owner": "Buildchain maintainers",
4544
+ "reason": "YAML enumeration covers first-class action and workflow inputs, not arbitrary step environment variables."
4545
+ }
4488
4546
  ]
4489
4547
  }
4490
4548
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-publication-release-registry",
4
- "generatedAt": "2026-07-31T13:41:52.861Z",
5
- "publishedAt": "2026-07-31T13:41:52.861Z",
4
+ "generatedAt": "2026-07-31T15:25:20.771Z",
5
+ "publishedAt": "2026-07-31T15:25:20.771Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "b8e84bcb51e131207896c426fb8e79fd0f46b5ac",
22
+ "sourceRevision": "00a07a9a2eb0e4839727cf455fa209066166abbf",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "package": {
34
34
  "name": "@kungfu-tech/buildchain",
35
- "version": "3.0.3-alpha.1",
35
+ "version": "3.0.3-alpha.2",
36
36
  "versionSource": "package.json#version"
37
37
  },
38
38
  "sourceKind": "package-site-bundle",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "contract": "kungfu-buildchain-site-manifest",
4
- "generatedAt": "2026-07-31T13:41:52.861Z",
5
- "publishedAt": "2026-07-31T13:41:52.861Z",
4
+ "generatedAt": "2026-07-31T15:25:20.771Z",
5
+ "publishedAt": "2026-07-31T15:25:20.771Z",
6
6
  "reproducible": true,
7
7
  "timestampPolicy": "ci-injected",
8
8
  "deterministicInputs": [
@@ -19,7 +19,7 @@
19
19
  "declared Buildchain surface manifest contract"
20
20
  ],
21
21
  "sourceDateEpoch": "0",
22
- "sourceRevision": "b8e84bcb51e131207896c426fb8e79fd0f46b5ac",
22
+ "sourceRevision": "00a07a9a2eb0e4839727cf455fa209066166abbf",
23
23
  "timestampPolicyDetails": {
24
24
  "contract": "kungfu-buildchain-surface-timestamp-policy",
25
25
  "timestampFields": [
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "package": {
39
39
  "name": "@kungfu-tech/buildchain",
40
- "version": "3.0.3-alpha.1",
40
+ "version": "3.0.3-alpha.2",
41
41
  "versionSource": "package.json#version"
42
42
  },
43
43
  "entrypoint": "buildchain-site.json",
@@ -213,7 +213,7 @@
213
213
  "path": "docs/cli.md",
214
214
  "plane": "use",
215
215
  "exists": true,
216
- "digest": "sha256:60c6b00e0dbf60f0b7ba8c3c549027925d94a6bdf8a2af3f8e35d24ef1dd50ba"
216
+ "digest": "sha256:9afe80ef29fb5cce140ffc7980c41172495878726e5504e47914b8dc7af8fc1f"
217
217
  },
218
218
  {
219
219
  "id": "build-facts",
package/docs/cli.md CHANGED
@@ -123,13 +123,18 @@ predicate, platform manifest, Passport, and Buildchain evidence locally. See
123
123
 
124
124
  ### Governed paper lifecycle
125
125
 
126
- `buildchain paper` is the unified operator surface for a paper repository. Its
127
- eight subcommands return versioned JSON contracts with `--json`:
126
+ `buildchain paper` is the unified operator surface for one paper repository or
127
+ a discovered fleet. Every subcommand returns a versioned JSON contract with
128
+ `--json`:
128
129
 
129
130
  ```bash
130
131
  buildchain paper scaffold --package @kungfu-tech/paper-example \
131
132
  --repository kungfu-systems/paper-example
132
133
  buildchain paper migrate --json
134
+ buildchain paper work start golden-path --json
135
+ buildchain paper work submit --json
136
+ buildchain paper fleet audit --root ../papers --json
137
+ buildchain paper fleet update --root ../paper-worktrees --json
133
138
  buildchain paper preflight --offline --json
134
139
  buildchain paper bootstrap npm --json
135
140
  buildchain paper build --json
@@ -142,9 +147,27 @@ The safety and authority boundary is explicit:
142
147
 
143
148
  - `scaffold` plans a 14-file, no-overwrite repository shape by default; add
144
149
  `--write` to create only missing files.
145
- - `migrate` plans the five Buildchain-owned control-file changes needed by an
146
- existing paper repository. Add `--write` only after reviewing exact old and
147
- new digests; paper content and publication configuration are never rewritten.
150
+ - `migrate` plans the Buildchain-owned authority, workflow, contract lock,
151
+ version pin, and package control changes needed by an existing paper
152
+ repository. It pins an exact v3 dependency and adds pnpm-backed paper scripts.
153
+ Add `--write` only after reviewing exact old and new digests; paper content
154
+ and publication configuration are never rewritten. Refresh
155
+ `pnpm-lock.yaml` with `pnpm install --lockfile-only` after a write.
156
+ - `work start` derives the protected development branch from the configured
157
+ publication semver line and creates a safe local work branch only when the
158
+ worktree is clean, the sole `origin` is the canonical `kungfu-systems`
159
+ repository, and local HEAD equals the exact remotely observed development
160
+ SHA. It never fetches or merges silently.
161
+ - `work submit` accepts only an allowed non-protected work branch that contains
162
+ the exact remote development commit. It rejects divergent remote work,
163
+ wrong-base pull requests, dirty trees, forks, and ambiguous remotes; execution
164
+ uses a normal non-force push and opens or reuses a PR to the derived
165
+ development branch.
166
+ - `fleet audit` discovers `paper-*` repositories from a root and emits one
167
+ deterministic audit root over exact runtime, dependency, lockfile, workflow,
168
+ authority, and repository observations. `fleet update` reuses the migration
169
+ contract for every discovered repository, remains dry-run by default, and
170
+ refuses protected or non-work branches.
148
171
  - The scaffolded `.buildchain/paper/provisioning-authority.json` binds both
149
172
  caller workflow byte digests, their exact reusable-workflow SHA, the runtime
150
173
  SHA, contract-lock bytes, npm registry and trusted-publisher coordinates, and
@@ -180,8 +203,9 @@ The ordered evidence states are `scaffolded`, `governed`, `admitted`,
180
203
 
181
204
  The corresponding Node surface is
182
205
  `@kungfu-tech/buildchain/paper`. Planning and status functions are read-only;
183
- `writePaperScaffold()` and `writePaperMigration()` are the bounded local
184
- writers, and
206
+ `writePaperScaffold()`, `writePaperMigration()`, and
207
+ `writePaperFleetUpdate()` are the bounded local writers. Work plans expose
208
+ separate rechecking executors for local branch creation and normal push, and
185
209
  `executePaperNpmBootstrap()` preserves the same confirmation boundary used by
186
210
  the CLI.
187
211
 
@@ -358,6 +358,33 @@ Operationally, responsibility remains split:
358
358
  - the papers site consumes publication evidence and owns reader-facing
359
359
  rendering.
360
360
 
361
+ Daily work begins and ends through the repository-pinned v3 CLI:
362
+
363
+ ```sh
364
+ pnpm paper:work:start -- golden-path
365
+ # edit, test, and commit paper source
366
+ pnpm paper:work:submit
367
+ ```
368
+
369
+ The start plan derives `dev/vN/vN.M` from `publication.version` and requires
370
+ local HEAD to equal the exact canonical remote development SHA. The submit plan
371
+ allows only a non-protected work branch containing that SHA, a clean committed
372
+ tree, a normal fast-forward push, and a pull request back to the same derived
373
+ development branch. Neither command force-pushes, guesses a fork target, or
374
+ silently fetches and merges stale state.
375
+
376
+ Maintainers can inspect a sibling fleet without per-repository command copies:
377
+
378
+ ```sh
379
+ buildchain paper fleet audit --root /path/to/papers --json
380
+ buildchain paper fleet update --root /path/to/isolated-paper-worktrees --json
381
+ ```
382
+
383
+ Fleet update is dry-run first and accepts only isolated work branches. Its
384
+ typed plan carries exact-old and expected-new digests for the same owned
385
+ surfaces as `paper migrate`; `--write` also refreshes each pnpm lockfile. It
386
+ never rewrites paper content or publication configuration.
387
+
361
388
  Run a local readiness check without network observations:
362
389
 
363
390
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kungfu-tech/buildchain",
3
- "version": "3.0.3-alpha.1",
3
+ "version": "3.0.3-alpha.2",
4
4
  "private": false,
5
5
  "description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
6
6
  "repository": "https://github.com/kungfu-systems/buildchain",
@@ -28,8 +28,9 @@ Current shared surfaces:
28
28
  `@kungfu-tech/buildchain/publication-reproducibility`.
29
29
  - build-once publication bundle manifests and exact-byte verification through
30
30
  `@kungfu-tech/buildchain/publication-sealed-bundle`.
31
- - governed paper scaffold, preflight, status, npm bootstrap, build, Alpha, and
32
- resume plans through `@kungfu-tech/buildchain/paper`.
31
+ - governed Paper work-branch start/submit, fleet audit/update, scaffold,
32
+ preflight, status, npm bootstrap, build, Alpha, and resume plans through
33
+ `@kungfu-tech/buildchain/paper`.
33
34
 
34
35
  ## Toolkit Imports
35
36
 
@@ -33,6 +33,11 @@ const target = (
33
33
  strictRequiredChecks,
34
34
  allowedBypassActors,
35
35
  });
36
+ const paperTargets = (mainCheck) => [
37
+ target("main", [check(mainCheck)], false),
38
+ ...["dev", "alpha", "release"].map((family) =>
39
+ target(`${family}/v0/v0.1`, [check("check / check")], true)),
40
+ ];
36
41
  const PUBLIC_REPOSITORY_TARGETS = Object.freeze({
37
42
  ".github": [
38
43
  target("main", [check("governance")], false),
@@ -92,31 +97,11 @@ const PUBLIC_REPOSITORY_TARGETS = Object.freeze({
92
97
  target("dev/v1/v1.0", [check("Signed-off commits"), check("check")], true),
93
98
  target("alpha/v1/v1.0", [check("Signed-off commits"), check("check")], false),
94
99
  ],
95
- "paper-episodes-to-primitives": [
96
- target("main", [check("governance")], false),
97
- target("dev/v0/v0.1", [check("check / check")], true),
98
- target("alpha/v0/v0.1", [check("check / check")], true),
99
- ],
100
- "paper-kfd-foundation-real-world-agent-work": [
101
- target("main", [check("check / check")], false),
102
- target("dev/v0/v0.1", [check("check / check")], true),
103
- target("alpha/v0/v0.1", [check("check / check")], true),
104
- ],
105
- "paper-kfd-machine-life-roadmap": [
106
- target("main", [check("check / check")], false),
107
- target("dev/v0/v0.1", [check("check / check")], true),
108
- target("alpha/v0/v0.1", [check("check / check")], true),
109
- ],
110
- "paper-kungfu-product-white-paper": [
111
- target("main", [check("check / check")], false),
112
- target("dev/v0/v0.1", [check("check / check")], true),
113
- target("alpha/v0/v0.1", [check("check / check")], true),
114
- ],
115
- "paper-observer-declared-timelines": [
116
- target("main", [check("check / check")], false),
117
- target("dev/v0/v0.1", [check("check / check")], true),
118
- target("alpha/v0/v0.1", [check("check / check")], true),
119
- ],
100
+ "paper-episodes-to-primitives": paperTargets("governance"),
101
+ "paper-kfd-foundation-real-world-agent-work": paperTargets("check / check"),
102
+ "paper-kfd-machine-life-roadmap": paperTargets("check / check"),
103
+ "paper-kungfu-product-white-paper": paperTargets("check / check"),
104
+ "paper-observer-declared-timelines": paperTargets("check / check"),
120
105
  "site-kungfu-tech": [
121
106
  target("main", [check("web-surface / Record web-surface controller receipt")], false),
122
107
  ],
@@ -652,7 +652,7 @@ export {
652
652
  resolvePaperRepository,
653
653
  writePaperMigration,
654
654
  writePaperScaffold,
655
- } from "./paper.js";
655
+ } from "./paper.js"; export * from "./paper-work.js"; export * from "./paper-fleet.js";
656
656
 
657
657
  export {
658
658
  SURFACE_TIMESTAMP_POLICY_CONTRACT,
@@ -0,0 +1,260 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { planPaperMigration, writePaperMigration } from "./paper.js";
4
+ import {
5
+ PAPER_PATHS,
6
+ PAPER_WORK_BRANCH_PATTERN,
7
+ gitValue,
8
+ paperWorkSource,
9
+ readJson,
10
+ rootedPlan,
11
+ sha256Text,
12
+ stableJson,
13
+ workCheck,
14
+ } from "./paper-repository.js";
15
+
16
+ export const PAPER_FLEET_AUDIT_CONTRACT = "kungfu-buildchain-paper-fleet-audit";
17
+ export const PAPER_FLEET_UPDATE_PLAN_CONTRACT =
18
+ "kungfu-buildchain-paper-fleet-update-plan";
19
+
20
+ export function discoverPaperFleet(root = process.cwd()) {
21
+ const resolvedRoot = path.resolve(root);
22
+ if (
23
+ !fs.existsSync(resolvedRoot) ||
24
+ !fs.statSync(resolvedRoot).isDirectory()
25
+ ) {
26
+ throw new Error("paper fleet root must be an existing directory");
27
+ }
28
+ return fs
29
+ .readdirSync(resolvedRoot, { withFileTypes: true })
30
+ .filter(
31
+ (entry) =>
32
+ entry.isDirectory() &&
33
+ entry.name.startsWith("paper-") &&
34
+ fs.existsSync(path.join(resolvedRoot, entry.name, ".git")),
35
+ )
36
+ .map((entry) => path.join(resolvedRoot, entry.name))
37
+ .sort();
38
+ }
39
+
40
+ function plannedManagedSurfaces(options) {
41
+ try {
42
+ const plan = planPaperMigration(options);
43
+ return { changes: plan.changes, error: "" };
44
+ } catch (error) {
45
+ return { changes: [], error: error.message };
46
+ }
47
+ }
48
+
49
+ function paperFleetEntry({
50
+ cwd,
51
+ buildchainRoot,
52
+ buildchainVersion,
53
+ buildchainSha,
54
+ }) {
55
+ const source = paperWorkSource(cwd);
56
+ const packageJson = readJson(path.resolve(cwd, "package.json")).value || {};
57
+ const pinPath = path.resolve(cwd, PAPER_PATHS.versionPin);
58
+ const lockPath = path.resolve(cwd, "pnpm-lock.yaml");
59
+ const expected = plannedManagedSurfaces({
60
+ cwd,
61
+ buildchainRoot,
62
+ buildchainVersion,
63
+ buildchainSha,
64
+ });
65
+ const managed = expected.changes.map((entry) => ({
66
+ path: entry.path,
67
+ status:
68
+ entry.action === "unchanged"
69
+ ? "current"
70
+ : entry.action === "create"
71
+ ? "missing"
72
+ : "drifted",
73
+ expectedSha256: entry.sha256,
74
+ currentSha256: entry.currentSha256,
75
+ }));
76
+ const dependency =
77
+ packageJson.devDependencies?.["@kungfu-tech/buildchain"] || "";
78
+ const lockText = fs.existsSync(lockPath)
79
+ ? fs.readFileSync(lockPath, "utf8")
80
+ : "";
81
+ const checks = [
82
+ workCheck(
83
+ "repository.canonical-origin",
84
+ source.canonical,
85
+ "Repository identity resolves to the canonical kungfu-systems origin.",
86
+ "git remote -v",
87
+ ),
88
+ workCheck(
89
+ "package.buildchain-v3",
90
+ dependency === buildchainVersion && /^3\./.test(dependency),
91
+ "package.json pins the exact Buildchain v3 runtime.",
92
+ "buildchain paper migrate --write --json",
93
+ ),
94
+ workCheck(
95
+ "package.pnpm-lock",
96
+ Boolean(lockText) &&
97
+ lockText.includes("@kungfu-tech/buildchain") &&
98
+ lockText.includes(buildchainVersion),
99
+ "pnpm-lock.yaml binds the Buildchain dependency.",
100
+ "pnpm install --lockfile-only",
101
+ ),
102
+ workCheck(
103
+ "runtime.version-pin",
104
+ fs.existsSync(pinPath) &&
105
+ fs.readFileSync(pinPath, "utf8").trim() === buildchainVersion,
106
+ "The repository version pin equals the exact Buildchain runtime.",
107
+ "buildchain paper migrate --write --json",
108
+ ),
109
+ workCheck(
110
+ "managed-surfaces.current",
111
+ !expected.error && managed.every((entry) => entry.status === "current"),
112
+ "Every Buildchain-owned paper control surface matches v3.",
113
+ "buildchain paper migrate --write --json",
114
+ ),
115
+ ];
116
+ return {
117
+ name: path.basename(cwd),
118
+ cwd,
119
+ repository: source.repository,
120
+ branch: source.branch,
121
+ head: source.head,
122
+ clean: source.clean,
123
+ package: packageJson.name || "",
124
+ buildchainDependency: dependency,
125
+ expectedError: expected.error,
126
+ managed,
127
+ checks,
128
+ ok: checks.every((entry) => entry.status === "pass"),
129
+ };
130
+ }
131
+
132
+ export function collectPaperFleetAudit({
133
+ root = process.cwd(),
134
+ repositories = [],
135
+ buildchainRoot = process.cwd(),
136
+ buildchainVersion = "",
137
+ buildchainSha = "",
138
+ governance = {},
139
+ } = {}) {
140
+ const resolvedRoot = path.resolve(root);
141
+ const paths = (
142
+ repositories.length > 0
143
+ ? repositories.map((entry) => path.resolve(entry))
144
+ : discoverPaperFleet(resolvedRoot)
145
+ ).sort();
146
+ const entries = paths.map((cwd) =>
147
+ paperFleetEntry({ cwd, buildchainRoot, buildchainVersion, buildchainSha }),
148
+ );
149
+ const governanceEntries = entries.map((entry) => ({
150
+ repository: entry.repository,
151
+ ...(governance[entry.repository] || { status: "unobserved" }),
152
+ }));
153
+ const payload = {
154
+ schemaVersion: 1,
155
+ contract: PAPER_FLEET_AUDIT_CONTRACT,
156
+ ok:
157
+ entries.length > 0 &&
158
+ entries.every((entry) => entry.ok) &&
159
+ governanceEntries.every((entry) =>
160
+ ["pass", "unobserved"].includes(entry.status),
161
+ ),
162
+ root: resolvedRoot,
163
+ runtime: {
164
+ version: buildchainVersion,
165
+ sha: buildchainSha || gitValue(buildchainRoot, ["rev-parse", "HEAD"]),
166
+ },
167
+ summary: {
168
+ repositories: entries.length,
169
+ current: entries.filter((entry) => entry.ok).length,
170
+ drifted: entries.filter((entry) => !entry.ok).length,
171
+ governanceObserved: governanceEntries.filter(
172
+ (entry) => entry.status !== "unobserved",
173
+ ).length,
174
+ },
175
+ repositories: entries,
176
+ governance: governanceEntries,
177
+ };
178
+ return { ...payload, auditRoot: sha256Text(stableJson(payload)) };
179
+ }
180
+
181
+ export function planPaperFleetUpdate(options = {}) {
182
+ const audit = collectPaperFleetAudit(options);
183
+ const plans = audit.repositories.map((entry) => {
184
+ if (!PAPER_WORK_BRANCH_PATTERN.test(entry.branch)) {
185
+ return {
186
+ contract: "kungfu-buildchain-paper-migration",
187
+ ok: false,
188
+ cwd: entry.cwd,
189
+ dryRun: true,
190
+ error:
191
+ "paper fleet update requires an allowed non-protected work branch",
192
+ changes: [],
193
+ };
194
+ }
195
+ try {
196
+ return planPaperMigration({
197
+ cwd: entry.cwd,
198
+ buildchainRoot: options.buildchainRoot,
199
+ buildchainVersion: options.buildchainVersion,
200
+ buildchainSha: options.buildchainSha,
201
+ });
202
+ } catch (error) {
203
+ return {
204
+ contract: "kungfu-buildchain-paper-migration",
205
+ ok: false,
206
+ cwd: entry.cwd,
207
+ dryRun: true,
208
+ error: error.message,
209
+ changes: [],
210
+ };
211
+ }
212
+ });
213
+ const ok = plans.length > 0 && plans.every((entry) => entry.ok);
214
+ return rootedPlan({
215
+ schemaVersion: 1,
216
+ contract: PAPER_FLEET_UPDATE_PLAN_CONTRACT,
217
+ ok,
218
+ root: audit.root,
219
+ dryRun: true,
220
+ auditRoot: audit.auditRoot,
221
+ runtime: audit.runtime,
222
+ plans,
223
+ nextActions: ok
224
+ ? [
225
+ {
226
+ id: "write-fleet-update",
227
+ command: "buildchain paper fleet update --write --json",
228
+ description:
229
+ "Apply only reviewed Buildchain-owned control surfaces, then refresh each pnpm lockfile.",
230
+ },
231
+ ]
232
+ : [
233
+ {
234
+ id: "repair-blocked-repositories",
235
+ command: "git status --short",
236
+ description:
237
+ "Fleet update requires every target repository to be clean and rooted exactly.",
238
+ },
239
+ ],
240
+ });
241
+ }
242
+
243
+ export function writePaperFleetUpdate(plan) {
244
+ if (!plan || plan.contract !== PAPER_FLEET_UPDATE_PLAN_CONTRACT || !plan.ok) {
245
+ return {
246
+ ...plan,
247
+ ok: false,
248
+ dryRun: false,
249
+ results: [],
250
+ errorCode: "paper-fleet-update-blocked",
251
+ };
252
+ }
253
+ const results = plan.plans.map((entry) => writePaperMigration(entry));
254
+ return {
255
+ ...plan,
256
+ ok: results.every((entry) => entry.ok),
257
+ dryRun: false,
258
+ results,
259
+ };
260
+ }