@kungfu-tech/buildchain 3.0.3-alpha.0 → 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.
Files changed (115) hide show
  1. package/AGENTS.md +1 -1
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +32 -1
  4. package/actions/promote-buildchain-ref/README.md +90 -19
  5. package/actions/report-buildchain-issue/README.md +19 -1
  6. package/actions/run-lifecycle/README.md +5 -0
  7. package/actions/validate-config/README.md +19 -1
  8. package/bin/buildchain.mjs +41 -241
  9. package/bin/internal/command-registry.mjs +82 -0
  10. package/bin/internal/trust-release-cli.mjs +1 -0
  11. package/contracts/auditable-demo-media-profiles-v1.json +116 -1
  12. package/contracts/buildchain-v2-residuals-v1.json +0 -45
  13. package/contracts/evidence/auditable-demo-responsive-web-delivery-v1.json +143 -0
  14. package/contracts/evidence/auditable-demo-web-delivery-v1.json +7 -3
  15. package/contracts/fixtures/auditable-demo-responsive-web-delivery-v1/complete-transcript.txt +2 -0
  16. package/contracts/fixtures/auditable-demo-responsive-web-delivery-v1/public-projection.json +16 -0
  17. package/contracts/fixtures/auditable-demo-responsive-web-delivery-v1/scene.json +12 -0
  18. package/dist/site/buildchain-contract.json +161 -42
  19. package/dist/site/buildchain-site.json +180 -84
  20. package/dist/site/capability-registry.json +6 -6
  21. package/dist/site/cli-registry.json +932 -98
  22. package/dist/site/controller-registry.json +97 -5
  23. package/dist/site/kfd-claims.json +316 -23
  24. package/dist/site/kfd-upstream-aggregate.json +1 -1
  25. package/dist/site/manual-registry.json +20 -20
  26. package/dist/site/node-api-registry.json +371 -56
  27. package/dist/site/page-registry.json +155 -59
  28. package/dist/site/public-surface-audit.json +286 -24
  29. package/dist/site/publication-registry.json +4 -4
  30. package/dist/site/release-provenance.json +3 -0
  31. package/dist/site/site-manifest.json +24 -24
  32. package/dist/site/workflow-registry.json +570 -79
  33. package/docs/MAP.md +22 -4
  34. package/docs/auditable-demo.md +35 -12
  35. package/docs/aws-us-elastic-runner-burst-plane.md +381 -0
  36. package/docs/cli.md +143 -12
  37. package/docs/consumer-issue-reporting.md +19 -1
  38. package/docs/github-governance-authority.md +2 -2
  39. package/docs/lifecycle-protocol.md +20 -2
  40. package/docs/migration-inventory.md +24 -5
  41. package/docs/ownership.md +19 -1
  42. package/docs/publication-artifacts.md +185 -18
  43. package/docs/publication-authority.md +4 -4
  44. package/docs/publish-transaction.md +78 -31
  45. package/docs/release-candidate.md +24 -0
  46. package/docs/release-flow.md +50 -32
  47. package/docs/release-governance.md +31 -31
  48. package/docs/release-passport.md +4 -0
  49. package/docs/release-propagation.md +57 -12
  50. package/docs/reusable-build-surface.md +95 -27
  51. package/docs/runtime-train-validation.md +20 -2
  52. package/docs/stable-candidate-patrol.md +6 -6
  53. package/docs/toolkit-observability.md +22 -0
  54. package/docs/versioning.md +6 -4
  55. package/docs/web-surface-deployments.md +62 -0
  56. package/package.json +7 -5
  57. package/packages/core/README.md +61 -4
  58. package/packages/core/buildchain-contract.js +8 -4
  59. package/packages/core/cache-evidence.js +1 -0
  60. package/packages/core/diagnostics.js +105 -30
  61. package/packages/core/github-governance-authority.js +10 -25
  62. package/packages/core/index.js +40 -0
  63. package/packages/core/paper-fleet.js +260 -0
  64. package/packages/core/paper-repository.js +210 -0
  65. package/packages/core/paper-work.js +365 -0
  66. package/packages/core/paper.js +3180 -0
  67. package/packages/core/public-surface-audit.js +26 -75
  68. package/packages/core/public-surface-cli.js +117 -0
  69. package/packages/core/publication-artifact.js +1 -6
  70. package/packages/core/publication-authority.js +1 -0
  71. package/packages/core/publication-reproducibility.js +947 -0
  72. package/packages/core/publication-sealed-bundle.js +190 -0
  73. package/packages/core/publish-transaction.js +147 -17
  74. package/packages/core/release-candidate.js +234 -0
  75. package/packages/core/release-passport.js +483 -201
  76. package/packages/core/release-propagation.js +175 -1
  77. package/scripts/auditable-demo.mjs +119 -5
  78. package/scripts/aws-codebuild-toolchain.mjs +285 -0
  79. package/scripts/aws-macos-jit-core.mjs +378 -0
  80. package/scripts/aws-macos-jit.mjs +123 -0
  81. package/scripts/aws-runner-burst-core.mjs +248 -0
  82. package/scripts/aws-runner-burst.mjs +79 -0
  83. package/scripts/aws-windows-jit-core.mjs +374 -0
  84. package/scripts/aws-windows-jit.mjs +121 -0
  85. package/scripts/build-contract-core.mjs +69 -6
  86. package/scripts/build-standalone-binary.mjs +23 -0
  87. package/scripts/buildchain-cli-help.mjs +256 -0
  88. package/scripts/buildchain-contract-lock.mjs +1 -1
  89. package/scripts/check-internal-architecture.mjs +69 -2
  90. package/scripts/check-inventory.mjs +69 -2
  91. package/scripts/check-javascript-syntax.mjs +31 -0
  92. package/scripts/check-maintainability.mjs +371 -0
  93. package/scripts/compiler-cache-evidence.mjs +221 -0
  94. package/scripts/dev-alpha-candidate-patrol.mjs +268 -43
  95. package/scripts/finalize-native-artifact-signing-result.mjs +75 -4
  96. package/scripts/generate-channel-promotion-workflow.mjs +3 -12
  97. package/scripts/generate-release-candidate-passport.mjs +4 -0
  98. package/scripts/generate-site-bundle.mjs +68 -188
  99. package/scripts/import-artifact-signing-results.mjs +64 -5
  100. package/scripts/inspect-artifact-signing-requests.mjs +7 -0
  101. package/scripts/maintainability-metrics.mjs +339 -0
  102. package/scripts/npm-publish-transaction.mjs +57 -6
  103. package/scripts/paper-work-fleet-cli.mjs +563 -0
  104. package/scripts/paper.mjs +631 -0
  105. package/scripts/promotion-channel-router.mjs +16 -4
  106. package/scripts/publication-reproducibility.mjs +62 -0
  107. package/scripts/release-propagation.mjs +39 -0
  108. package/scripts/resolve-build-contract.mjs +6 -0
  109. package/scripts/route-offline-runners.mjs +236 -0
  110. package/scripts/run-lifecycle-core.mjs +62 -1
  111. package/scripts/runtime-ref-core.mjs +23 -2
  112. package/scripts/seal-artifact-signing-requests.mjs +13 -6
  113. package/scripts/site-capability-metadata.mjs +202 -0
  114. package/scripts/stable-candidate-qualification.mjs +42 -0
  115. package/scripts/web-surface-core.mjs +272 -86
