@kungfu-tech/buildchain 4.1.3-alpha.9 → 4.1.4-alpha.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 (205) hide show
  1. package/AGENTS.md +11 -13
  2. package/CONTRIBUTING.md +32 -9
  3. package/README.md +56 -140
  4. package/actions/release/promotion/ref/README.md +1 -1
  5. package/architecture/action-taxonomy.json +1 -1
  6. package/architecture/agent-change-map.md +32 -45
  7. package/architecture/build-orchestration.json +1 -1
  8. package/architecture/capability-state-machine-manifest.json +5 -5
  9. package/architecture/ci-lane-change-budget.json +92 -92
  10. package/architecture/code-layout.json +2 -12
  11. package/architecture/core-mechanisms.json +38 -39
  12. package/architecture/cross-platform-adopter-qualification.json +1 -1
  13. package/architecture/dev-delivery-orchestration.json +2 -2
  14. package/architecture/floating-consumer-policy.json +3 -3
  15. package/architecture/internal-capabilities.json +27 -28
  16. package/architecture/maintainability-debt.json +60 -94
  17. package/architecture/maintainability-policy.json +7 -13
  18. package/architecture/minimal-consumer-migration.json +43 -5014
  19. package/architecture/platform-stage-checkpoints.json +1 -1
  20. package/architecture/release-tail-contract.json +2 -2
  21. package/architecture/release-topology.json +33 -25
  22. package/architecture/stage-capsule-qualification.json +1 -1
  23. package/architecture/universal-workflow-bootstrap.json +60 -60
  24. package/architecture/universal-workflow-capability-policy.json +3 -3
  25. package/architecture/universal-workflow-fault-campaign.json +7 -7
  26. package/architecture/workflow-taxonomy.json +59 -60
  27. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  28. package/dist/site/buildchain-contract.json +138 -66
  29. package/dist/site/buildchain-site.json +257 -331
  30. package/dist/site/capability-registry.json +4 -4
  31. package/dist/site/cli-registry.json +6 -142
  32. package/dist/site/controller-registry.json +19 -19
  33. package/dist/site/kfd-claims.json +481 -529
  34. package/dist/site/kfd-upstream-aggregate.json +1 -1
  35. package/dist/site/manual-registry.json +22 -22
  36. package/dist/site/node-api-registry.json +82 -339
  37. package/dist/site/page-registry.json +229 -304
  38. package/dist/site/public-surface-audit.json +544 -754
  39. package/dist/site/publication-authority-registry.json +316 -316
  40. package/dist/site/publication-registry.json +1 -1
  41. package/dist/site/site-manifest.json +23 -23
  42. package/dist/site/workflow-registry.json +1071 -1027
  43. package/docs/MAP.md +28 -20
  44. package/docs/auditable-demo.md +17 -49
  45. package/docs/bootstrap-recovery.md +46 -37
  46. package/docs/cli-reference.md +2 -46
  47. package/docs/cli.md +97 -186
  48. package/docs/code-organization.md +7 -5
  49. package/docs/dev-alpha-candidate-patrol.md +19 -42
  50. package/docs/dev-delivery-orchestration.md +15 -11
  51. package/docs/dev-delivery-qualification-landing-adr.md +10 -9
  52. package/docs/dev-delivery-warrant.md +17 -16
  53. package/docs/dev-qualification-patrol.md +25 -20
  54. package/docs/getting-started.md +80 -139
  55. package/docs/github-artifact-attestation.md +31 -81
  56. package/docs/install.md +66 -30
  57. package/docs/lifecycle-protocol.md +19 -57
  58. package/docs/migration-inventory.md +17 -45
  59. package/docs/next-development-transition.md +13 -13
  60. package/docs/node-api-reference.md +122 -138
  61. package/docs/oci-publication.md +13 -10
  62. package/docs/publication-artifacts.md +128 -486
  63. package/docs/publication-authority.md +15 -45
  64. package/docs/publish-transaction.md +13 -11
  65. package/docs/release-candidate.md +126 -355
  66. package/docs/release-discussions.md +36 -67
  67. package/docs/release-flow.md +80 -339
  68. package/docs/release-governance.md +114 -962
  69. package/docs/release-promotion-request.md +45 -43
  70. package/docs/release-propagation.md +13 -27
  71. package/docs/release-tail-provider-plane.md +38 -65
  72. package/docs/reusable-build-surface.md +78 -196
  73. package/docs/runtime-train-validation.md +8 -6
  74. package/docs/stable-candidate-patrol.md +13 -66
  75. package/docs/v4-adopter-delivery.md +15 -15
  76. package/docs/v4-declarative-release-tail-migration.md +19 -17
  77. package/docs/v4-production-release.md +11 -6
  78. package/docs/v4-publication-rehearsal.md +12 -27
  79. package/docs/v4-stage-capsule.md +14 -20
  80. package/docs/v4-tail-reseal.md +34 -20
  81. package/docs/web-surface-deployments.md +26 -264
  82. package/docs/workflow-catalog.md +31 -31
  83. package/docs/workflow-path-migration.md +10 -4
  84. package/package.json +5 -4
  85. package/packages/core/adoption/buildchain-kfd-claims.js +7 -7
  86. package/packages/core/adoption/cli/output.mjs +1 -9
  87. package/packages/core/adoption/cli/product-gate.mjs +1 -15
  88. package/packages/core/adoption/cli/project.mjs +9 -10
  89. package/packages/core/adoption/cli/standards.mjs +1 -14
  90. package/packages/core/adoption/cli/support.mjs +0 -2
  91. package/packages/core/adoption/cli/surface-register.mjs +0 -2
  92. package/packages/core/adoption/commands/init-repo.mjs +77 -485
  93. package/packages/core/adoption/consumer-init.js +279 -0
  94. package/packages/core/adoption/qualification/admission.js +1 -1
  95. package/packages/core/build/cli/cache.mjs +0 -2
  96. package/packages/core/build/cli/facts.mjs +0 -3
  97. package/packages/core/build/cli/homebrew.mjs +0 -4
  98. package/packages/core/build/cli/lifecycle.mjs +0 -3
  99. package/packages/core/build/github-artifact-attestation.js +2 -2
  100. package/packages/core/build/platform-stage-checkpoints.js +1 -1
  101. package/packages/core/build/signing/dispatch.js +1 -1
  102. package/packages/core/consumer/buildchain-config.js +9 -0
  103. package/packages/core/consumer/contract/inspection.js +351 -9
  104. package/packages/core/consumer/contract/javascript-syntax.js +439 -0
  105. package/packages/core/consumer/contract/javascript-values.js +61 -0
  106. package/packages/core/consumer/contract/javascript.js +555 -0
  107. package/packages/core/consumer/contract/local-validation.js +84 -0
  108. package/packages/core/contracts/buildchain-contract.js +39 -8
  109. package/packages/core/contracts/cli/introspection.mjs +1 -9
  110. package/packages/core/contracts/public-surface-cli.js +0 -1
  111. package/packages/core/dev-delivery/dev-delivery-contract-surface.js +2 -2
  112. package/packages/core/governance/buildchain-publication-authority.js +24 -24
  113. package/packages/core/governance/cli/doctor.mjs +58 -42
  114. package/packages/core/governance/commands/architecture.mjs +6 -1
  115. package/packages/core/governance/commands/audit-publication-control-plane.mjs +1 -1
  116. package/packages/core/governance/commands/workflow-friction-report.mjs +1 -1
  117. package/packages/core/governance/github-governance-authority.js +2 -2
  118. package/packages/core/governance/publication/collection.js +1 -1
  119. package/packages/core/index.js +1 -3
  120. package/packages/core/observability/cli/candidate.mjs +0 -2
  121. package/packages/core/observability/cli/context.mjs +0 -5
  122. package/packages/core/observability/cli/diagnostics.mjs +0 -7
  123. package/packages/core/observability/cli/events.mjs +2 -10
  124. package/packages/core/paper/commands/paper-work-fleet-cli.mjs +1 -1
  125. package/packages/core/paper/commands/paper.mjs +33 -422
  126. package/packages/core/paper/consumer-status.js +72 -0
  127. package/packages/core/paper/operations/identity.js +0 -3
  128. package/packages/core/paper/operations/migration.js +460 -0
  129. package/packages/core/paper/operations/preflight-checks.js +8 -68
  130. package/packages/core/paper/operations/scaffold.js +16 -326
  131. package/packages/core/paper/operations/status.js +27 -53
  132. package/packages/core/paper/paper-agent-entry.js +36 -147
  133. package/packages/core/paper/paper-fleet.js +24 -39
  134. package/packages/core/paper/paper-repository.js +16 -0
  135. package/packages/core/paper/paper-runtime-channels.js +1 -1
  136. package/packages/core/paper/paper-scaffold-content.js +153 -162
  137. package/packages/core/paper/paper-work.js +3 -10
  138. package/packages/core/paper/paper.js +43 -5
  139. package/packages/core/providers/github/artifact-attestation.js +1 -1
  140. package/packages/core/providers/github/pipeline-context-artifacts.js +172 -0
  141. package/packages/core/providers/github/pipeline-release-evidence.js +17 -98
  142. package/packages/core/providers/github/pipeline-stable-baseline.js +86 -0
  143. package/packages/core/providers/github/pipeline-stable-source.js +22 -4
  144. package/packages/core/providers/github/release-evidence-assets.js +97 -0
  145. package/packages/core/publication/authority/control-plane.js +2 -2
  146. package/packages/core/publication/authority/seal.js +1 -1
  147. package/packages/core/publication/cli/dispatch.mjs +1 -12
  148. package/packages/core/publication/pipeline/actions.js +16 -9
  149. package/packages/core/publication/pipeline/apply.js +17 -9
  150. package/packages/core/publication/pipeline/context.js +29 -0
  151. package/packages/core/publication/pipeline/journal.js +13 -1
  152. package/packages/core/publication/pipeline/prepare.js +15 -6
  153. package/packages/core/publication/pipeline/recovery-import.js +1 -1
  154. package/packages/core/publication/pipeline/recovery-materials.js +63 -1
  155. package/packages/core/publication/pipeline/recovery-prepare.js +21 -9
  156. package/packages/core/publication/pipeline/stable-entry.js +12 -3
  157. package/packages/core/publication/pipeline/stable-products.js +21 -19
  158. package/packages/core/publication/pipeline/version-actions.js +15 -6
  159. package/packages/core/publication/publication-rehearsal-projection.js +0 -9
  160. package/packages/core/release/cli/dispatch.mjs +1 -4
  161. package/packages/core/release/cli/inspection-handlers.mjs +1 -33
  162. package/packages/core/release/cli/release-handlers.mjs +2 -19
  163. package/packages/core/release/cli/verification-handlers.mjs +4 -25
  164. package/packages/core/release/next-development-projection.js +13 -13
  165. package/packages/core/release/passport/locations.js +13 -8
  166. package/packages/core/release/release-passport.js +70 -5
  167. package/packages/core/web/cli/badges.mjs +0 -4
  168. package/packages/core/web/publication-admission.js +1 -1
  169. package/packages/core/web/publication-capability.js +1 -1
  170. package/packages/core/web/readme-badges.js +1 -1
  171. package/packages/core/workflow/cli/main.mjs +1 -4
  172. package/packages/core/workflow/cli/process.mjs +1 -1
  173. package/packages/core/workflow/commands/buildchain-cli-help.mjs +2 -8
  174. package/packages/core/workflow/engine/conformance.js +1 -1
  175. package/packages/core/workflow/pipeline/build.js +21 -1
  176. package/packages/core/workflow/pipeline/delivery-observation.js +6 -2
  177. package/packages/core/workflow/workflow-taxonomy.mjs +61 -17
  178. package/scripts/check-code-layout.mjs +2 -0
  179. package/scripts/check-core-mechanism-inventory.mjs +29 -13
  180. package/scripts/check-floating-consumer-policy-contract.mjs +3 -3
  181. package/scripts/check-inventory.mjs +27 -27
  182. package/scripts/check-maintainability.mjs +28 -5
  183. package/scripts/check-public-dogfood-contract.mjs +3 -3
  184. package/scripts/check-release-topology.mjs +1 -1
  185. package/scripts/check-self-consumer-contract.mjs +7 -7
  186. package/scripts/check-universal-workflow-bootstrap.mjs +1 -1
  187. package/scripts/generate-channel-promotion-workflow.mjs +1 -1
  188. package/scripts/generate-minimal-consumer-contract.mjs +21 -1
  189. package/scripts/generate-site-bundle.mjs +2 -1
  190. package/scripts/generate-tail-reseal-fixture.mjs +3 -3
  191. package/scripts/inventory/promotion.mjs +1 -1
  192. package/scripts/next-development-self-dogfood-harness.mjs +1 -1
  193. package/scripts/next-development-self-dogfood.mjs +4 -4
  194. package/scripts/site-capability-metadata.mjs +2 -6
  195. package/scripts/source-metric-lineage.mjs +62 -7
  196. package/scripts/verify-golden-path.mjs +31 -35
  197. package/templates/native-dev-delivery.yml +1 -1
  198. package/templates/universal-buildchain-bootstrap.yml +1 -1
  199. package/packages/core/paper/operations/bootstrap.js +0 -75
  200. package/packages/core/paper/operations/plans.js +0 -213
  201. package/packages/core/paper/operations/provisioning.js +0 -113
  202. package/packages/core/paper/operations/scaffold-config.js +0 -77
  203. package/packages/core/paper/operations/scaffold-workflows.js +0 -116
  204. package/packages/core/paper/operations/workspace.js +0 -60
  205. package/packages/core/paper/paper-npm-bootstrap.js +0 -492
