@kungfu-tech/buildchain 4.0.1-alpha.9 → 4.0.1

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 (98) hide show
  1. package/README.md +9 -6
  2. package/actions/v4-release-candidate-promote/README.md +3 -0
  3. package/architecture/agent-change-map.md +1015 -0
  4. package/architecture/ci-lane-change-budget.json +70 -1
  5. package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
  6. package/architecture/internal-capabilities.json +156 -11
  7. package/architecture/maintainability-debt.json +3229 -0
  8. package/architecture/maintainability-policy.json +480 -129
  9. package/architecture/v3-core-mechanism-inventory.json +4 -3
  10. package/architecture/v3-v4-capability-closure.md +59 -0
  11. package/architecture/v3-v4-live-capability-inventory.json +102404 -0
  12. package/architecture/v4-cross-platform-adopter-qualification.json +52 -0
  13. package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
  14. package/architecture/v4-runtime-semantic-closure.json +710 -0
  15. package/bin/internal/trust-release-release-handlers.mjs +5 -0
  16. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  17. package/contracts/v4-publication-qualification-v1.schema.json +51 -0
  18. package/dist/site/agent-index.json +1 -0
  19. package/dist/site/artifact-schemas.json +2 -0
  20. package/dist/site/buildchain-contract.json +123 -32
  21. package/dist/site/buildchain-site.json +96 -28
  22. package/dist/site/capability-registry.json +6 -6
  23. package/dist/site/cli-registry.json +5 -2
  24. package/dist/site/controller-registry.json +41 -5
  25. package/dist/site/kfd-claims.json +170 -21
  26. package/dist/site/kfd-upstream-aggregate.json +1 -1
  27. package/dist/site/manual-registry.json +6 -6
  28. package/dist/site/node-api-registry.json +1474 -215
  29. package/dist/site/page-registry.json +85 -17
  30. package/dist/site/public-surface-audit.json +85 -20
  31. package/dist/site/publication-authority-registry.json +28 -1
  32. package/dist/site/publication-registry.json +4 -4
  33. package/dist/site/release-provenance.json +5 -0
  34. package/dist/site/schemas/v4-publication-qualification-v1.schema.json +51 -0
  35. package/dist/site/site-manifest.json +10 -10
  36. package/dist/site/workflow-registry.json +94 -18
  37. package/docs/MAP.md +1 -1
  38. package/docs/cli-reference.md +4 -4
  39. package/docs/dev-delivery-warrant.md +12 -9
  40. package/docs/node-api-reference.md +291 -206
  41. package/docs/publish-transaction.md +11 -4
  42. package/docs/release-candidate.md +7 -0
  43. package/docs/v4-adopter-delivery.md +23 -4
  44. package/docs/v4-declarative-release-tail-migration.md +43 -0
  45. package/package.json +8 -3
  46. package/packages/core/adopter-delivery-passport.js +36 -0
  47. package/packages/core/buildchain-contract.js +31 -1
  48. package/packages/core/buildchain-publication-authority.js +1 -0
  49. package/packages/core/dev-delivery-warrant-settlement.js +239 -3
  50. package/packages/core/dev-delivery-warrant-state.js +15 -2
  51. package/packages/core/dev-delivery-warrant.js +21 -4
  52. package/packages/core/github-governance-authority.js +4 -4
  53. package/packages/core/index.js +1 -0
  54. package/packages/core/paper-scaffold-content.js +401 -0
  55. package/packages/core/paper.js +12 -378
  56. package/packages/core/publication-control-plane-audit.js +135 -111
  57. package/packages/core/publication-rehearsal-projection.js +104 -0
  58. package/packages/core/publication-rehearsal-runtime.js +190 -0
  59. package/packages/core/release-candidate-recovery.js +6 -5
  60. package/packages/core/release-passport.js +82 -184
  61. package/packages/core/v4-cross-platform-adopter-qualification.js +343 -0
  62. package/packages/core/v4-floating-consumer-policy.js +3 -2
  63. package/packages/core/v4-floating-consumer-release-passport.js +134 -15
  64. package/packages/core/v4-publication-qualification.js +222 -0
  65. package/scripts/artifact-signing-controller-core.mjs +1 -1
  66. package/scripts/audit-publication-control-plane.mjs +17 -17
  67. package/scripts/buildchain-cli-help.mjs +3 -0
  68. package/scripts/check-internal-architecture.mjs +23 -3
  69. package/scripts/check-inventory.mjs +23 -5
  70. package/scripts/check-maintainability.mjs +145 -4
  71. package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
  72. package/scripts/check-v3-v4-capability-inventory.mjs +574 -0
  73. package/scripts/check-v4-floating-consumer-policy-contract.mjs +10 -3
  74. package/scripts/check-v4-runtime-semantic-closure.mjs +266 -0
  75. package/scripts/dev-delivery-warrant-options.mjs +27 -11
  76. package/scripts/dev-delivery-warrant.mjs +45 -5
  77. package/scripts/dev-pr-auto-merge.mjs +25 -11
  78. package/scripts/dev-pr-delivery-warrant.mjs +44 -0
  79. package/scripts/dev-pr-prequeue-guard.mjs +0 -18
  80. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  81. package/scripts/generate-agent-change-map.mjs +118 -0
  82. package/scripts/generate-release-candidate-passport.mjs +26 -1
  83. package/scripts/generate-site-bundle.mjs +6 -0
  84. package/scripts/maintainability-domain-metrics.mjs +237 -0
  85. package/scripts/maintainability-governance.mjs +473 -0
  86. package/scripts/maintainability-metrics.mjs +28 -6
  87. package/scripts/project-cut-merge-queue-admission.mjs +394 -0
  88. package/scripts/promotion-routing-evidence.mjs +35 -6
  89. package/scripts/release-candidate-resolver.mjs +44 -0
  90. package/scripts/release-line-policy.mjs +25 -15
  91. package/scripts/release-tail.mjs +19 -2
  92. package/scripts/resume-from-candidate-run.mjs +106 -103
  93. package/scripts/run-lifecycle-core.mjs +341 -376
  94. package/scripts/site-capability-metadata.mjs +4 -0
  95. package/scripts/stable-candidate-qualification.mjs +9 -10
  96. package/scripts/v3-v4-capability-catalog.mjs +439 -0
  97. package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
  98. package/scripts/v4-declarative-promotion-admission.mjs +42 -0