@@ -0,0 +1,563 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import path from "node:path";
3
+ import {
4
+ PAPER_FLEET_AUDIT_CONTRACT,
5
+ PAPER_FLEET_UPDATE_PLAN_CONTRACT,
6
+ PAPER_MIGRATION_CONTRACT,
7
+ PAPER_SCAFFOLD_CONTRACT,
8
+ PAPER_WORK_START_PLAN_CONTRACT,
9
+ PAPER_WORK_SUBMIT_PLAN_CONTRACT,
10
+ collectPaperFleetAudit,
11
+ collectPaperStatus,
12
+ createPaperWorkStartPlan,
13
+ createPaperWorkSubmitPlan,
14
+ discoverPaperFleet,
15
+ executePaperWorkStart,
16
+ executePaperWorkSubmitPush,
17
+ planPaperMigration,
18
+ planPaperScaffold,
19
+ planPaperFleetUpdate,
20
+ writePaperFleetUpdate,
21
+ writePaperMigration,
22
+ writePaperScaffold,
23
+ } from "../packages/core/paper.js";
24
+ import {
25
+ BUILDCHAIN_GITHUB_GOVERNANCE_AUTHORITY,
26
+ compileEffectiveGithubGovernancePolicy,
27
+ } from "../packages/core/github-governance-authority.js";
28
+
29
+ function commandResult(command, args, { cwd, timeout = 60000 } = {}) {
30
+ const result = spawnSync(command, args, {
31
+ cwd,
32
+ env: process.env,
33
+ encoding: "utf8",
34
+ timeout,
35
+ maxBuffer: 2 * 1024 * 1024,
36
+ });
37
+ return {
38
+ ok: result.status === 0,
39
+ stdout: String(result.stdout || "").trim(),
40
+ stderr: String(result.stderr || "").trim(),
41
+ error: result.error?.message || "",
42
+ };
43
+ }
44
+
45
+ function readFlag(args, name, fallback = "") {
46
+ const index = args.indexOf(`--${name}`);
47
+ return index === -1 ? fallback : args[index + 1] || "";
48
+ }
49
+
50
+ function githubPullRequests({ cwd, repository, branch }) {
51
+ const query = commandResult(
52
+ "gh",
53
+ [
54
+ "pr",
55
+ "list",
56
+ "--repo",
57
+ repository,
58
+ "--head",
59
+ branch,
60
+ "--state",
61
+ "open",
62
+ "--json",
63
+ "number,url,headRefName,baseRefName",
64
+ ],
65
+ { cwd },
66
+ );
67
+ if (!query.ok) {
68
+ return {
69
+ ok: false,
70
+ rows: [],
71
+ errorCode: query.error ? "gh-unavailable" : "github-pr-query-failed",
72
+ };
73
+ }
74
+ try {
75
+ const rows = JSON.parse(query.stdout || "[]");
76
+ return {
77
+ ok: Array.isArray(rows),
78
+ rows: Array.isArray(rows) ? rows : [],
79
+ errorCode: "",
80
+ };
81
+ } catch {
82
+ return { ok: false, rows: [], errorCode: "github-pr-response-invalid" };
83
+ }
84
+ }
85
+
86
+ function createPullRequest(plan, title, body) {
87
+ const prTitle =
88
+ title ||
89
+ `chore(paper): ${plan.source.branch.replace(/^[^/]+\//, "").replaceAll("-", " ")}`;
90
+ const prBody =
91
+ body ||
92
+ [
93
+ "## Summary",
94
+ "",
95
+ "Submit this Paper work branch through the protected Buildchain development path.",
96
+ "",
97
+ "## Safety",
98
+ "",
99
+ "- No direct push to a protected channel.",
100
+ "- No force push.",
101
+ "- Publication remains behind the accepted PR and release gates.",
102
+ ].join("\n");
103
+ return commandResult(
104
+ "gh",
105
+ [
106
+ "pr",
107
+ "create",
108
+ "--repo",
109
+ plan.repository,
110
+ "--base",
111
+ plan.target.branch,
112
+ "--head",
113
+ plan.source.branch,
114
+ "--title",
115
+ prTitle,
116
+ "--body",
117
+ prBody,
118
+ ],
119
+ { cwd: plan.cwd },
120
+ );
121
+ }
122
+
123
+ function executeWorkSubmit(plan, args) {
124
+ const pushed = executePaperWorkSubmitPush(plan);
125
+ if (!pushed.ok) return pushed;
126
+ if (plan.pullRequest?.url)
127
+ return { ...pushed, reused: true, pr: plan.pullRequest };
128
+ const created = createPullRequest(
129
+ plan,
130
+ readFlag(args, "title"),
131
+ readFlag(args, "body"),
132
+ );
133
+ if (!created.ok) {
134
+ return {
135
+ ...pushed,
136
+ ok: false,
137
+ errorCode: created.error ? "gh-unavailable" : "github-pr-create-failed",
138
+ stderr: created.error || created.stderr,
139
+ };
140
+ }
141
+ const url =
142
+ created.stdout
143
+ .split(/\s+/)
144
+ .find((entry) => /^https:\/\/github\.com\//.test(entry)) || "";
145
+ return {
146
+ ...pushed,
147
+ reused: false,
148
+ pr: {
149
+ url,
150
+ headRefName: plan.source.branch,
151
+ baseRefName: plan.target.branch,
152
+ },
153
+ };
154
+ }
155
+
156
+ function compactRuleset(value) {
157
+ return {
158
+ id: value.id,
159
+ name: value.name || "",
160
+ enforcement: value.enforcement || "",
161
+ target: value.target || "",
162
+ bypass_actors: value.bypass_actors || [],
163
+ conditions: value.conditions || {},
164
+ rules: (value.rules || []).map((rule) => ({
165
+ type: rule.type || "",
166
+ parameters: rule.parameters || {},
167
+ })),
168
+ };
169
+ }
170
+
171
+ function fetchRulesets(cwd, repository) {
172
+ const listed = commandResult(
173
+ "gh",
174
+ ["api", `repos/${repository}/rulesets?includes_parents=true`],
175
+ { cwd },
176
+ );
177
+ if (!listed.ok) return { ok: false, rows: [] };
178
+ try {
179
+ const summaries = JSON.parse(listed.stdout || "[]");
180
+ const rows = summaries.flatMap((summary) => {
181
+ const detail = commandResult(
182
+ "gh",
183
+ ["api", `repos/${repository}/rulesets/${summary.id}`],
184
+ { cwd },
185
+ );
186
+ if (!detail.ok) return [];
187
+ try {
188
+ return [compactRuleset(JSON.parse(detail.stdout || "{}"))];
189
+ } catch {
190
+ return [];
191
+ }
192
+ });
193
+ return { ok: rows.length === summaries.length, rows };
194
+ } catch {
195
+ return { ok: false, rows: [] };
196
+ }
197
+ }
198
+
199
+ function paperGovernanceTargetPolicies(repository) {
200
+ const name = String(repository || "").split("/")[1] || "";
201
+ return (
202
+ BUILDCHAIN_GITHUB_GOVERNANCE_AUTHORITY.repositoryAdmission
203
+ .publicAuthoritativeTargets[name] || []
204
+ ).filter((entry) => /^(dev|alpha|release)\//.test(entry.targetRef));
205
+ }
206
+
207
+ function bindingKey(entry) {
208
+ return `${entry.context}:${entry.appId ?? ""}`;
209
+ }
210
+
211
+ function sameBindings(observed, expected) {
212
+ const left = (observed || []).map(bindingKey).sort();
213
+ const right = (expected || []).map(bindingKey).sort();
214
+ return JSON.stringify(left) === JSON.stringify(right);
215
+ }
216
+
217
+ function bypassAllowed(observed, allowed) {
218
+ return (observed || []).every((actor) =>
219
+ (allowed || []).some(
220
+ (candidate) =>
221
+ candidate.actorType === actor.actorType &&
222
+ candidate.actorId === actor.actorId &&
223
+ candidate.bypassMode === actor.bypassMode,
224
+ ),
225
+ );
226
+ }
227
+
228
+ export function evaluatePaperGithubGovernance({
229
+ repository,
230
+ actions = {},
231
+ rulesets = [],
232
+ protections = {},
233
+ } = {}) {
234
+ const targetPolicies = paperGovernanceTargetPolicies(repository);
235
+ const checks = [
236
+ {
237
+ id: "actions.default-workflow-permissions",
238
+ ok: actions.default_workflow_permissions === "read",
239
+ },
240
+ {
241
+ id: "actions.pull-request-approval-disabled",
242
+ ok: actions.can_approve_pull_request_reviews === false,
243
+ },
244
+ ];
245
+ const targets = targetPolicies.map((expected) => {
246
+ const observation = protections[expected.targetRef] || {};
247
+ const effective = compileEffectiveGithubGovernancePolicy({
248
+ branch: expected.targetRef,
249
+ defaultBranch: "",
250
+ protectedBranch: observation.ok === true,
251
+ protection: observation.protection || null,
252
+ rulesets,
253
+ });
254
+ const targetChecks = [
255
+ ["observed", observation.ok === true],
256
+ ["native-pull-request", effective.nativePullRequestRequired === true],
257
+ ["approving-review", Number(effective.requiredApprovals) >= 1],
258
+ ["code-owner-review", effective.codeOwnerReviewRequired === true],
259
+ [
260
+ "fresh-review",
261
+ effective.dismissStaleReviews === true ||
262
+ effective.requireLastPushApproval === true,
263
+ ],
264
+ ["administrator-enforcement", effective.enforceAdmins === true],
265
+ ["conversation-resolution", effective.conversationResolution === true],
266
+ [
267
+ "required-check-bindings",
268
+ sameBindings(
269
+ effective.requiredCheckBindings,
270
+ expected.requiredCheckBindings,
271
+ ),
272
+ ],
273
+ [
274
+ "strict-required-checks",
275
+ effective.strictRequiredChecks === expected.strictRequiredChecks,
276
+ ],
277
+ ["force-push-blocked", effective.allowForcePushes === false],
278
+ ["deletion-blocked", effective.allowDeletions === false],
279
+ [
280
+ "bypass-policy",
281
+ bypassAllowed(effective.bypassActors, expected.allowedBypassActors),
282
+ ],
283
+ ].map(([id, ok]) => ({
284
+ id: `protection.${expected.targetRef}.${id}`,
285
+ ok,
286
+ }));
287
+ checks.push(...targetChecks);
288
+ return {
289
+ targetRef: expected.targetRef,
290
+ status: targetChecks.every((entry) => entry.ok) ? "pass" : "fail",
291
+ effectivePolicy: effective,
292
+ };
293
+ });
294
+ const normalizedChecks = checks.map((entry) => ({
295
+ ...entry,
296
+ status: entry.ok ? "pass" : "fail",
297
+ }));
298
+ return {
299
+ status:
300
+ targetPolicies.length > 0 && normalizedChecks.every((entry) => entry.ok)
301
+ ? "pass"
302
+ : "fail",
303
+ checks: normalizedChecks,
304
+ targets,
305
+ };
306
+ }
307
+
308
+ function fetchProtection(cwd, repository, targetRef) {
309
+ const result = commandResult(
310
+ "gh",
311
+ [
312
+ "api",
313
+ `repos/${repository}/branches/${encodeURIComponent(targetRef)}/protection`,
314
+ ],
315
+ { cwd },
316
+ );
317
+ if (!result.ok) return { ok: false, protection: null };
318
+ try {
319
+ return { ok: true, protection: JSON.parse(result.stdout || "{}") };
320
+ } catch {
321
+ return { ok: false, protection: null };
322
+ }
323
+ }
324
+
325
+ function githubGovernance(cwd, repository) {
326
+ const actions = commandResult(
327
+ "gh",
328
+ ["api", `repos/${repository}/actions/permissions/workflow`],
329
+ { cwd },
330
+ );
331
+ const rulesets = fetchRulesets(cwd, repository);
332
+ const targets = paperGovernanceTargetPolicies(repository);
333
+ const protections = Object.fromEntries(
334
+ targets.map((entry) => [
335
+ entry.targetRef,
336
+ fetchProtection(cwd, repository, entry.targetRef),
337
+ ]),
338
+ );
339
+ if (!actions.ok || !rulesets.ok) {
340
+ return {
341
+ status: "fail",
342
+ errorCode: actions.error
343
+ ? "gh-unavailable"
344
+ : "github-governance-query-failed",
345
+ checks: [],
346
+ };
347
+ }
348
+ try {
349
+ const policy = JSON.parse(actions.stdout || "{}");
350
+ const evaluation = evaluatePaperGithubGovernance({
351
+ repository,
352
+ actions: policy,
353
+ rulesets: rulesets.rows,
354
+ protections,
355
+ });
356
+ return {
357
+ ...evaluation,
358
+ errorCode: "",
359
+ actions: policy,
360
+ rulesets: rulesets.rows,
361
+ };
362
+ } catch {
363
+ return {
364
+ status: "fail",
365
+ errorCode: "github-governance-response-invalid",
366
+ checks: [],
367
+ };
368
+ }
369
+ }
370
+
371
+ function runWorkStart({ args, cwd }) {
372
+ const topic = args[0]?.startsWith("--") ? "" : args[0] || "";
373
+ const plan = createPaperWorkStartPlan({
374
+ cwd,
375
+ topic,
376
+ branch: readFlag(args, "branch"),
377
+ });
378
+ return args.includes("--execute") ? executePaperWorkStart(plan) : plan;
379
+ }
380
+
381
+ function runScaffold(options) {
382
+ const plan = planPaperScaffold({
383
+ cwd: options.cwd,
384
+ buildchainRoot: options.buildchainRoot,
385
+ buildchainVersion: options.buildchainVersion,
386
+ buildchainRef: readFlag(
387
+ options.args,
388
+ "buildchain-ref",
389
+ options.buildchainRef,
390
+ ),
391
+ buildchainSha: options.buildchainSha,
392
+ name: readFlag(options.args, "name", path.basename(options.cwd)),
393
+ title: readFlag(options.args, "title"),
394
+ packageName: readFlag(options.args, "package"),
395
+ repository: readFlag(options.args, "repository"),
396
+ version: readFlag(options.args, "version", "0.1.0-alpha.0"),
397
+ siteBaseUrl: readFlag(options.args, "site-base-url"),
398
+ });
399
+ return options.args.some((entry) => ["--write", "--execute"].includes(entry))
400
+ ? writePaperScaffold(plan)
401
+ : plan;
402
+ }
403
+
404
+ function runMigration(options) {
405
+ const plan = planPaperMigration({
406
+ cwd: options.cwd,
407
+ buildchainRoot: options.buildchainRoot,
408
+ buildchainVersion: options.buildchainVersion,
409
+ buildchainSha: options.buildchainSha,
410
+ });
411
+ return options.args.some((entry) => ["--write", "--execute"].includes(entry))
412
+ ? writePaperMigration(plan)
413
+ : plan;
414
+ }
415
+
416
+ function runWorkSubmit({ args, cwd }) {
417
+ const repository = collectPaperStatus({ cwd }).identity.repository;
418
+ const branch = commandResult("git", ["branch", "--show-current"], {
419
+ cwd,
420
+ }).stdout;
421
+ const observation =
422
+ repository && branch
423
+ ? githubPullRequests({ cwd, repository, branch })
424
+ : { ok: false, rows: [], errorCode: "paper-repository-unresolved" };
425
+ const plan = createPaperWorkSubmitPlan({
426
+ cwd,
427
+ pullRequests: observation.rows,
428
+ pullRequestObservation: observation,
429
+ });
430
+ return args.includes("--execute") ? executeWorkSubmit(plan, args) : plan;
431
+ }
432
+
433
+ function runFleetAudit(options) {
434
+ const root = path.resolve(readFlag(options.args, "root", options.cwd));
435
+ const repositories = discoverPaperFleet(root);
436
+ const governance = {};
437
+ if (!options.args.includes("--offline")) {
438
+ for (const repositoryCwd of repositories) {
439
+ const repository = collectPaperStatus({ cwd: repositoryCwd }).identity
440
+ .repository;
441
+ if (repository)
442
+ governance[repository] = githubGovernance(repositoryCwd, repository);
443
+ }
444
+ }
445
+ return collectPaperFleetAudit({
446
+ ...options,
447
+ root,
448
+ repositories,
449
+ governance,
450
+ args: undefined,
451
+ command: undefined,
452
+ subcommand: undefined,
453
+ cwd: undefined,
454
+ });
455
+ }
456
+
457
+ function refreshFleetLocks(result) {
458
+ for (const entry of result.results || []) {
459
+ if (!entry.ok) continue;
460
+ const lock = commandResult("pnpm", ["install", "--lockfile-only"], {
461
+ cwd: entry.cwd,
462
+ });
463
+ if (lock.ok) continue;
464
+ return {
465
+ ...result,
466
+ ok: false,
467
+ errorCode: "paper-fleet-lock-refresh-failed",
468
+ lockFailure: { cwd: entry.cwd, stderr: lock.error || lock.stderr },
469
+ };
470
+ }
471
+ return result;
472
+ }
473
+
474
+ function runFleetUpdate(options) {
475
+ const plan = planPaperFleetUpdate({
476
+ root: path.resolve(readFlag(options.args, "root", options.cwd)),
477
+ buildchainRoot: options.buildchainRoot,
478
+ buildchainVersion: options.buildchainVersion,
479
+ buildchainSha: options.buildchainSha,
480
+ });
481
+ return options.args.includes("--write")
482
+ ? refreshFleetLocks(writePaperFleetUpdate(plan))
483
+ : plan;
484
+ }
485
+
486
+ export function runPaperWorkFleetCli(options) {
487
+ const route = `${options.command}:${options.subcommand}`;
488
+ if (options.command === "scaffold")
489
+ return { handled: true, result: runScaffold(options) };
490
+ if (options.command === "migrate")
491
+ return { handled: true, result: runMigration(options) };
492
+ if (options.command === "status") {
493
+ return { handled: true, result: collectPaperStatus({ cwd: options.cwd }) };
494
+ }
495
+ if (route === "work:start")
496
+ return { handled: true, result: runWorkStart(options) };
497
+ if (route === "work:submit")
498
+ return { handled: true, result: runWorkSubmit(options) };
499
+ if (route === "fleet:audit")
500
+ return { handled: true, result: runFleetAudit(options) };
501
+ if (route === "fleet:update")
502
+ return { handled: true, result: runFleetUpdate(options) };
503
+ return { handled: false, result: undefined };
504
+ }
505
+
506
+ export function printPaperWorkFleetSummary(result, fallback = () => {}) {
507
+ if (
508
+ [PAPER_SCAFFOLD_CONTRACT, PAPER_MIGRATION_CONTRACT].includes(
509
+ result.contract,
510
+ )
511
+ )
512
+ return false;
513
+ if (result.contract === PAPER_WORK_START_PLAN_CONTRACT) {
514
+ process.stdout.write(
515
+ `paper work start: ${result.ok ? (result.dryRun ? "ready" : "created") : "blocked"}\n`,
516
+ );
517
+ process.stdout.write(
518
+ `${result.source.developmentRef} -> ${result.target.branch || "<invalid>"}\n`,
519
+ );
520
+ for (const check of result.checks)
521
+ process.stdout.write(
522
+ `- ${check.status}: ${check.id}: ${check.message}\n`,
523
+ );
524
+ return true;
525
+ }
526
+ if (result.contract === PAPER_WORK_SUBMIT_PLAN_CONTRACT) {
527
+ process.stdout.write(
528
+ `paper work submit: ${result.ok ? (result.dryRun ? "ready" : result.pr?.url || "submitted") : "blocked"}\n`,
529
+ );
530
+ process.stdout.write(
531
+ `${result.source.branch} -> ${result.target.branch}\n`,
532
+ );
533
+ for (const check of result.checks)
534
+ process.stdout.write(
535
+ `- ${check.status}: ${check.id}: ${check.message}\n`,
536
+ );
537
+ return true;
538
+ }
539
+ if (result.contract === PAPER_FLEET_AUDIT_CONTRACT) {
540
+ process.stdout.write(
541
+ `paper fleet audit: ${result.summary.current}/${result.summary.repositories} current\n`,
542
+ );
543
+ process.stdout.write(`audit root: ${result.auditRoot}\n`);
544
+ for (const entry of result.repositories)
545
+ process.stdout.write(
546
+ `- ${entry.ok ? "current" : "drifted"}: ${entry.name}\n`,
547
+ );
548
+ return true;
549
+ }
550
+ if (result.contract === PAPER_FLEET_UPDATE_PLAN_CONTRACT) {
551
+ process.stdout.write(
552
+ `paper fleet update: ${result.ok ? (result.dryRun ? "ready" : "written") : "blocked"}\n`,
553
+ );
554
+ process.stdout.write(`plan root: ${result.planRoot}\n`);
555
+ for (const entry of result.plans)
556
+ process.stdout.write(
557
+ `- ${entry.ok ? "ready" : "blocked"}: ${entry.cwd}\n`,
558
+ );
559
+ return true;
560
+ }
561
+ fallback(result);
562
+ return false;
563
+ }