@@ -0,0 +1,460 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { parse, stringify } from "smol-toml";
4
+ import YAML from "yaml";
5
+ import {
6
+ consumerAgentInstructions,
7
+ consumerConfiguration,
8
+ } from "../../adoption/consumer-init.js";
9
+ import { consumerWorkflows } from "../../consumer/contract/entries.js";
10
+ import { compileConsumerPlan } from "../../consumer/contract/plan.js";
11
+ import { validateConsumerWiring } from "../../consumer/contract/local-validation.js";
12
+ import {
13
+ loadBuildchainConfig,
14
+ validateBuildchainConfig,
15
+ } from "../../consumer/buildchain-config.js";
16
+ import {
17
+ gitResult,
18
+ gitValue,
19
+ PAPER_PATHS,
20
+ sha256Text,
21
+ stableJson,
22
+ } from "../paper-repository.js";
23
+ import { buildchainPackageIdentity } from "./runtime.js";
24
+ import {
25
+ BUILDCHAIN_PACKAGE_NAME,
26
+ PAPER_MIGRATION_CONTRACT,
27
+ } from "./identity.js";
28
+ import { jsonText } from "./files.js";
29
+
30
+ const LEGACY_WORKFLOWS = new Map([
31
+ ["build.yml", ["public-build-publication.yml", ".build-publication.yml"]],
32
+ [
33
+ ".build-candidate.yml",
34
+ ["public-build-publication.yml", ".build-publication.yml"],
35
+ ],
36
+ ["verify.yml", ["public-build-check.yml", ".build-check.yml"]],
37
+ [
38
+ "public-release-paper.yml",
39
+ ["public-release-paper.yml", ".release-paper.yml"],
40
+ ],
41
+ [".release-paper.yml", ["public-release-paper.yml", ".release-paper.yml"]],
42
+ ]);
43
+ const LEGACY_SCRIPTS = {
44
+ "buildchain:paper": "buildchain paper",
45
+ "paper:preflight": "buildchain paper preflight --json",
46
+ "paper:agent:verify": "buildchain paper agent verify --json",
47
+ "paper:work:start": "buildchain paper work start",
48
+ "paper:work:submit": "buildchain paper work submit",
49
+ "paper:status": "buildchain paper status --json",
50
+ };
51
+
52
+ export function paperFileTarget(cwd, relative) {
53
+ const parts = relative.split("/");
54
+ for (let index = 1; index <= parts.length; index++) {
55
+ const target = path.join(cwd, ...parts.slice(0, index));
56
+ const stat = fs.lstatSync(target, { throwIfNoEntry: false });
57
+ if (
58
+ stat &&
59
+ (stat.isSymbolicLink() ||
60
+ (index < parts.length ? !stat.isDirectory() : !stat.isFile()))
61
+ )
62
+ throw new Error(
63
+ `${relative}: Paper scaffold requires regular files and directories`,
64
+ );
65
+ }
66
+ return path.join(cwd, relative);
67
+ }
68
+
69
+ function regularSource(cwd, relative) {
70
+ const absolute = paperFileTarget(cwd, relative);
71
+ return fs.existsSync(absolute)
72
+ ? fs.readFileSync(absolute, "utf8")
73
+ : undefined;
74
+ }
75
+
76
+ function retireWorkflows(cwd, files) {
77
+ regularSource(cwd, ".github/workflows/buildchain.yml");
78
+ const directory = path.join(cwd, ".github/workflows");
79
+ if (!fs.existsSync(directory)) return;
80
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
81
+ if (!/\.ya?ml$/iu.test(entry.name) && entry.isFile()) continue;
82
+ const relative = `.github/workflows/${entry.name}`;
83
+ const source = regularSource(cwd, relative);
84
+ if (files.has(relative)) {
85
+ if (source !== files.get(relative))
86
+ throw new Error(`Existing shared caller differs: ${relative}`);
87
+ continue;
88
+ }
89
+ const callees = LEGACY_WORKFLOWS.get(entry.name);
90
+ const workflow = YAML.parse(source);
91
+ const jobs = Object.values(workflow?.jobs || {});
92
+ if (
93
+ !callees ||
94
+ !jobs.length ||
95
+ jobs.some((job) => {
96
+ const match =
97
+ typeof job?.uses === "string" &&
98
+ /^kungfu-systems\/buildchain\/\.github\/workflows\/([^@]+)@(v[34](?:-alpha)?|[0-9a-f]{40})$/u.exec(
99
+ job.uses,
100
+ );
101
+ return (
102
+ !match ||
103
+ !callees.includes(match[1]) ||
104
+ Object.hasOwn(job, "steps") ||
105
+ Object.hasOwn(job, "runs-on")
106
+ );
107
+ })
108
+ )
109
+ throw new Error(
110
+ `Unowned or customized Paper workflow requires explicit product migration: ${relative}`,
111
+ );
112
+ files.set(relative, null);
113
+ }
114
+ }
115
+
116
+ function retireControl(cwd, files, relative, contract, digestKey) {
117
+ const source = regularSource(cwd, relative);
118
+ if (source === undefined) return;
119
+ const value = JSON.parse(source);
120
+ const { [digestKey]: digest, ...payload } = value;
121
+ if (value.contract !== contract || digest !== sha256Text(stableJson(payload)))
122
+ throw new Error(
123
+ `Cannot retire an unverified Paper control file: ${relative}`,
124
+ );
125
+ files.set(relative, null);
126
+ }
127
+
128
+ function productCommands(config, stage) {
129
+ const lifecycle = config.lifecycle || {};
130
+ const value = lifecycle[stage];
131
+ if (
132
+ !value?.commands?.length ||
133
+ value.shell ||
134
+ Object.keys(value.env || {}).length ||
135
+ Object.keys(lifecycle.env || {}).length ||
136
+ value.timeoutMinutes ||
137
+ value.retries !== 1
138
+ )
139
+ throw new Error(
140
+ `Move ${stage} shell, environment, retry and script settings into product commands before Paper migration`,
141
+ );
142
+ return value.commands;
143
+ }
144
+
145
+ function migratedPackage(current, config, runtimeVersion) {
146
+ if (current.version && current.version !== config.publication.version)
147
+ throw new Error(
148
+ "Paper package and publication versions disagree; migration cannot choose a version authority",
149
+ );
150
+ const scripts = { ...(current.scripts || {}) };
151
+ for (const [name, command] of Object.entries(LEGACY_SCRIPTS)) {
152
+ if (scripts[name] !== undefined && scripts[name] !== command)
153
+ throw new Error(
154
+ `Customized managed Paper script requires explicit migration: ${name}`,
155
+ );
156
+ delete scripts[name];
157
+ }
158
+ return {
159
+ ...current,
160
+ private: true,
161
+ version: config.publication.version,
162
+ description: current.description || config.publication.title,
163
+ scripts,
164
+ devDependencies: {
165
+ ...(current.devDependencies || {}),
166
+ "@kungfu-tech/buildchain": runtimeVersion,
167
+ },
168
+ };
169
+ }
170
+
171
+ export function migrationFiles({ cwd, buildchainRoot, buildchainVersion }) {
172
+ regularSource(cwd, PAPER_PATHS.config);
173
+ const loaded = loadBuildchainConfig(cwd);
174
+ const instructions = consumerAgentInstructions(
175
+ regularSource(cwd, "AGENTS.md") || "",
176
+ );
177
+ if (loaded?.config.schema === 2) {
178
+ validateBuildchainConfig(cwd, {
179
+ requireLifecycleStages: ["build", "verify"],
180
+ });
181
+ if (!loaded.config.products.some((product) => product.type === "paper"))
182
+ throw new Error("Paper migration requires a Paper product");
183
+ validateConsumerWiring(cwd, loaded.path);
184
+ const files = new Map([["AGENTS.md", instructions]]);
185
+ const packageText = regularSource(cwd, "package.json");
186
+ const sourcePackage =
187
+ packageText === undefined ? null : JSON.parse(packageText);
188
+ if (sourcePackage?.devDependencies?.["@kungfu-tech/buildchain"]) {
189
+ const version = buildchainPackageIdentity(
190
+ buildchainRoot,
191
+ buildchainVersion,
192
+ ).version;
193
+ if (!/^4\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version))
194
+ throw new Error("Paper migration requires an exact v4 CLI dependency");
195
+ sourcePackage.devDependencies["@kungfu-tech/buildchain"] = version;
196
+ files.set("package.json", jsonText(sourcePackage));
197
+ const workspace = regularSource(cwd, PAPER_PATHS.pnpmWorkspace);
198
+ if (workspace !== undefined)
199
+ files.set(
200
+ PAPER_PATHS.pnpmWorkspace,
201
+ paperPnpmWorkspace(workspace, version),
202
+ );
203
+ }
204
+ return files;
205
+ }
206
+ const config = loaded?.config;
207
+ if (
208
+ config?.project?.type !== "publication-artifact" ||
209
+ config.publication?.kind !== "paper"
210
+ )
211
+ throw new Error(
212
+ "Paper migration requires a schema-1 Paper publication or a schema-2 Paper product",
213
+ );
214
+ const raw = parse(regularSource(cwd, PAPER_PATHS.config));
215
+ const allowed = new Set([
216
+ "schema",
217
+ "project",
218
+ "publication",
219
+ "publish",
220
+ "lifecycle",
221
+ "next_development",
222
+ ]);
223
+ if (Object.keys(raw).some((key) => !allowed.has(key)))
224
+ throw new Error(
225
+ "Paper migration cannot discard additional configuration sections; move them into product sources first",
226
+ );
227
+ const current = JSON.parse(regularSource(cwd, "package.json") || "null");
228
+ if (!current || Array.isArray(current) || typeof current !== "object")
229
+ throw new Error("Paper migration requires a valid source package.json");
230
+ const runtimeVersion = buildchainPackageIdentity(
231
+ buildchainRoot,
232
+ buildchainVersion,
233
+ ).version;
234
+ if (!/^4\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(runtimeVersion))
235
+ throw new Error("Paper migration requires an exact v4 CLI dependency");
236
+ const plan = parse(
237
+ consumerConfiguration({
238
+ type: "paper",
239
+ version: config.publication.version,
240
+ }),
241
+ );
242
+ const product = plan.products[0];
243
+ product.build = productCommands(config, "build");
244
+ product.verify = productCommands(config, "verify");
245
+ if (config.lifecycle.install)
246
+ product.install = productCommands(config, "install");
247
+ if (
248
+ Object.keys(raw.lifecycle || {}).some(
249
+ (key) => !["build", "verify", "install"].includes(key),
250
+ )
251
+ )
252
+ throw new Error(
253
+ "Paper migration cannot retain consumer release lifecycle hooks or global shell settings",
254
+ );
255
+ product.artifacts = config.publication.artifactPaths.map(
256
+ (artifact, index) => ({
257
+ id: index ? `pdf-${index + 1}` : "main",
258
+ path: artifact,
259
+ kind: "pdf",
260
+ }),
261
+ );
262
+ if (product.artifacts.some((artifact) => !artifact.path.endsWith(".pdf")))
263
+ throw new Error("Paper migration requires explicit PDF artifacts");
264
+ product.targets[0].artifacts = product.artifacts.map(
265
+ (artifact) => artifact.id,
266
+ );
267
+ const toml = stringify(plan);
268
+ compileConsumerPlan(toml);
269
+ const { version: _version, ...metadata } = raw.publication;
270
+ const metadataPath = "paper/publication-metadata.json";
271
+ const metadataBytes = jsonText({
272
+ schema: "buildchain.paper-product-metadata/v1",
273
+ ...metadata,
274
+ });
275
+ const existingMetadata = regularSource(cwd, metadataPath);
276
+ if (existingMetadata !== undefined && existingMetadata !== metadataBytes)
277
+ throw new Error(
278
+ `Paper migration would overwrite product metadata: ${metadataPath}`,
279
+ );
280
+ const files = new Map([
281
+ [PAPER_PATHS.config, toml],
282
+ [
283
+ "package.json",
284
+ jsonText(migratedPackage(current, config, runtimeVersion)),
285
+ ],
286
+ ["AGENTS.md", instructions],
287
+ [metadataPath, metadataBytes],
288
+ ...Object.entries(consumerWorkflows()),
289
+ ]);
290
+ retireWorkflows(cwd, files);
291
+ retireControl(
292
+ cwd,
293
+ files,
294
+ PAPER_PATHS.provisioningAuthority,
295
+ "kungfu-buildchain-paper-provisioning-authority",
296
+ "authorityDigest",
297
+ );
298
+ retireControl(
299
+ cwd,
300
+ files,
301
+ PAPER_PATHS.agentEntry,
302
+ "kungfu-buildchain-paper-agent-entry",
303
+ "entryDigest",
304
+ );
305
+ const pin = regularSource(cwd, PAPER_PATHS.versionPin);
306
+ if (pin !== undefined) {
307
+ if (!/^[34]\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?\s*$/u.test(pin))
308
+ throw new Error("Unrecognized Paper runtime pin cannot be retired");
309
+ files.set(PAPER_PATHS.versionPin, null);
310
+ }
311
+ const workspace = regularSource(cwd, PAPER_PATHS.pnpmWorkspace);
312
+ if (workspace !== undefined)
313
+ files.set(
314
+ PAPER_PATHS.pnpmWorkspace,
315
+ paperPnpmWorkspace(workspace, runtimeVersion),
316
+ );
317
+ // Existing channel locks and publication receipts are preserved byte-for-byte.
318
+ return files;
319
+ }
320
+
321
+ function paperPnpmWorkspace(current, buildchainVersion) {
322
+ const entry = `${BUILDCHAIN_PACKAGE_NAME}@${buildchainVersion}`;
323
+ const source = String(current || "");
324
+ const lines = source ? source.replace(/\r\n/g, "\n").split("\n") : [];
325
+ if (lines.at(-1) === "") lines.pop();
326
+ const keyLines = lines
327
+ .map((line, index) =>
328
+ /^minimumReleaseAgeExclude:\s*(?:#.*)?$/.test(line) ? index : -1,
329
+ )
330
+ .filter((index) => index >= 0);
331
+ const unsupportedKey = lines.some(
332
+ (line) =>
333
+ /^minimumReleaseAgeExclude\s*:/.test(line) &&
334
+ !/^minimumReleaseAgeExclude:\s*(?:#.*)?$/.test(line),
335
+ );
336
+ if (unsupportedKey || keyLines.length > 1) {
337
+ throw new Error(
338
+ "paper migration requires minimumReleaseAgeExclude to be one top-level block sequence",
339
+ );
340
+ }
341
+ if (keyLines.length === 0) {
342
+ const prefix = lines.length > 0 ? [...lines, ""] : [];
343
+ return `${[...prefix, "minimumReleaseAgeExclude:", ` - '${entry}'`].join(
344
+ "\n",
345
+ )}\n`;
346
+ }
347
+ const keyIndex = keyLines[0];
348
+ let blockEnd = lines.length;
349
+ for (let index = keyIndex + 1; index < lines.length; index += 1) {
350
+ if (/^[^\s#]/.test(lines[index])) {
351
+ blockEnd = index;
352
+ break;
353
+ }
354
+ }
355
+ const retained = [];
356
+ for (const line of lines.slice(keyIndex + 1, blockEnd)) {
357
+ if (!line.trim() || /^\s*#/.test(line)) {
358
+ retained.push(line);
359
+ continue;
360
+ }
361
+ const item = line.match(/^\s*-\s+(.+?)\s*(?:#.*)?$/);
362
+ if (!item) {
363
+ throw new Error(
364
+ "paper migration requires minimumReleaseAgeExclude to contain scalar package entries",
365
+ );
366
+ }
367
+ const value = item[1]
368
+ .trim()
369
+ .replace(/^'(.*)'$/, "$1")
370
+ .replace(/^"(.*)"$/, "$1");
371
+ if (!value.startsWith(`${BUILDCHAIN_PACKAGE_NAME}@`)) retained.push(line);
372
+ }
373
+ return `${[
374
+ ...lines.slice(0, keyIndex + 1),
375
+ ...retained,
376
+ ` - '${entry}'`,
377
+ ...lines.slice(blockEnd),
378
+ ].join("\n")}\n`;
379
+ }
380
+
381
+ export function applyPaperMigration(plan) {
382
+ if (!plan || plan.contract !== PAPER_MIGRATION_CONTRACT) {
383
+ throw new Error("paper migration plan contract mismatch");
384
+ }
385
+ if (!plan.ok) {
386
+ return {
387
+ ...plan,
388
+ dryRun: false,
389
+ written: [],
390
+ updated: [],
391
+ removed: [],
392
+ ok: false,
393
+ errorCode: "paper-migration-blocked",
394
+ };
395
+ }
396
+ if (fs.realpathSync(plan.cwd) !== plan.cwd)
397
+ throw new Error("Paper migration root changed after planning");
398
+ if (
399
+ gitValue(plan.cwd, ["rev-parse", "HEAD"]) !== plan.source.head ||
400
+ gitResult(plan.cwd, ["status", "--porcelain"]).stdout !== ""
401
+ )
402
+ throw new Error(
403
+ "paper migration source changed after planning; no stale plan was applied",
404
+ );
405
+ for (const entry of plan._plannedFiles) paperFileTarget(plan.cwd, entry.path);
406
+ const written = [];
407
+ const updated = [];
408
+ const removed = [];
409
+ for (const entry of plan._plannedFiles) {
410
+ if (entry.action === "unchanged") continue;
411
+ const target = path.resolve(plan.cwd, entry.path);
412
+ const exists = fs.existsSync(target);
413
+ const current =
414
+ exists && fs.statSync(target).isFile()
415
+ ? fs.readFileSync(target, "utf8")
416
+ : undefined;
417
+ const currentSha256 = current === undefined ? "" : sha256Text(current);
418
+ if (
419
+ (entry.action === "create" && exists) ||
420
+ (["update", "remove"].includes(entry.action) &&
421
+ currentSha256 !== entry.currentSha256)
422
+ ) {
423
+ throw new Error(
424
+ `paper migration race detected at ${entry.path}; no stale plan was applied`,
425
+ );
426
+ }
427
+ }
428
+ for (const entry of plan._plannedFiles) {
429
+ if (entry.action === "unchanged") continue;
430
+ const target = path.resolve(plan.cwd, entry.path);
431
+ if (entry.action === "remove") {
432
+ fs.unlinkSync(target);
433
+ removed.push(entry.path);
434
+ continue;
435
+ }
436
+ fs.mkdirSync(path.dirname(target), { recursive: true });
437
+ fs.writeFileSync(target, entry.content, {
438
+ flag: entry.action === "create" ? "wx" : "w",
439
+ });
440
+ (entry.action === "create" ? written : updated).push(entry.path);
441
+ }
442
+ return {
443
+ ...plan,
444
+ ok: true,
445
+ dryRun: false,
446
+ written,
447
+ updated,
448
+ removed,
449
+ idempotent:
450
+ written.length === 0 && updated.length === 0 && removed.length === 0,
451
+ nextActions: [
452
+ {
453
+ id: "paper-preflight",
454
+ command: `buildchain paper preflight --cwd ${JSON.stringify(plan.cwd)} --offline --json`,
455
+ description:
456
+ "Verify the migrated repository before any external mutation.",
457
+ },
458
+ ],
459
+ };
460
+ }
@@ -197,73 +197,13 @@ export function paperPreflightPublicationChecks({ npm, status }) {
197
197
  },
198
198
  ];
199
199
  }
200
- export function paperPreflightNextActions({
201
- validationError,
202
- source,
203
- lockEvaluation,
204
- npm,
205
- repositoryActions,
206
- generatedWriteAuthority,
207
- status,
208
- }) {
209
- const actions = [];
210
- if (validationError) {
211
- actions.push({
212
- id: "repair-config",
213
- command: "buildchain validate --require-lifecycle-stages verify",
214
- description: validationError,
215
- });
216
- }
217
- if (!source.clean) {
218
- actions.push({
219
- id: "commit-source",
220
- command: "git status --short",
221
- description:
222
- "Review and commit the exact source before deterministic publication.",
223
- });
224
- }
225
- if (!lockEvaluation.compatible) {
226
- actions.push({
227
- id: "refresh-contract-lock",
228
- command: "buildchain paper scaffold --json",
229
- description:
230
- "Review the current runtime contract and resolve the contract-lock difference without overwriting repository files.",
231
- });
232
- }
233
- if (npm.package.exists === false || npm.trust.configured === false) {
234
- actions.push({
235
- id: "bootstrap-npm",
236
- command: "buildchain paper bootstrap npm --json",
237
- description:
238
- "Run the public-package bootstrap and Trusted Publishing dry-run.",
239
- });
240
- }
241
- if (
242
- repositoryActions.status === "observed" &&
243
- (repositoryActions.defaultWorkflowPermissions !== "read" ||
244
- repositoryActions.canApprovePullRequestReviews !== false)
245
- ) {
246
- actions.push({
247
- id: "constrain-repository-actions",
248
- command: "",
249
- description:
250
- "Set default workflow permissions to read and disable Actions pull-request approval through the repository provisioner.",
251
- });
252
- }
253
- if (generatedWriteAuthority.configured === false) {
254
- actions.push({
255
- id: "configure-generated-write-authority",
256
- command: "",
200
+ export function paperPreflightNextActions() {
201
+ return [
202
+ {
203
+ id: "migrate-consumer-contract",
204
+ command: "buildchain paper migrate --json",
257
205
  description:
258
- "Install a least-privilege GitHub App or configure an equivalent narrow generated-write token without exposing its value.",
259
- });
260
- }
261
- if (status.deterministicBuild.status !== "qualifying") {
262
- actions.push({
263
- id: "build-paper",
264
- command: "buildchain paper build --execute --json",
265
- description: "Run the existing qualifying two-clean-build gate.",
266
- });
267
- }
268
- return actions;
206
+ "Review migration of this legacy Paper repository to schema 2 and the shared normal/recovery caller pair. Historical receipts remain unchanged.",
207
+ },
208
+ ];
269
209
  }