@kungfu-tech/buildchain 3.0.4-alpha.2 → 3.0.4-alpha.3

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 (49) hide show
  1. package/bin/buildchain.mjs +126 -72
  2. package/bin/internal/trust-release-cli.mjs +15 -537
  3. package/bin/internal/trust-release-command-handlers.mjs +14 -0
  4. package/bin/internal/trust-release-inspection-handlers.mjs +175 -0
  5. package/bin/internal/trust-release-release-handlers.mjs +317 -0
  6. package/bin/internal/trust-release-verification-handlers.mjs +306 -0
  7. package/dist/site/buildchain-contract.json +35 -24
  8. package/dist/site/buildchain-site.json +22 -10
  9. package/dist/site/controller-registry.json +16 -3
  10. package/dist/site/kfd-claims.json +9 -7
  11. package/dist/site/kfd-upstream-aggregate.json +1 -1
  12. package/dist/site/manual-registry.json +2 -2
  13. package/dist/site/node-api-registry.json +7 -7
  14. package/dist/site/page-registry.json +9 -4
  15. package/dist/site/public-surface-audit.json +56 -8
  16. package/dist/site/publication-registry.json +4 -4
  17. package/dist/site/release-model.json +7 -0
  18. package/dist/site/site-manifest.json +6 -6
  19. package/dist/site/workflow-registry.json +13 -5
  20. package/docs/MAP.md +1 -1
  21. package/docs/release-propagation.md +166 -8
  22. package/package.json +1 -1
  23. package/packages/core/buildchain-kfd-claims.js +1 -1
  24. package/packages/core/controller-evidence.js +8 -1
  25. package/packages/core/index.js +1 -13
  26. package/packages/core/paper-npm-bootstrap.js +492 -0
  27. package/packages/core/paper.js +271 -669
  28. package/packages/core/public-surface-cli.js +12 -1
  29. package/packages/core/release-passport.js +67 -71
  30. package/packages/core/release-propagation-common.js +64 -0
  31. package/packages/core/release-propagation-execution-profile.js +59 -0
  32. package/packages/core/release-propagation-release.js +196 -0
  33. package/packages/core/release-propagation-stage-evidence.js +364 -0
  34. package/packages/core/release-propagation-work-capture.js +64 -0
  35. package/packages/core/release-propagation-work-constants.js +34 -0
  36. package/packages/core/release-propagation-work-control.js +203 -0
  37. package/packages/core/release-propagation-work-transitions.js +145 -0
  38. package/packages/core/release-propagation-work.js +517 -0
  39. package/packages/core/release-propagation.js +34 -158
  40. package/scripts/aws-windows-jit-controller-core.mjs +269 -0
  41. package/scripts/aws-windows-jit-controller.mjs +502 -0
  42. package/scripts/check-internal-architecture.mjs +178 -52
  43. package/scripts/check-maintainability.mjs +76 -17
  44. package/scripts/generate-site-bundle.mjs +16 -7
  45. package/scripts/maintainability-metrics.mjs +24 -4
  46. package/scripts/release-propagation.mjs +126 -0
  47. package/scripts/resolve-artifact-transfer-mode.mjs +117 -0
  48. package/scripts/web-surface-core.mjs +46 -207
  49. package/scripts/web-surface-routing.mjs +286 -0
@@ -45,6 +45,7 @@ import {
45
45
  scaffoldPackageJson,
46
46
  scaffoldReadme,
47
47
  } from "./paper-scaffold-content.js";
48
+ import { executePaperNpmBootstrap as executePaperNpmBootstrapOperation } from "./paper-npm-bootstrap.js";
48
49
 
49
50
  export { PAPER_PATHS, resolvePaperRepository } from "./paper-repository.js";
50
51
  export {
@@ -1230,8 +1231,7 @@ function releaseTransactionFacts(candidates, version) {
1230
1231
  };
1231
1232
  }
1232
1233
 
1233
- export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1234
- const resolvedCwd = path.resolve(cwd);
1234
+ function collectPaperGovernanceFacts(resolvedCwd) {
1235
1235
  const configResult = paperConfig(resolvedCwd);
1236
1236
  const loaded = configResult.loaded;
1237
1237
  const publication = loaded?.config?.publication;
@@ -1272,7 +1272,13 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1272
1272
  gitRepo.ok &&
1273
1273
  gitRepo.stdout === "true",
1274
1274
  );
1275
+ return {
1276
+ configResult, loaded, publication, packageName, version, configFact,
1277
+ scaffoldFacts, contractLockError, scaffoldOk, governedOk,
1278
+ };
1279
+ }
1275
1280
 