@@ -5,10 +5,52 @@ import path from "node:path";
5
5
  import { execFileSync } from "node:child_process";
6
6
  import { pathToFileURL } from "node:url";
7
7
  import {
8
- analyzeJavaScript,
8
+ analyzeSource,
9
+ analyzeWorkflow,
9
10
  collectMaintainabilityMetrics,
10
11
  isHandMaintainedSource,
11
12
  } from "./maintainability-metrics.mjs";
13
+ import {
14
+ calculateDebtMeasuredExcess,
15
+ evaluateDebtAuthority,
16
+ evaluateDebtBudget,
17
+ evaluateExceptionBudget,
18
+ evaluateExceptionGovernance,
19
+ evaluateTestBudgets,
20
+ evaluateWorkflowBudgets,
21
+ } from "./maintainability-governance.mjs";
22
+
23
+ function collectHotspots(root, current, limit = 20, shallowFallback = []) {
24
+ const maintained = new Set([
25
+ ...Object.keys(current.files || {}),
26
+ ...Object.keys(current.tests || {}),
27
+ ...Object.keys(current.workflows || {}),
28
+ ]);
29
+ // Shallow consumers cannot reproduce repository-wide churn ranking.
30
+ // Reuse the audited routes; full clones still re-rank them below.
31
+ // This keeps consumer verification deterministic without inventing history.
32
+ if (
33
+ gitOutput(root, ["rev-parse", "--is-shallow-repository"]).trim() === "true"
34
+ ) {
35
+ return shallowFallback.slice(0, limit);
36
+ }
37
+ const counts = new Map();
38
+ const record = (file) =>
39
+ maintained.has(file) && counts.set(file, (counts.get(file) || 0) + 1);
40
+ for (const args of [
41
+ ["log", "-n", "500", "--format=", "--name-only"],
42
+ ["diff", "--name-only"],
43
+ ])
44
+ for (const file of gitOutput(root, args).split("\n").filter(Boolean))
45
+ record(file);
46
+ return [...counts]
47
+ .sort(
48
+ ([leftPath, left], [rightPath, right]) =>
49
+ right - left || leftPath.localeCompare(rightPath),
50
+ )
51
+ .slice(0, limit)
52
+ .map(([file]) => file);
53
+ }
12
54
 