1281
+ function collectPaperEvidenceFacts(resolvedCwd, publication, version) {
1276
1282
  const admissionInputs = [PAPER_PATHS.capability, PAPER_PATHS.admission].map(
1277
1283
  (relativePath) => ({
1278
1284
  relativePath,
@@ -1330,6 +1336,82 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1330
1336
  visibility.value,
1331
1337
  "production",
1332
1338
  );
1339
+ return {
1340
+ admitted, bootstrapValue, trustValue, content, reproducibility, deterministic,
1341
+ sealedOk, sealedError, candidates, transactionFacts, packagePublished,
1342
+ alphaComplete, stagingVisible, productionVisible,
1343
+ };
1344
+ }
1345
+
1346
+ function paperStatusBlockingNextActions({ stateMap, deterministic, transactionFacts }) {
1347
+ const actions = [];
1348
+ if (!stateMap.scaffolded.satisfied) {
1349
+ actions.push({
1350
+ id: "scaffold-paper",
1351
+ command: "buildchain paper scaffold --help",
1352
+ description: "Plan a no-overwrite Buildchain paper scaffold.",
1353
+ });
1354
+ } else if (!stateMap.governed.satisfied) {
1355
+ actions.push({
1356
+ id: "restore-governance",
1357
+ command: "buildchain paper preflight --json",
1358
+ description:
1359
+ "Repair the Git/contract-lock governance checks reported by preflight.",
1360
+ });
1361
+ } else if (!stateMap["content-ready"].satisfied) {
1362
+ actions.push({
1363
+ id: "complete-paper-content",
1364
+ command: "make check",
1365
+ description: "Add every declared paper source and metadata input.",
1366
+ });
1367
+ } else if (!deterministic) {
1368
+ actions.push({
1369
+ id: "build-reproducible-artifact",
1370
+ command: "buildchain paper build --execute --json",
1371
+ description:
1372
+ "Run the existing two-clean-build reproducibility gate and promote exact bytes.",
1373
+ });
1374
+ } else if (
1375
+ !stateMap.bootstrapped.satisfied ||
1376
+ !stateMap["trust-bound"].satisfied
1377
+ ) {
1378
+ actions.push({
1379
+ id: "bootstrap-npm",
1380
+ command: "buildchain paper bootstrap npm --json",
1381
+ description:
1382
+ "Inspect the dry-run npm bootstrap and Trusted Publishing handoff.",
1383
+ });
1384
+ } else if (!stateMap["alpha-complete"].satisfied) {
1385
+ actions.push({
1386
+ id: transactionFacts.selected ? "resume-alpha" : "start-alpha",
1387
+ command: transactionFacts.selected
1388
+ ? "buildchain paper resume --json"
1389
+ : "buildchain paper alpha --json",
1390
+ description: transactionFacts.selected
1391
+ ? "Resume the exact sealed release transaction."
1392
+ : "Plan the protected dev-to-alpha publication PR.",
1393
+ });
1394
+ }
1395
+ return actions;
1396
+ }
1397
+
1398
+ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1399
+ const resolvedCwd = path.resolve(cwd);
1400
+ const governance = collectPaperGovernanceFacts(resolvedCwd);
1401
+ const evidence = collectPaperEvidenceFacts(
1402
+ resolvedCwd,
1403
+ governance.publication,
1404
+ governance.version,
1405
+ );
1406
+ const {
1407
+ configResult, loaded, publication, packageName, version, configFact,
1408
+ scaffoldFacts, contractLockError, scaffoldOk, governedOk,
1409
+ } = governance;
1410
+ const {
1411
+ admitted, bootstrapValue, trustValue, content, reproducibility, deterministic,
1412
+ sealedOk, sealedError, candidates, transactionFacts, packagePublished,
1413
+ alphaComplete, stagingVisible, productionVisible,
1414
+ } = evidence;
1333
1415
 
1334
1416
  const states = [
1335
1417
  state(
@@ -1439,54 +1521,7 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1439
1521
  const highestEvidenceState =
1440
1522
  [...states].reverse().find((entry) => entry.satisfied)?.id || "none";
1441
1523
  const stateMap = Object.fromEntries(states.map((entry) => [entry.id, entry]));
1442
- const blockingNextActions = [];
1443
- if (!stateMap.scaffolded.satisfied) {
1444
- blockingNextActions.push({
1445
- id: "scaffold-paper",
1446
- command: "buildchain paper scaffold --help",
1447
- description: "Plan a no-overwrite Buildchain paper scaffold.",
1448
- });
1449
- } else if (!stateMap.governed.satisfied) {
1450
- blockingNextActions.push({
1451
- id: "restore-governance",
1452
- command: "buildchain paper preflight --json",
1453
- description:
1454
- "Repair the Git/contract-lock governance checks reported by preflight.",
1455
- });
1456
- } else if (!stateMap["content-ready"].satisfied) {
1457
- blockingNextActions.push({
1458
- id: "complete-paper-content",
1459
- command: "make check",
1460
- description: "Add every declared paper source and metadata input.",
1461
- });
1462
- } else if (!deterministic) {
1463
- blockingNextActions.push({
1464
- id: "build-reproducible-artifact",
1465
- command: "buildchain paper build --execute --json",
1466
- description:
1467
- "Run the existing two-clean-build reproducibility gate and promote exact bytes.",
1468
- });
1469
- } else if (
1470
- !stateMap.bootstrapped.satisfied ||
1471
- !stateMap["trust-bound"].satisfied
1472
- ) {
1473
- blockingNextActions.push({
1474
- id: "bootstrap-npm",
1475
- command: "buildchain paper bootstrap npm --json",
1476
- description:
1477
- "Inspect the dry-run npm bootstrap and Trusted Publishing handoff.",
1478
- });
1479
- } else if (!stateMap["alpha-complete"].satisfied) {
1480
- blockingNextActions.push({
1481
- id: transactionFacts.selected ? "resume-alpha" : "start-alpha",
1482
- command: transactionFacts.selected
1483
- ? "buildchain paper resume --json"
1484
- : "buildchain paper alpha --json",
1485
- description: transactionFacts.selected
1486
- ? "Resume the exact sealed release transaction."
1487
- : "Plan the protected dev-to-alpha publication PR.",
1488
- });
1489
- }
1524
+ const blockingNextActions = paperStatusBlockingNextActions({ stateMap, deterministic, transactionFacts });
1490
1525
  return {
1491
1526
  schemaVersion: 1,
1492
1527
  contract: PAPER_STATUS_CONTRACT,
@@ -2050,146 +2085,8 @@ function localToolchainObservation(cwd, publication) {
2050
2085
  };
2051
2086
  }
2052
2087
 
2053
- export function collectPaperPreflight({
2054
- cwd = process.cwd(),
2055
- buildchainRoot = process.cwd(),
2056
- buildchainVersion = "",
2057
- buildchainRef = "v3",
2058
- buildchainSha = "",
2059
- registry = NPM_REGISTRY,
2060
- offline = false,
2061
- agentEntryMode = "contract",
2062
- } = {}) {
2063
- const resolvedCwd = path.resolve(cwd);
2064
- const status = collectPaperStatus({ cwd: resolvedCwd });
2065
- const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
2066
- const configResult = paperConfig(resolvedCwd);
2067
- let validation;
2068
- let validationError = "";
2069
- try {
2070
- validation = validateBuildchainConfig(resolvedCwd, {
2071
- requireLifecycleStages: ["verify"],
2072
- });
2073
- } catch (error) {
2074
- validationError = error.message;
2075
- }
2076
- const runtime = runtimeFacts({
2077
- buildchainRoot,
2078
- buildchainVersion,
2079
- buildchainRef: provisioning.value?.runtime?.ref || buildchainRef,
2080
- buildchainSha,
2081
- });
2082
- const agentEntry = collectPaperAgentEntry({
2083
- cwd: resolvedCwd,
2084
- buildchainSha: runtime.resolvedSha,
2085
- mode: agentEntryMode,
2086
- });
2087
- const lockPath = path.resolve(resolvedCwd, PAPER_PATHS.contractLock);
2088
- let lockEvaluation = {
2089
- status: "missing-lock",
2090
- compatible: false,
2091
- drift: false,
2092
- reasons: ["Buildchain contract lock is missing"],
2093
- };
2094
- try {
2095
- const lock = readBuildchainContractLock(lockPath);
2096
- if (lock) {
2097
- const current = runtimeContractWorld(buildchainRoot);
2098
- lockEvaluation = evaluateBuildchainContractLock({
2099
- lock,
2100
- current,
2101
- runtimeRef: runtime.ref,
2102
- runtimeSha: runtime.resolvedSha,
2103
- runtimeClass: /alpha/i.test(runtime.ref) ? "alpha" : "stable",
2104
- });
2105
- }
2106
- } catch (error) {
2107
- lockEvaluation = {
2108
- status: "invalid-lock",
2109
- compatible: false,
2110
- drift: false,
2111
- reasons: [error.message],
2112
- };
2113
- }
2114
- const source = {
2115
- repositoryRoot: gitValue(resolvedCwd, ["rev-parse", "--show-toplevel"]),
2116
- head: gitValue(resolvedCwd, ["rev-parse", "HEAD"]),
2117
- tree: gitValue(resolvedCwd, ["rev-parse", "HEAD^{tree}"]),
2118
- branch: gitValue(resolvedCwd, ["branch", "--show-current"]),
2119
- clean: gitResult(resolvedCwd, ["status", "--porcelain"]).stdout === "",
2120
- };
2121
- const packageName = status.identity.package;
2122
- const repository = status.identity.repository;
2123
- const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value, {
2124
- repository,
2125
- workflow: path.posix.basename(PAPER_PATHS.releaseWorkflow),
2126
- });
2127
- const npm =
2128
- offline || !packageName
2129
- ? {
2130
- package: {
2131
- status: "unknown",
2132
- exists: null,
2133
- version: "",
2134
- registry,
2135
- errorCode: offline ? "offline" : "package-unresolved",
2136
- },
2137
- auth: {
2138
- status: "unknown",
2139
- authenticated: null,
2140
- identity: "",
2141
- errorCode: offline ? "offline" : "package-unresolved",
2142
- },
2143
- trust: {
2144
- status: "unknown",
2145
- configured: null,
2146
- publishers: [],
2147
- errorCode: offline ? "offline" : "package-unresolved",
2148
- },
2149
- }
2150
- : {
2151
- package: liveNpmPackageObservation(
2152
- packageName,
2153
- registry,
2154
- resolvedCwd,
2155
- ),
2156
- auth: liveNpmAuthObservation(registry, resolvedCwd),
2157
- trust: liveNpmTrustObservation(
2158
- packageName,
2159
- registry,
2160
- resolvedCwd,
2161
- expectedPublisher,
2162
- ),
2163
- };
2164
- const repositoryPermissions = offline
2165
- ? {
2166
- status: "unknown",
2167
- repository,
2168
- canWrite: null,
2169
- errorCode: "offline",
2170
- }
2171
- : liveRepositoryPermissionObservation(repository, resolvedCwd);
2172
- const repositoryActions = offline
2173
- ? {
2174
- status: "unknown",
2175
- defaultWorkflowPermissions: "",
2176
- canApprovePullRequestReviews: null,
2177
- errorCode: "offline",
2178
- }
2179
- : liveRepositoryActionsPolicyObservation(repository, resolvedCwd);
2180
- const generatedWriteAuthority = offline
2181
- ? {
2182
- status: "unknown",
2183
- configured: null,
2184
- mode: "",
2185
- errorCode: "offline",
2186
- }
2187
- : liveGeneratedWriteAuthorityObservation(repository, resolvedCwd);
2188
- const toolchain = localToolchainObservation(
2189
- resolvedCwd,
2190
- configResult.loaded?.config?.publication,
2191
- );
2192
- const checks = [
2088
+ function paperPreflightLocalChecks({ agentEntry, provisioning, validationError, source, toolchain, runtime, lockEvaluation }) {
2089
+ return [
2193
2090
  ...agentEntry.checks.map((entry) => ({
2194
2091
  ...entry,
2195
2092
  blocking: true,
@@ -2256,6 +2153,11 @@ export function collectPaperPreflight({
2256
2153
  ? `Buildchain runtime contract is ${lockEvaluation.status}.`
2257
2154
  : `Buildchain runtime contract is not admitted: ${(lockEvaluation.reasons || []).join("; ")}`,
2258
2155
  },
2156
+ ];
2157
+ }
2158
+
2159
+ function paperPreflightRepositoryChecks({ repositoryPermissions, repositoryActions, generatedWriteAuthority }) {
2160
+ return [
2259
2161
  {
2260
2162
  id: "repository.write-permission",
2261
2163
  status:
@@ -2309,6 +2211,11 @@ export function collectPaperPreflight({
2309
2211
  ? "No GitHub App or compatible narrow generated-write credential is configured."
2310
2212
  : `Generated-write authority is unknown (${generatedWriteAuthority.errorCode}).`,
2311
2213
  },
2214
+ ];
2215
+ }
2216
+
2217
+ function paperPreflightPublicationChecks({ npm, status }) {
2218
+ return [
2312
2219
  {
2313
2220
  id: "npm.package",
2314
2221
  status:
@@ -2365,24 +2272,19 @@ export function collectPaperPreflight({
2365
2272
  : `${status.conflicts.length} release-state conflict(s) require repair.`,
2366
2273
  },
2367
2274
  ];
2368
- const blockingChecks = checks.filter(
2369
- (entry) => entry.blocking && ["fail", "unknown"].includes(entry.status),
2370
- );
2371
- const externalMutationChecks = checks.filter(
2372
- (entry) =>
2373
- entry.scope === "external-mutation" &&
2374
- ["fail", "pending", "unknown"].includes(entry.status),
2375
- );
2376
- const nextActions = [];
2275
+ }
2276
+
2277
+ function paperPreflightNextActions({ validationError, source, lockEvaluation, npm, repositoryActions, generatedWriteAuthority, status }) {
2278
+ const actions = [];
2377
2279
  if (validationError) {
2378
- nextActions.push({
2280
+ actions.push({
2379
2281
  id: "repair-config",
2380
2282
  command: "buildchain validate --require-lifecycle-stages verify",
2381
2283
  description: validationError,
2382
2284
  });
2383
2285
  }
2384
2286
  if (!source.clean) {
2385
- nextActions.push({
2287
+ actions.push({
2386
2288
  id: "commit-source",
2387
2289
  command: "git status --short",
2388
2290
  description:
@@ -2390,7 +2292,7 @@ export function collectPaperPreflight({
2390
2292
  });
2391
2293
  }
2392
2294
  if (!lockEvaluation.compatible) {
2393
- nextActions.push({
2295
+ actions.push({
2394
2296
  id: "refresh-contract-lock",
2395
2297
  command: "buildchain paper scaffold --json",
2396
2298
  description:
@@ -2398,7 +2300,7 @@ export function collectPaperPreflight({
2398
2300
  });
2399
2301
  }
2400
2302
  if (npm.package.exists === false || npm.trust.configured === false) {
2401
- nextActions.push({
2303
+ actions.push({
2402
2304
  id: "bootstrap-npm",
2403
2305
  command: "buildchain paper bootstrap npm --json",
2404
2306
  description:
@@ -2410,7 +2312,7 @@ export function collectPaperPreflight({
2410
2312
  (repositoryActions.defaultWorkflowPermissions !== "read" ||
2411
2313
  repositoryActions.canApprovePullRequestReviews !== false)
2412
2314
  ) {
2413
- nextActions.push({
2315
+ actions.push({
2414
2316
  id: "constrain-repository-actions",
2415
2317
  command: "",
2416
2318
  description:
@@ -2418,7 +2320,7 @@ export function collectPaperPreflight({
2418
2320
  });
2419
2321
  }
2420
2322
  if (generatedWriteAuthority.configured === false) {
2421
- nextActions.push({
2323
+ actions.push({
2422
2324
  id: "configure-generated-write-authority",
2423
2325
  command: "",
2424
2326
  description:
@@ -2426,12 +2328,149 @@ export function collectPaperPreflight({
2426
2328
  });
2427
2329
  }
2428
2330
  if (status.deterministicBuild.status !== "qualifying") {
2429
- nextActions.push({
2331
+ actions.push({
2430
2332
  id: "build-paper",
2431
2333
  command: "buildchain paper build --execute --json",
2432
2334
  description: "Run the existing qualifying two-clean-build gate.",
2433
2335
  });
2434
2336
  }
2337
+ return actions;
2338
+ }
2339
+
2340
+ export function collectPaperPreflight({
2341
+ cwd = process.cwd(),
2342
+ buildchainRoot = process.cwd(),
2343
+ buildchainVersion = "",
2344
+ buildchainRef = "v3",
2345
+ buildchainSha = "",
2346
+ registry = NPM_REGISTRY,
2347
+ offline = false,
2348
+ agentEntryMode = "contract",
2349
+ } = {}) {
2350
+ const resolvedCwd = path.resolve(cwd);
2351
+ const status = collectPaperStatus({ cwd: resolvedCwd });
2352
+ const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
2353
+ const configResult = paperConfig(resolvedCwd);
2354
+ let validation;
2355
+ let validationError = "";
2356
+ try {
2357
+ validation = validateBuildchainConfig(resolvedCwd, { requireLifecycleStages: ["verify"] });
2358
+ } catch (error) {
2359
+ validationError = error.message;
2360
+ }
2361
+ const runtime = runtimeFacts({
2362
+ buildchainRoot, buildchainVersion,
2363
+ buildchainRef: provisioning.value?.runtime?.ref || buildchainRef, buildchainSha,
2364
+ });
2365
+ const agentEntry = collectPaperAgentEntry({ cwd: resolvedCwd, buildchainSha: runtime.resolvedSha, mode: agentEntryMode });
2366
+ const lockPath = path.resolve(resolvedCwd, PAPER_PATHS.contractLock);
2367
+ let lockEvaluation = {
2368
+ status: "missing-lock",
2369
+ compatible: false,
2370
+ drift: false,
2371
+ reasons: ["Buildchain contract lock is missing"],
2372
+ };
2373
+ try {
2374
+ const lock = readBuildchainContractLock(lockPath);
2375
+ if (lock) {
2376
+ const current = runtimeContractWorld(buildchainRoot);
2377
+ lockEvaluation = evaluateBuildchainContractLock({
2378
+ lock,
2379
+ current,
2380
+ runtimeRef: runtime.ref,
2381
+ runtimeSha: runtime.resolvedSha,
2382
+ runtimeClass: /alpha/i.test(runtime.ref) ? "alpha" : "stable",
2383
+ });
2384
+ }
2385
+ } catch (error) {
2386
+ lockEvaluation = {
2387
+ status: "invalid-lock",
2388
+ compatible: false,
2389
+ drift: false,
2390
+ reasons: [error.message],
2391
+ };
2392
+ }
2393
+ const source = {
2394
+ repositoryRoot: gitValue(resolvedCwd, ["rev-parse", "--show-toplevel"]),
2395
+ head: gitValue(resolvedCwd, ["rev-parse", "HEAD"]),
2396
+ tree: gitValue(resolvedCwd, ["rev-parse", "HEAD^{tree}"]),
2397
+ branch: gitValue(resolvedCwd, ["branch", "--show-current"]),
2398
+ clean: gitResult(resolvedCwd, ["status", "--porcelain"]).stdout === "",
2399
+ };
2400
+ const packageName = status.identity.package;
2401
+ const repository = status.identity.repository;
2402
+ const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value, {
2403
+ repository, workflow: path.posix.basename(PAPER_PATHS.releaseWorkflow),
2404
+ });
2405
+ const npm =
2406
+ offline || !packageName
2407
+ ? {
2408
+ package: {
2409
+ status: "unknown",
2410
+ exists: null,
2411
+ version: "",
2412
+ registry,
2413
+ errorCode: offline ? "offline" : "package-unresolved",
2414
+ },
2415
+ auth: {
2416
+ status: "unknown",
2417
+ authenticated: null,
2418
+ identity: "",
2419
+ errorCode: offline ? "offline" : "package-unresolved",
2420
+ },
2421
+ trust: {
2422
+ status: "unknown",
2423
+ configured: null,
2424
+ publishers: [],
2425
+ errorCode: offline ? "offline" : "package-unresolved",
2426
+ },
2427
+ }
2428
+ : {
2429
+ package: liveNpmPackageObservation(packageName, registry, resolvedCwd),
2430
+ auth: liveNpmAuthObservation(registry, resolvedCwd),
2431
+ trust: liveNpmTrustObservation(packageName, registry, resolvedCwd, expectedPublisher),
2432
+ };
2433
+ const repositoryPermissions = offline
2434
+ ? {
2435
+ status: "unknown",
2436
+ repository,
2437
+ canWrite: null,
2438
+ errorCode: "offline",
2439
+ }
2440
+ : liveRepositoryPermissionObservation(repository, resolvedCwd);
2441
+ const repositoryActions = offline
2442
+ ? {
2443
+ status: "unknown",
2444
+ defaultWorkflowPermissions: "",
2445
+ canApprovePullRequestReviews: null,
2446
+ errorCode: "offline",
2447
+ }
2448
+ : liveRepositoryActionsPolicyObservation(repository, resolvedCwd);
2449
+ const generatedWriteAuthority = offline
2450
+ ? {
2451
+ status: "unknown",
2452
+ configured: null,
2453
+ mode: "",
2454
+ errorCode: "offline",
2455
+ }
2456
+ : liveGeneratedWriteAuthorityObservation(repository, resolvedCwd);
2457
+ const toolchain = localToolchainObservation(resolvedCwd, configResult.loaded?.config?.publication);
2458
+ const checks = [
2459
+ ...paperPreflightLocalChecks({ agentEntry, provisioning, validationError, source, toolchain, runtime, lockEvaluation }),
2460
+ ...paperPreflightRepositoryChecks({ repositoryPermissions, repositoryActions, generatedWriteAuthority }),
2461
+ ...paperPreflightPublicationChecks({ npm, status }),
2462
+ ];
2463
+ const blockingChecks = checks.filter(
2464
+ (entry) => entry.blocking && ["fail", "unknown"].includes(entry.status),
2465
+ );
2466
+ const externalMutationChecks = checks.filter(
2467
+ (entry) =>
2468
+ entry.scope === "external-mutation" &&
2469
+ ["fail", "pending", "unknown"].includes(entry.status),
2470
+ );
2471
+ const nextActions = paperPreflightNextActions({
2472
+ validationError, source, lockEvaluation, npm, repositoryActions, generatedWriteAuthority, status,
2473
+ });
2435
2474
  return {
2436
2475
  schemaVersion: 1,
2437
2476
  contract: PAPER_PREFLIGHT_CONTRACT,
@@ -2725,467 +2764,30 @@ function bootstrapPackageShape({ packageName, version }) {
2725
2764
  };
2726
2765
  }
2727
2766
 
2728
- export function executePaperNpmBootstrap({
2729
- cwd = process.cwd(),
2730
- packageName = "",
2731
- bootstrapVersion = DEFAULT_BOOTSTRAP_VERSION,
2732
- registry = NPM_REGISTRY,
2733
- repository = "",
2734
- workflow = "paper-release.yml",
2735
- environment = "",
2736
- execute = false,
2737
- confirmedPackage = "",
2738
- userconfig = "",
2739
- offline = false,
2740
- } = {}) {
2741
- const resolvedCwd = path.resolve(cwd);
2742
- if (registry !== NPM_REGISTRY) {
2743
- throw new Error(
2744
- `paper npm bootstrap requires the official registry ${NPM_REGISTRY}`,
2745
- );
2746
- }
2747
- if (bootstrapVersion !== DEFAULT_BOOTSTRAP_VERSION) {
2748
- throw new Error(
2749
- `paper npm bootstrap version is fixed at ${DEFAULT_BOOTSTRAP_VERSION}`,
2750
- );
2751
- }
2752
- const configResult = paperConfig(resolvedCwd);
2753
- if (configResult.error) throw new Error(configResult.error);
2754
- const name = normalizePackageName(
2755
- packageName ||
2756
- configResult.loaded.config.publish?.package ||
2757
- configResult.loaded.config.publish?.mainPackage,
2758
- );
2759
- const repo = normalizeRepository(
2760
- repository || resolvePaperRepository(resolvedCwd),
2761
- );
2762
- const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
2763
- if (!provisioning.valid) {
2764
- throw new Error(
2765
- `paper npm bootstrap requires a valid provisioning authority: ${provisioning.errors.join("; ")}`,
2766
- );
2767
- }
2768
- const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value);
2769
- if (
2770
- provisioning.value.package?.name !== name ||
2771
- expectedPublisher.repository !== repo ||
2772
- expectedPublisher.workflow !== toPosix(workflow).replace(/^\/+/, "") ||
2773
- expectedPublisher.environment !== String(environment || "")
2774
- ) {
2775
- throw new Error(
2776
- "paper npm bootstrap coordinates differ from the exact provisioning authority",
2777
- );
2778
- }
2779
- if (execute && confirmedPackage !== name) {
2780
- throw new Error(
2781
- `real npm bootstrap requires --confirm-public-package ${name}`,
2782
- );
2783
- }
2784
- if (execute && offline) {
2785
- throw new Error("real npm bootstrap cannot run with --offline");
2786
- }
2787
- if (execute && (!repo || !workflow)) {
2788
- throw new Error(
2789
- "real npm bootstrap requires GitHub repository and workflow coordinates",
2790
- );
2791
- }
2792
- const packageObservation = offline
2793
- ? {
2794
- status: "unknown",
2795
- exists: null,
2796
- version: "",
2797
- registry,
2798
- errorCode: "offline",
2799
- }
2800
- : liveNpmPackageObservation(name, registry, resolvedCwd);
2801
- const auth = offline
2802
- ? {
2803
- status: "unknown",
2804
- authenticated: null,
2805
- identity: "",
2806
- errorCode: "offline",
2807
- }
2808
- : liveNpmAuthObservation(registry, resolvedCwd);
2809
- const plan = {
2810
- schemaVersion: 1,
2811
- contract: PAPER_NPM_BOOTSTRAP_CONTRACT,
2812
- ok: true,
2813
- dryRun: !execute,
2814
- externalMutation: execute,
2815
- package: {
2816
- name,
2817
- bootstrapVersion,
2818
- registry,
2819
- existsBefore: packageObservation.exists,
2820
- observedVersion: packageObservation.version,
2821
- },
2822
- repository: {
2823
- github: repo,
2824
- workflow,
2825
- environment,
2826
- },
2827
- authority: {
2828
- digest: provisioning.value.authorityDigest,
2829
- policyDigest: provisioning.value.policy.policyDigest,
2830
- },
2831
- auth: {
2832
- authenticated: auth.authenticated,
2833
- identity: auth.identity,
2834
- errorCode: auth.errorCode,
2835
- },
2836
- publish: {
2837
- status: packageObservation.exists === true ? "existing" : "planned",
2838
- distTag: "bootstrap",
2839
- access: "public",
2840
- },
2841
- trust: {
2842
- status: "planned",
2843
- urls: [],
2844
- },
2845
- receipts: [],
2846
- nextActions: execute
2847
- ? []
2848
- : [
2849
- {
2850
- id: "confirm-public-bootstrap",
2851
- command: `buildchain paper bootstrap npm --execute --confirm-public-package ${name} --json`,
2852
- description:
2853
- "After reviewing this dry-run, bootstrap the exact public package and configure GitHub Trusted Publishing.",
2854
- },
2855
- ],
2856
- };
2857
- if (!execute) {
2858
- const dryRunRoot = fs.mkdtempSync(
2859
- path.join(os.tmpdir(), "buildchain-paper-bootstrap-dry-run-"),
2860
- );
2861
- try {
2862
- fs.writeFileSync(
2863
- path.join(dryRunRoot, "package.json"),
2864
- jsonText(
2865
- bootstrapPackageShape({
2866
- packageName: name,
2867
- version: bootstrapVersion,
2868
- }),
2869
- ),
2870
- );
2871
- const configArgs = userconfig ? ["--userconfig", userconfig] : [];
2872
- const pack = commandResult(
2873
- "npm",
2874
- [
2875
- "pack",
2876
- "--dry-run",
2877
- "--json",
2878
- "--ignore-scripts",
2879
- `--registry=${registry}`,
2880
- ...configArgs,
2881
- ],
2882
- { cwd: dryRunRoot },
2883
- );
2884
- const publish = commandResult(
2885
- "npm",
2886
- [
2887
- "publish",
2888
- "--dry-run",
2889
- "--ignore-scripts",
2890
- "--access",
2891
- "public",
2892
- "--tag",
2893
- "bootstrap",
2894
- `--registry=${registry}`,
2895
- ...configArgs,
2896
- ],
2897
- { cwd: dryRunRoot },
2898
- );
2899
- return {
2900
- ...plan,
2901
- ok: pack.ok && publish.ok,
2902
- errorCode: !pack.ok
2903
- ? "npm-pack-dry-run-failed"
2904
- : !publish.ok
2905
- ? "npm-publish-dry-run-failed"
2906
- : "",
2907
- dryRunChecks: {
2908
- minimalPackageOnly: true,
2909
- pack: {
2910
- status: pack.ok ? "pass" : "fail",
2911
- entryCount: (() => {
2912
- const parsed = safeParseJson(pack.stdout);
2913
- const value = Array.isArray(parsed) ? parsed[0] : parsed;
2914
- return Array.isArray(value?.files) ? value.files.length : 0;
2915
- })(),
2916
- },
2917
- publish: {
2918
- status: publish.ok ? "pass" : "fail",
2919
- registry,
2920
- access: "public",
2921
- distTag: "bootstrap",
2922
- },
2923
- },
2924
- };
2925
- } finally {
2926
- fs.rmSync(dryRunRoot, { recursive: true, force: true });
2927
- }
2928
- }
2929
- if (packageObservation.exists === null) {
2930
- return {
2931
- ...plan,
2932
- ok: false,
2933
- errorCode: "npm-package-status-unknown",
2934
- publish: {
2935
- ...plan.publish,
2936
- status: "blocked",
2937
- },
2938
- trust: {
2939
- status: "blocked",
2940
- urls: [],
2941
- },
2942
- nextActions: [
2943
- {
2944
- id: "verify-npm-package",
2945
- command: `npm view ${name} version --json --registry=${registry}`,
2946
- description:
2947
- "Resolve whether the exact public package exists before any publish mutation.",
2948
- },
2949
- ],
2950
- };
2951
- }
2952
- if (auth.authenticated !== true) {
2953
- return {
2954
- ...plan,
2955
- ok: false,
2956
- errorCode: "npm-auth-required",
2957
- publish: {
2958
- ...plan.publish,
2959
- status: "blocked",
2960
- },
2961
- trust: {
2962
- status: "blocked",
2963
- urls: [],
2964
- },
2965
- nextActions: [
2966
- {
2967
- id: "authenticate-npm",
2968
- command: `npm whoami --registry=${registry}`,
2969
- description:
2970
- "Authenticate npm without sharing token contents, then rerun the exact bootstrap command.",
2971
- },
2972
- ],
2973
- };
2974
- }
2975
- const tempRoot = fs.mkdtempSync(
2976
- path.join(os.tmpdir(), "buildchain-paper-bootstrap-"),
2977
- );
2978
- try {
2979
- fs.writeFileSync(
2980
- path.join(tempRoot, "package.json"),
2981
- jsonText(
2982
- bootstrapPackageShape({ packageName: name, version: bootstrapVersion }),
2983
- ),
2984
- );
2985
- const configArgs = userconfig ? ["--userconfig", userconfig] : [];
2986
- const pack = commandResult(
2987
- "npm",
2988
- [
2989
- "pack",
2990
- "--dry-run",
2991
- "--json",
2992
- "--ignore-scripts",
2993
- `--registry=${registry}`,
2994
- ...configArgs,
2995
- ],
2996
- { cwd: tempRoot },
2997
- );
2998
- if (!pack.ok) {
2999
- return {
3000
- ...plan,
3001
- ok: false,
3002
- errorCode: "npm-pack-dry-run-failed",
3003
- publish: { ...plan.publish, status: "blocked" },
3004
- trust: { status: "blocked", urls: [] },
3005
- };
3006
- }
3007
- const publishDryRun = commandResult(
3008
- "npm",
3009
- [
3010
- "publish",
3011
- "--dry-run",
3012
- "--ignore-scripts",
3013
- "--access",
3014
- "public",
3015
- "--tag",
3016
- "bootstrap",
3017
- `--registry=${registry}`,
3018
- ...configArgs,
3019
- ],
3020
- { cwd: tempRoot },
3021
- );
3022
- if (!publishDryRun.ok) {
3023
- return {
3024
- ...plan,
3025
- ok: false,
3026
- errorCode: "npm-publish-dry-run-failed",
3027
- publish: { ...plan.publish, status: "blocked" },
3028
- trust: { status: "blocked", urls: [] },
3029
- };
3030
- }
3031
- let publishStatus =
3032
- packageObservation.exists === true ? "existing" : "published";
3033
- if (packageObservation.exists !== true) {
3034
- const published = commandResult(
3035
- "npm",
3036
- [
3037
- "publish",
3038
- "--ignore-scripts",
3039
- "--access",
3040
- "public",
3041
- "--tag",
3042
- "bootstrap",
3043
- `--registry=${registry}`,
3044
- ...configArgs,
3045
- ],
3046
- { cwd: tempRoot, timeout: 60000 },
3047
- );
3048
- if (!published.ok) {
3049
- const failure = {
3050
- ...plan,
3051
- ok: false,
3052
- errorCode: "npm-publish-failed",
3053
- publish: { ...plan.publish, status: "failed" },
3054
- trust: { status: "blocked", urls: [] },
3055
- };
3056
- failure.receipts = [
3057
- writePaperReceipt(resolvedCwd, PAPER_PATHS.npmBootstrap, failure),
3058
- ];
3059
- return failure;
3060
- }
3061
- }
3062
- const trustArgs = [
3063
- "trust",
3064
- "github",
3065
- name,
3066
- "--repo",
3067
- repo,
3068
- "--file",
3069
- workflow,
3070
- ...(environment ? ["--env", environment] : []),
3071
- "--allow-publish",
3072
- "--yes",
3073
- "--json",
3074
- `--registry=${registry}`,
3075
- ...configArgs,
3076
- ];
3077
- const trustCommand = commandResult("npm", trustArgs, {
3078
- cwd: resolvedCwd,
3079
- timeout: 60000,
3080
- });
3081
- const parsedTrust = safeParseJson(trustCommand.stdout);
3082
- const urls = [
3083
- ...extractUrls(
3084
- parsedTrust || `${trustCommand.stdout}\n${trustCommand.stderr}`,
3085
- ),
3086
- ];
3087
- const trustList =
3088
- trustCommand.ok && urls.length === 0
3089
- ? liveNpmTrustObservation(
3090
- name,
3091
- registry,
3092
- resolvedCwd,
3093
- expectedPublisher,
3094
- )
3095
- : {
3096
- status: "unknown",
3097
- configured: null,
3098
- publishers: [],
3099
- errorCode:
3100
- urls.length > 0 ? "web-action-required" : "npm-trust-failed",
3101
- };
3102
- const trustStatus =
3103
- trustCommand.ok && trustList.configured === true
3104
- ? "configured"
3105
- : urls.length > 0
3106
- ? "action-required"
3107
- : "failed";
3108
- const packageAfter = liveNpmPackageObservation(name, registry, resolvedCwd);
3109
- const packageVerified = packageAfter.exists === true;
3110
- const receipt = {
3111
- ...plan,
3112
- ok: trustStatus !== "failed" && packageVerified,
3113
- dryRun: false,
3114
- externalMutation: true,
3115
- errorCode:
3116
- trustStatus === "failed"
3117
- ? "npm-trust-failed"
3118
- : packageVerified
3119
- ? ""
3120
- : "npm-package-readback-failed",
3121
- package: {
3122
- ...plan.package,
3123
- existsAfter: packageAfter.exists,
3124
- observedVersionAfter: packageAfter.version,
3125
- readbackStatus: packageAfter.status,
3126
- },
3127
- publish: {
3128
- ...plan.publish,
3129
- status: publishStatus,
3130
- },
3131
- trust: {
3132
- status: trustStatus,
3133
- urls,
3134
- expectedPublisher,
3135
- exactBinding: trustList.exactBinding === true,
3136
- publishers: trustList.publishers,
3137
- },
3138
- nextActions:
3139
- trustStatus === "configured" && packageVerified
3140
- ? [
3141
- {
3142
- id: "paper-preflight",
3143
- command: "buildchain paper preflight --json",
3144
- description:
3145
- "Verify package existence and Trusted Publisher binding from live read-only sources.",
3146
- },
3147
- ]
3148
- : trustStatus === "action-required" && packageVerified
3149
- ? [
3150
- {
3151
- id: "complete-npm-web-action",
3152
- command: "",
3153
- description:
3154
- "Open the exact npm URL returned below, complete the web step, then rerun preflight.",
3155
- urls,
3156
- },
3157
- ]
3158
- : !packageVerified
3159
- ? [
3160
- {
3161
- id: "verify-npm-package-readback",
3162
- command: `npm view ${name} version --json --registry=${registry}`,
3163
- description:
3164
- "The mutation returned but the official registry did not prove package existence; do not infer success.",
3165
- },
3166
- ]
3167
- : [
3168
- {
3169
- id: "retry-npm-trust",
3170
- command: `buildchain paper bootstrap npm --execute --confirm-public-package ${name} --json`,
3171
- description:
3172
- "The package exists; retry only the idempotent Trusted Publisher configuration path.",
3173
- },
3174
- ],
3175
- };
3176
- receipt.receipts = [
3177
- writePaperReceipt(resolvedCwd, PAPER_PATHS.npmBootstrap, receipt),
3178
- ];
3179
- if (
3180
- packageVerified &&
3181
- (trustStatus === "configured" || trustStatus === "action-required")
3182
- ) {
3183
- receipt.receipts.push(
3184
- writePaperReceipt(resolvedCwd, PAPER_PATHS.npmTrust, receipt),
3185
- );
3186
- }
3187
- return receipt;
3188
- } finally {
3189
- fs.rmSync(tempRoot, { recursive: true, force: true });
3190
- }
2767
+ export function executePaperNpmBootstrap(options = {}) {
2768
+ return executePaperNpmBootstrapOperation(options, {
2769
+ DEFAULT_BOOTSTRAP_VERSION,
2770
+ NPM_REGISTRY,
2771
+ PAPER_NPM_BOOTSTRAP_CONTRACT,
2772
+ PAPER_PATHS,
2773
+ bootstrapPackageShape,
2774
+ commandResult,
2775
+ expectedPaperTrustedPublisher,
2776
+ extractUrls,
2777
+ fs,
2778
+ jsonText,
2779
+ liveNpmAuthObservation,
2780
+ liveNpmPackageObservation,
2781
+ liveNpmTrustObservation,
2782
+ normalizePackageName,
2783
+ normalizeRepository,
2784
+ os,
2785
+ paperConfig,
2786
+ path,
2787
+ resolvePaperRepository,
2788
+ safeParseJson,
2789
+ toPosix,
2790
+ validatePaperProvisioningAuthority,
2791
+ writePaperReceipt,
2792
+ });
3191
2793
  }