13
55
  function readJson(root, file) {
14
56
  return JSON.parse(fs.readFileSync(path.join(root, file), "utf8"));
@@ -68,7 +110,35 @@ function sourceMetricsAtRevision(root, revision) {
68
110
  return Object.fromEntries(
69
111
  files.map((file) => [
70
112
  file,
71
- analyzeJavaScript(file, gitOutput(root, ["show", `${revision}:${file}`])),
113
+ analyzeSource(file, gitOutput(root, ["show", `${revision}:${file}`])),
114
+ ]),
115
+ );
116
+ }
117
+
118
+ function testMetricsAtRevision(root, revision) {
119
+ const files = gitOutput(root, ["ls-tree", "-r", "--name-only", revision])
120
+ .split("\n")
121
+ .filter((file) =>
122
+ /(?:^tests\/|(?:^|\/)tests?\/).+\.(?:c|m)?js$/u.test(file),
123
+ )
124
+ .sort();
125
+ return Object.fromEntries(
126
+ files.map((file) => [
127
+ file,
128
+ analyzeSource(file, gitOutput(root, ["show", `${revision}:${file}`])),
129
+ ]),
130
+ );
131
+ }
132
+
133
+ function workflowMetricsAtRevision(root, revision) {
134
+ const files = gitOutput(root, ["ls-tree", "-r", "--name-only", revision])
135
+ .split("\n")
136
+ .filter((file) => /^\.github\/workflows\/.*\.ya?ml$/u.test(file))
137
+ .sort();
138
+ return Object.fromEntries(
139
+ files.map((file) => [
140
+ file,
141
+ analyzeWorkflow(file, gitOutput(root, ["show", `${revision}:${file}`])),
72
142
  ]),
73
143
  );
74
144
  }
@@ -276,10 +346,14 @@ function evaluateRepositoryBudgets({ current, policy }) {
276
346
  return issues;
277
347
  }
278
348
 
349
+ function budgetsForFile(policy, file) {
350
+ return file.endsWith(".rs") ? policy.rustBudgets : policy.sourceBudgets;
351
+ }
352
+
279
353
  function evaluateMaintainability({ current, baselineFiles, policy }) {
280
354
  const issues = [];
281
- const budgets = policy.sourceBudgets;
282
355
  for (const [file, metrics] of Object.entries(current.files)) {
356
+ const budgets = budgetsForFile(policy, file);
283
357
  const baseline = baselineFiles[file];
284
358
  if (!baseline) {
285
359
  const transition = policy.approvedNewFileTransitions?.[file];
@@ -381,15 +455,66 @@ function evaluateMaintainability({ current, baselineFiles, policy }) {
381
455
 
382
456
  function checkMaintainability({ root = process.cwd() } = {}) {
383
457
  const policy = readJson(root, "architecture/maintainability-policy.json");
458
+ if (policy.schemaVersion !== 2) {
459
+ throw new Error("maintainability policy schemaVersion must be 2");
460
+ }
384
461
  const baseline = readJson(root, policy.baseline);
385
462
  const enforcementRevision = policy.enforcementRevision || baseline.revision;
463
+ const extendedCoverageRevision =
464
+ policy.extendedCoverageRevision || enforcementRevision;
386
465
  const hydratedRevisions = ensureMaintainabilityRevisionsAvailable(root, {
387
466
  baselineRevision: baseline.revision,
388
467
  enforcementRevision,
389
468
  });
469
+ const hydratedExtendedCoverageRevision = ensureRevisionAvailable(
470
+ root,
471
+ extendedCoverageRevision,
472
+ );
390
473
  const current = collectMaintainabilityMetrics({ root });
474
+ const debt = readJson(root, "architecture/maintainability-debt.json");
475
+ const architecture = readJson(
476
+ root,
477
+ "architecture/internal-capabilities.json",
478
+ );
391
479
  const baselineFiles = sourceMetricsAtRevision(root, enforcementRevision);
392
- const issues = evaluateMaintainability({ current, baselineFiles, policy });
480
+ const extendedSourceFiles = sourceMetricsAtRevision(
481
+ root,
482
+ extendedCoverageRevision,
483
+ );
484
+ for (const [file, metrics] of Object.entries(extendedSourceFiles)) {
485
+ if (file.endsWith(".rs")) baselineFiles[file] = metrics;
486
+ }
487
+ const baselineTests = testMetricsAtRevision(root, extendedCoverageRevision);
488
+ const baselineWorkflows = workflowMetricsAtRevision(
489
+ root,
490
+ extendedCoverageRevision,
491
+ );
492
+ const issues = evaluateExceptionGovernance({ policy });
493
+ issues.push(...evaluateExceptionBudget({ policy }));
494
+ const hotspots = collectHotspots(root, current, 20, debt.hotspots || []);
495
+ issues.push(
496
+ ...evaluateDebtAuthority({
497
+ current,
498
+ policy,
499
+ debt,
500
+ capabilityIds: new Set(
501
+ architecture.capabilities.map((entry) => entry.id),
502
+ ),
503
+ hotspots,
504
+ }),
505
+ );
506
+ issues.push(...evaluateDebtBudget({ debt, policy }));
507
+ issues.push(...evaluateMaintainability({ current, baselineFiles, policy }));
508
+ issues.push(
509
+ ...evaluateTestBudgets({ current, baselineFiles: baselineTests, policy }),
510
+ );
511
+ issues.push(
512
+ ...evaluateWorkflowBudgets({
513
+ current,
514
+ baselineFiles: baselineWorkflows,
515
+ policy,
516
+ }),
517
+ );
393
518
  issues.push(...evaluateRepositoryBudgets({ current, policy }));
394
519
  issues.push(
395
520
  ...evaluatePublicSurface({ root, revision: enforcementRevision, policy }),
@@ -401,12 +526,18 @@ function checkMaintainability({ root = process.cwd() } = {}) {
401
526
  schemaVersion: 1,
402
527
  baselineRevision: baseline.revision,
403
528
  enforcementRevision,
529
+ extendedCoverageRevision,
404
530
  hydratedBaselineRevision: hydratedRevisions[baseline.revision],
405
531
  hydratedEnforcementRevision: hydratedRevisions[enforcementRevision],
532
+ hydratedExtendedCoverageRevision,
406
533
  trackedFiles: current.repository.trackedFiles,
407
534
  sourceFiles: current.repository.handMaintainedSourceFiles,
408
535
  publicSurface: current.publicSurface,
409
536
  hotspots: current.hotspots,
537
+ governedDebtSurfaces: Object.values(debt.surfaces).reduce(
538
+ (total, entries) => total + Object.keys(entries).length,
539
+ 0,
540
+ ),
410
541
  };
411
542
  }
412
543
 
@@ -427,11 +558,21 @@ if (
427
558
  }
428
559
 
429
560
  export {
561
+ calculateDebtMeasuredExcess,
430
562
  checkMaintainability,
563
+ collectHotspots,
431
564
  ensureMaintainabilityRevisionsAvailable,
432
565
  ensureRevisionAvailable,
433
566
  evaluateMaintainability,
567
+ evaluateDebtAuthority,
568
+ evaluateDebtBudget,
569
+ evaluateExceptionBudget,
570
+ evaluateExceptionGovernance,
434
571
  evaluateRepositoryBudgets,
572
+ evaluateTestBudgets,
573
+ evaluateWorkflowBudgets,
435
574
  evaluatePublicSurface,
436
575
  sourceMetricsAtRevision,
576
+ testMetricsAtRevision,
577
+ workflowMetricsAtRevision,
437
578
  };
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import {
4
+ ProjectCutAdmissionError,
5
+ qualifyProjectCut,
6
+ releaseFamilyQueueLease,
7
+ } from "./project-cut-merge-queue-admission.mjs";
8
+
9
+ function parseArguments(argv) {
10
+ const options = {};
11
+ const names = new Map([
12
+ ["--base", "base"],
13
+ ["--head", "head"],
14
+ ["--initiative-id", "initiativeId"],
15
+ ["--assignment-id", "assignmentId"],
16
+ ["--delivery-class", "deliveryClass"],
17
+ ["--queue-attempt", "queueAttempt"],
18
+ ["--admission-proof-root", "admissionProofRoot"],
19
+ ["--release-family-marker", "releaseFamilyMarker"],
20
+ ["--expected-pr-head", "expectedPullRequestHead"],
21
+ ["--terminal-reason", "terminalReason"],
22
+ ["--evidence-root", "evidenceRoot"],
23
+ ]);
24
+ for (let index = 0; index < argv.length; index += 1) {
25
+ const name = argv[index];
26
+ const key = names.get(name);
27
+ if (!key) throw new Error(`unknown argument: ${name}`);
28
+ const value = argv[index + 1];
29
+ if (value === undefined || value.startsWith("--"))
30
+ throw new Error(`${name} requires a value`);
31
+ if (Object.hasOwn(options, key))
32
+ throw new Error(`${name} was provided more than once`);
33
+ options[key] = value;
34
+ index += 1;
35
+ }
36
+ return options;
37
+ }
38
+
39
+ function markerText(path) {
40
+ return path === "-"
41
+ ? fs.readFileSync(0, "utf8")
42
+ : fs.readFileSync(path, "utf8");
43
+ }
44
+
45
+ function repairReceipt(error) {
46
+ return {
47
+ schema: "project.cut.merge-queue-admission/v1",
48
+ ok: false,
49
+ decision: "repair-required",
50
+ retryable: false,
51
+ compositionChanged: false,
52
+ reasonCodes: [error.reasonCode],
53
+ error: error.message,
54
+ };
55
+ }
56
+
57
+ export function main(argv = process.argv.slice(2)) {
58
+ try {
59
+ const options = parseArguments(argv);
60
+ if (options.releaseFamilyMarker) {
61
+ console.log(
62
+ JSON.stringify(
63
+ releaseFamilyQueueLease(
64
+ markerText(options.releaseFamilyMarker),
65
+ options,
66
+ ),
67
+ ),
68
+ );
69
+ return 0;
70
+ }
71
+ if (!options.base || !options.head)
72
+ throw new Error("--base and --head are required");
73
+ console.log(
74
+ JSON.stringify(qualifyProjectCut({ cwd: process.cwd(), ...options })),
75
+ );
76
+ return 0;
77
+ } catch (error) {
78
+ if (error instanceof ProjectCutAdmissionError) {
79
+ console.log(JSON.stringify(repairReceipt(error)));
80
+ return 1;
81
+ }
82
+ console.error(`project-cut admission error: ${error.message}`);
83
+ return 2;
84
+ }
85
+ }
86
+
87
+ if (import.meta.url === `file://${process.argv[1]}`) process.exitCode = main();