@kungfu-tech/buildchain 4.0.2-alpha.5 → 4.0.2-alpha.50

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 (171) hide show
  1. package/AGENTS.md +10 -1
  2. package/CONTRIBUTING.md +24 -0
  3. package/README.md +2 -2
  4. package/actions/promote-buildchain-ref/README.md +1 -1
  5. package/actions/v4-release-candidate-promote/README.md +28 -0
  6. package/architecture/agent-change-map.md +70 -9
  7. package/architecture/ci-lane-change-budget.json +384 -0
  8. package/architecture/internal-capabilities.json +56 -9
  9. package/architecture/maintainability-debt.json +162 -107
  10. package/architecture/maintainability-policy.json +34 -30
  11. package/architecture/release-tail-contract-inventory.json +6 -6
  12. package/architecture/v4-architecture-constitution.md +23 -20
  13. package/architecture/v4-cross-platform-adopter-qualification.json +1 -1
  14. package/architecture/v4-delivery-authority-parity.json +2 -2
  15. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +1 -1
  16. package/architecture/v4-floating-consumer-policy.json +2 -2
  17. package/architecture/v4-next-development-parity.json +5 -5
  18. package/architecture/v4-partial-mutation-recovery-qualification.json +4 -1
  19. package/architecture/v4-platform-stage-checkpoints.json +1 -1
  20. package/architecture/v4-provider-operation-journal-contract.json +5 -2
  21. package/architecture/v4-publication-facade-patches.json +64 -0
  22. package/architecture/v4-release-activation-shadow-domain.json +5 -2
  23. package/architecture/v4-release-topology.json +198 -38
  24. package/architecture/v4-rust-wasm-production-authority.json +58 -0
  25. package/architecture/v4-stable-publication-fence.json +5 -2
  26. package/architecture/v4-stage-capsule-qualification.json +2 -2
  27. package/architecture/v4-tail-reseal-parity.json +8 -6
  28. package/architecture/v4-universal-workflow-bootstrap.json +176 -0
  29. package/architecture/v4-universal-workflow-fault-campaign.json +72 -0
  30. package/architecture/v4-universal-workflow-train-admission.json +40 -0
  31. package/architecture/workflow-taxonomy.json +1462 -0
  32. package/bin/buildchain.mjs +7 -7
  33. package/contracts/buildchain-v2-residuals-v1.json +57 -48
  34. package/contracts/dev-delivery-authority-v2.schema.json +16 -2
  35. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  36. package/contracts/v4-canonical-contracts-v1.schema.json +1 -0
  37. package/dist/site/buildchain-contract.json +93 -48
  38. package/dist/site/buildchain-site.json +276 -72
  39. package/dist/site/capability-registry.json +5 -5
  40. package/dist/site/controller-registry.json +50 -13
  41. package/dist/site/kfd-claims.json +2610 -983
  42. package/dist/site/kfd-upstream-aggregate.json +1 -1
  43. package/dist/site/manual-registry.json +14 -14
  44. package/dist/site/node-api-registry.json +273 -127
  45. package/dist/site/page-registry.json +257 -53
  46. package/dist/site/public-surface-audit.json +3239 -1232
  47. package/dist/site/publication-authority-registry.json +1135 -363
  48. package/dist/site/publication-registry.json +4 -4
  49. package/dist/site/release-provenance.json +2 -1
  50. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +16 -2
  51. package/dist/site/site-manifest.json +18 -18
  52. package/dist/site/workflow-registry.json +5097 -1659
  53. package/docs/MAP.md +2 -0
  54. package/docs/auditable-demo.md +1 -1
  55. package/docs/cli.md +21 -8
  56. package/docs/dev-delivery-warrant.md +1 -1
  57. package/docs/engineering-housekeeper.md +3 -3
  58. package/docs/kfd-support.md +26 -2
  59. package/docs/next-development-transition.md +2 -2
  60. package/docs/node-api-reference.md +216 -205
  61. package/docs/oci-publication.md +160 -0
  62. package/docs/publication-authority.md +2 -2
  63. package/docs/publish-transaction.md +36 -3
  64. package/docs/release-candidate.md +3 -3
  65. package/docs/release-flow.md +67 -19
  66. package/docs/release-governance.md +8 -8
  67. package/docs/release-passport.md +27 -2
  68. package/docs/reusable-build-surface.md +16 -0
  69. package/docs/v4-adopter-delivery.md +2 -2
  70. package/docs/v4-production-release.md +71 -8
  71. package/docs/v4-rust-wasm-production-authority.md +58 -0
  72. package/docs/v4-stage-capsule.md +2 -2
  73. package/docs/v4-tail-reseal.md +1 -1
  74. package/docs/workflow-catalog.md +155 -0
  75. package/docs/workflow-path-migration.md +109 -0
  76. package/package.json +12 -7
  77. package/packages/core/artifact-verification-envelope.js +5 -5
  78. package/packages/core/buildchain-contract.js +1 -1
  79. package/packages/core/buildchain-kfd-claims.js +2 -2
  80. package/packages/core/buildchain-publication-authority.js +43 -26
  81. package/packages/core/buildchain-v4-domain.wasm +0 -0
  82. package/packages/core/ci-lane-change-budget.js +14 -1
  83. package/packages/core/controller-evidence.js +4 -1
  84. package/packages/core/dev-delivery-candidate-identity.js +40 -6
  85. package/packages/core/dev-delivery-warrant-state.js +3 -4
  86. package/packages/core/dev-delivery-warrant.js +12 -2
  87. package/packages/core/github-governance-authority.js +4 -4
  88. package/packages/core/kfd-adopter-manifest.js +46 -47
  89. package/packages/core/next-development-projection.js +2 -2
  90. package/packages/core/next-development-transition.js +5 -6
  91. package/packages/core/oci-compose-qualification.js +140 -0
  92. package/packages/core/oci-publication-bundle.js +281 -0
  93. package/packages/core/oci-publication-graph.js +206 -0
  94. package/packages/core/paper-agent-entry.js +30 -4
  95. package/packages/core/paper-fleet.js +1 -1
  96. package/packages/core/paper-runtime-channels.js +348 -0
  97. package/packages/core/paper-scaffold-content.js +10 -0
  98. package/packages/core/paper.js +37 -71
  99. package/packages/core/publication-sealed-bundle.js +78 -13
  100. package/packages/core/release-line-dry-run.js +1 -1
  101. package/packages/core/release-passport-contract.js +5 -5
  102. package/packages/core/release-passport.js +5 -4
  103. package/packages/core/release-tail-product-capabilities.js +38 -0
  104. package/packages/core/release-tail-provider-plane.js +68 -1048
  105. package/packages/core/v4-canonical-contracts.js +15 -75
  106. package/packages/core/v4-domain-wasm-artifact.js +7 -0
  107. package/packages/core/v4-domain-wasm.js +216 -0
  108. package/packages/core/v4-floating-consumer-evidence.js +1 -1
  109. package/packages/core/v4-floating-consumer-policy.js +1 -1
  110. package/packages/core/v4-partial-mutation-recovery-qualification.js +9 -386
  111. package/packages/core/v4-product-publication.js +59 -0
  112. package/packages/core/v4-protected-publication-source.js +19 -21
  113. package/packages/core/v4-provider-operation-journal.js +18 -487
  114. package/packages/core/v4-provider-readback-idempotency.js +12 -97
  115. package/packages/core/v4-release-activation-shadow.js +13 -500
  116. package/packages/core/v4-release-invocation.js +28 -389
  117. package/packages/core/v4-stable-publication-fence.js +13 -342
  118. package/packages/core/v4-universal-workflow-bootstrap.js +586 -0
  119. package/scripts/binary-publication-evidence.mjs +135 -0
  120. package/scripts/build-contract-core.mjs +1 -14
  121. package/scripts/build-v4-domain-wasm.mjs +189 -0
  122. package/scripts/check-action-bundles.mjs +12 -2
  123. package/scripts/check-ci-lane-change-budget.mjs +4 -1
  124. package/scripts/check-core-mechanism-inventory.mjs +4 -3
  125. package/scripts/check-inventory.mjs +35 -44
  126. package/scripts/check-maintainability.mjs +16 -113
  127. package/scripts/check-v4-floating-consumer-policy-contract.mjs +2 -2
  128. package/scripts/check-v4-public-dogfood-contract.mjs +22 -9
  129. package/scripts/check-v4-release-topology.mjs +236 -50
  130. package/scripts/check-v4-runtime-semantic-closure.mjs +4 -1
  131. package/scripts/check-v4-universal-workflow-bootstrap.mjs +266 -0
  132. package/scripts/check-workflow-taxonomy.mjs +6 -0
  133. package/scripts/copy-v4-domain-wasm.mjs +14 -0
  134. package/scripts/dev-delivery-authority-command-adapters.mjs +1 -1
  135. package/scripts/dev-delivery-authority.mjs +8 -8
  136. package/scripts/dev-delivery-source-proof-reuse.mjs +2 -1
  137. package/scripts/dev-delivery-warrant-options.mjs +14 -9
  138. package/scripts/dev-delivery-warrant-store.mjs +73 -7
  139. package/scripts/dev-delivery-warrant-transition.mjs +109 -0
  140. package/scripts/dev-delivery-warrant.mjs +76 -99
  141. package/scripts/dev-pr-auto-merge.mjs +15 -15
  142. package/scripts/generate-channel-build-workflow.mjs +7 -2
  143. package/scripts/generate-channel-promotion-workflow.mjs +10 -25
  144. package/scripts/generate-site-bundle.mjs +11 -11
  145. package/scripts/generate-v4-universal-workflow-facades.mjs +497 -0
  146. package/scripts/generate-workflow-taxonomy.mjs +25 -0
  147. package/scripts/github-output.mjs +31 -0
  148. package/scripts/maintainability-metrics.mjs +16 -5
  149. package/scripts/maintainability-public-surface.mjs +115 -0
  150. package/scripts/next-development-review.mjs +307 -0
  151. package/scripts/oci-compose-preview.mjs +250 -0
  152. package/scripts/paper-work-fleet-cli.mjs +2 -0
  153. package/scripts/publication-candidate-kind.mjs +93 -0
  154. package/scripts/publication-candidate-sealer.mjs +16 -1
  155. package/scripts/release-asset-client.mjs +91 -0
  156. package/scripts/release-candidate-resolver.mjs +23 -35
  157. package/scripts/resume-from-candidate-run.mjs +11 -18
  158. package/scripts/site-capability-metadata.mjs +1 -0
  159. package/scripts/source-verification-evidence.mjs +344 -0
  160. package/scripts/stable-candidate-qualification.mjs +1 -1
  161. package/scripts/universal-facade-maintainability.mjs +91 -0
  162. package/scripts/v4-product-publication-intent.mjs +129 -0
  163. package/scripts/v4-publication-settlement.mjs +211 -0
  164. package/scripts/v4-universal-workflow-backflow.mjs +212 -0
  165. package/scripts/v4-universal-workflow-engine.mjs +620 -0
  166. package/scripts/v4-universal-workflow-self-dogfood.mjs +205 -0
  167. package/scripts/verify-version-state-delta.mjs +173 -0
  168. package/scripts/workflow-taxonomy.mjs +507 -0
  169. package/templates/native-dev-delivery.yml +4 -7
  170. package/templates/universal-buildchain-bootstrap-recovery.yml +282 -0
  171. package/templates/universal-buildchain-bootstrap.yml +28 -0
@@ -4,47 +4,47 @@ import path from "node:path";
4
4
  import { createPublicationAuthorityRegistry } from "./publication-authority.js";
5
5
 
6
6
  const DESCRIPTORS = Object.freeze([
7
+ [".github/workflows/public-release-oci-compose-preview.yml", "product-publication", true, ["oci-compose-preview"], "caller-secrets", "none", "fixed", "caller-bound"],
7
8
  [".github/workflows/.auditable-demo.yml", "non-publication-oidc"],
8
9
  [".github/workflows/.declarative-auditable-demo.yml", "governance-write"],
9
10
  [".github/workflows/.build.yml", "non-publication-oidc"],
10
11
  [".github/workflows/.release-candidate-promote.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "caller-bound"],
11
12
  [".github/workflows/.publication-authority.yml", "evidence-publication"],
12
13
  [".github/workflows/.release-verify.yml", "governance-write"],
13
- [".github/workflows/.sam-release.yml", "retired-deny"],
14
14
  [".github/workflows/.sam-verify.yml", "retired-deny"],
15
15
  [".github/workflows/.sync-remote-git.yml", "governance-write"],
16
16
  [".github/workflows/.web-surface.yml", "product-publication", true, ["web-production"], "oidc", "consumer-defined", "caller-bound", "caller-bound"],
17
- [".github/workflows/.wheel-release.yml", "retired-deny"],
18
17
  [".github/workflows/.wheel-verify.yml", "retired-deny"],
19
18
  [".github/workflows/.binary-release-assets.yml", "product-publication", true, ["github-release"], "github-token", "buildchain-release-assets"],
20
- [".github/workflows/binary-distribution.yml", "evidence-publication"],
21
- [".github/workflows/binary-release-assets.yml", "governance-write"],
19
+ [".github/workflows/self-build-binary-distribution.yml", "evidence-publication"],
20
+ [".github/workflows/self-release-binary-assets.yml", "governance-write"],
22
21
  [".github/workflows/artifact-signing-authority.yml", "evidence-publication"],
23
- [".github/workflows/auditable-demo.yml", "governance-write"],
24
- [".github/workflows/build-surface-fixture.yml", "non-publication-oidc"],
22
+ [".github/workflows/self-build-demo-dogfood.yml", "governance-write"],
23
+ [".github/workflows/self-build-fixture.yml", "non-publication-oidc"],
25
24
  [".github/workflows/build.yml", "non-publication-oidc"],
26
- [".github/workflows/buildchain-alpha-self-dogfood.yml", "non-publication-oidc"],
27
- [".github/workflows/buildchain-dev-delivery.yml", "governance-write"],
28
- [".github/workflows/buildchain-patrol-daily.yml", "governance-write"],
29
- [".github/workflows/buildchain-patrol-monthly.yml", "governance-write"],
30
- [".github/workflows/buildchain-patrol-weekly.yml", "governance-write"],
25
+ [".github/workflows/bootstrap.yml", "product-publication", true, ["universal-candidate-execution"], "caller-secrets", "none", "fixed", "caller-bound"],
26
+ [".github/workflows/self-build-alpha-dogfood.yml", "non-publication-oidc"],
27
+ [".github/workflows/self-ops-dev-delivery.yml", "governance-write"],
28
+ [".github/workflows/self-ops-patrol-daily.yml", "governance-write"],
29
+ [".github/workflows/self-ops-patrol-monthly.yml", "governance-write"],
30
+ [".github/workflows/self-ops-patrol-weekly.yml", "governance-write"],
31
31
  [".github/workflows/buildchain-patrol.yml", "governance-write"],
32
- [".github/workflows/buildchain-ref-promotion-recovery.yml", "governance-write"],
33
- [".github/workflows/buildchain-ref-promotion.yml", "governance-write"],
34
- [".github/workflows/buildchain-stable-candidate-patrol.yml", "governance-write"],
32
+ [".github/workflows/self-ops-promotion-recovery.yml", "governance-write"],
33
+ [".github/workflows/self-release-promote.yml", "governance-write"],
34
+ [".github/workflows/self-ops-stable-candidate-patrol.yml", "governance-write"],
35
35
  [".github/workflows/dev-qualification-patrol.yml", "governance-write"],
36
36
  [".github/workflows/dev-alpha-candidate-patrol.yml", "governance-write"],
37
37
  [".github/workflows/dev-delivery-warrant-close.yml", "governance-write"],
38
38
  [".github/workflows/dev-delivery-warrant-cancel.yml", "governance-write"],
39
- [".github/workflows/dev-merge-queue-governance.yml", "governance-write"],
39
+ [".github/workflows/self-ops-merge-queue.yml", "governance-write"],
40
40
  [".github/workflows/dev-pr-auto-merge.yml", "governance-write"],
41
- [".github/workflows/engineering-housekeeper-daily.yml", "governance-write"],
42
- [".github/workflows/engineering-housekeeper-monthly.yml", "governance-write"],
43
- [".github/workflows/engineering-housekeeper-weekly.yml", "governance-write"],
41
+ [".github/workflows/self-ops-housekeeping-daily.yml", "governance-write"],
42
+ [".github/workflows/self-ops-housekeeping-monthly.yml", "governance-write"],
43
+ [".github/workflows/self-ops-housekeeping-weekly.yml", "governance-write"],
44
44
  [".github/workflows/engineering-housekeeper.yml", "governance-write"],
45
- [".github/workflows/github-governance-audit.yml", "governance-write"],
45
+ [".github/workflows/self-ops-governance-audit.yml", "governance-write"],
46
46
  [".github/workflows/github-artifact-attestation.yml", "evidence-publication", true, ["github-artifact-attestation"], "oidc", "consumer-defined", "caller-bound", "caller-bound"],
47
- [".github/workflows/npm-publish.yml", "dry-run-only"],
47
+ [".github/workflows/self-release-npm-dry-run.yml", "dry-run-only"],
48
48
  [".github/workflows/paper-release-sealed.yml", "product-publication", true, ["npm-publish", "github-release"], "trusted-publishing", "none", "fixed", "caller-bound"],
49
49
  [".github/workflows/paper-release.yml", "product-publication", true, ["npm-publish", "github-release"], "trusted-publishing", "none", "fixed", "caller-bound"],
50
50
  [".github/workflows/patrol-daily.yml", "governance-write"],
@@ -53,16 +53,18 @@ const DESCRIPTORS = Object.freeze([
53
53
  [".github/workflows/patrol-weekly.yml", "governance-write"],
54
54
  [".github/workflows/publication-artifact.yml", "evidence-publication"],
55
55
  [".github/workflows/release-candidate-promote.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "caller-bound"],
56
- [".github/workflows/release-line-bootstrap.yml", "governance-write"],
56
+ [".github/workflows/self-release-line-open.yml", "governance-write"],
57
57
  [".github/workflows/release-propagation.yml", "governance-write"],
58
58
  [".github/workflows/release-tail.yml", "product-publication", true, ["artifact.publish", "signed-channel.commit", "release.activate", "released-evidence.synthesize"], "caller-secrets", "caller-bound", "caller-bound", "fixed"],
59
- [".github/workflows/release-verify.yml", "governance-write"],
59
+ [".github/workflows/self-build-release-verify-compat.yml", "governance-write"],
60
60
  [".github/workflows/stable-candidate-patrol.yml", "governance-write"],
61
- [".github/workflows/v4-declarative-release-tail-dogfood.yml", "product-publication", true, ["artifact.publish", "signed-channel.commit", "release.activate", "released-evidence.synthesize"], "caller-secrets", "none", "fixed", "fixed"],
61
+ [".github/workflows/universal-bootstrap-recovery.yml", "product-publication", true, ["universal-candidate-execution"], "caller-secrets", "none", "fixed", "caller-bound"],
62
+ [".github/workflows/self-ops-bootstrap-dogfood.yml", "product-publication", true, ["universal-candidate-execution"], "caller-secrets", "none", "fixed", "caller-bound"],
63
+ [".github/workflows/self-release-tail-dogfood.yml", "product-publication", true, ["artifact.publish", "signed-channel.commit", "release.activate", "released-evidence.synthesize"], "caller-secrets", "none", "fixed", "fixed"],
62
64
  ]);
63
65
 
64
- export function buildchainPublicationAuthorityDescriptors() {
65
- return DESCRIPTORS.map(([
66
+ export function buildchainPublicationAuthorityDescriptors({ root = process.cwd() } = {}) {
67
+ const descriptors = DESCRIPTORS.map(([
66
68
  workflowPath,
67
69
  authorityClass,
68
70
  publicationCapable = false,
@@ -84,6 +86,21 @@ export function buildchainPublicationAuthorityDescriptors() {
84
86
  environmentMode,
85
87
  runnerPolicy: publicationCapable ? "qualified-measured" : "unqualified",
86
88
  }));
89
+ const taxonomyPath = path.join(root, "architecture/workflow-taxonomy.json");
90
+ if (!fs.existsSync(taxonomyPath)) return descriptors;
91
+ const taxonomy = JSON.parse(fs.readFileSync(taxonomyPath, "utf8"));
92
+ for (const entry of taxonomy.entries) {
93
+ if (!entry.compatibility) continue;
94
+ const previous = descriptors.find((item) => item.workflowPath === entry.compatibility.path);
95
+ if (!previous) continue;
96
+ const prefix = entry.role === "component" ? "." : `${entry.role}-`;
97
+ const workflowPath = `.github/workflows/${prefix}${entry.category}-${entry.purpose}.yml`;
98
+ if (fs.readFileSync(path.join(root, workflowPath), "utf8") !== fs.readFileSync(path.join(root, entry.compatibility.path), "utf8")) {
99
+ throw new Error(`publication authority alias differs from canonical workflow: ${workflowPath}`);
100
+ }
101
+ descriptors.push({ ...previous, workflowPath, publisherWorkflowPath: previous.publisherWorkflowPath === previous.workflowPath ? workflowPath : previous.publisherWorkflowPath });
102
+ }
103
+ return descriptors;
87
104
  }
88
105
  export function createBuildchainPublicationAuthorityRegistry({ root = process.cwd() } = {}) {
89
106
  const workflowsDir = path.join(root, ".github", "workflows");
@@ -94,7 +111,7 @@ export function createBuildchainPublicationAuthorityRegistry({ root = process.cw
94
111
  text: fs.readFileSync(path.join(workflowsDir, name), "utf8"),
95
112
  }));
96
113
  return createPublicationAuthorityRegistry({
97
- descriptors: buildchainPublicationAuthorityDescriptors(),
114
+ descriptors: buildchainPublicationAuthorityDescriptors({ root }),
98
115
  workflows,
99
116
  });
100
117
  }
@@ -10,6 +10,7 @@ const AUTHORITY_CLASSES = new Set([
10
10
  "post-merge-advisory",
11
11
  "diagnostic",
12
12
  "evidence",
13
+ "governed-delegation",
13
14
  "release",
14
15
  ]);
15
16
  const TRIGGER_CLASSES = new Set([
@@ -196,7 +197,19 @@ export function evaluateCiLaneChangeBudget({
196
197
  }
197
198
  const declarations = new Map();
198
199
  const diagnostics = [];
199
- for (const declaration of policy.declarations || []) {
200
+ const familyDeclarations = (policy.declarationFamilies || []).flatMap(
201
+ ({ laneIds, ...declaration }) => {
202
+ if (!Array.isArray(laneIds) || laneIds.length === 0)
203
+ throw new Error("CI lane declaration family requires laneIds");
204
+ if (new Set(laneIds).size !== laneIds.length)
205
+ throw new Error("CI lane declaration family laneIds must be unique");
206
+ return laneIds.map((laneId) => ({ ...declaration, laneId }));
207
+ },
208
+ );
209
+ for (const declaration of [
210
+ ...(policy.declarations || []),
211
+ ...familyDeclarations,
212
+ ]) {
200
213
  const laneId = String(declaration?.laneId || "").trim();
201
214
  if (!laneId) throw new Error("CI lane declaration laneId is required");
202
215
  if (declarations.has(laneId))
@@ -196,6 +196,9 @@ function descriptorDigest(descriptor) {
196
196
 
197
197
  export function createControllerRegistry({ workflows = [] } = {}) {
198
198
  const workflowById = new Map(workflows.map((entry) => [entry.id, entry]));
199
+ for (const entry of workflows) {
200
+ if (entry.taxonomy && !entry.taxonomy.compatibilityAlias && !workflowById.has(entry.taxonomy.id)) workflowById.set(entry.taxonomy.id, entry);
201
+ }
199
202
  const controllers = CONTROLLER_SPECS.map((spec) => {
200
203
  const workflow = workflowById.get(spec.workflowId);
201
204
  if (!workflow) throw new Error(`controller ${spec.id} requires workflow descriptor ${spec.workflowId}`);
@@ -215,7 +218,7 @@ export function createControllerRegistry({ workflows = [] } = {}) {
215
218
  contract: BUILDCHAIN_CONTROLLER_DESCRIPTOR_CONTRACT,
216
219
  id: spec.id,
217
220
  version: spec.version,
218
- workflow: { id: workflow.id, path: workflow.path },
221
+ workflow: { id: workflow.id, path: workflow.path, ...(workflow.taxonomy?.contractPath ? { contractPath: workflow.taxonomy.contractPath } : {}) },
219
222
  inputs,
220
223
  expected: {
221
224
  stages: spec.stages.map((id) => ({ id, required: !optionalStages.has(id) })),
@@ -20,7 +20,21 @@ export const EXACT_DEV_DELIVERY_PROOF_FIELDS = Object.freeze([
20
20
  "environmentRoot",
21
21
  "sourceWorkflowRunId",
22
22
  ]);
23
-
23
+ export function devDeliverySourceBinding(input = {}) {
24
+ const [sourceRoot, assignmentRoot, initiativeRoot] = [
25
+ "sourceRoot",
26
+ "assignmentRoot",
27
+ "initiativeRoot",
28
+ ].map((field) => String(input[field] || "").trim());
29
+ if (sourceRoot && !assignmentRoot && !initiativeRoot)
30
+ return { sourceRoot: exactRoot(sourceRoot, "sourceRoot") };
31
+ if (!sourceRoot && assignmentRoot && initiativeRoot)
32
+ return {
33
+ assignmentRoot: exactRoot(assignmentRoot, "assignmentRoot"),
34
+ initiativeRoot: exactRoot(initiativeRoot, "initiativeRoot"),
35
+ };
36
+ throw new Error("provide sourceRoot alone or both historical roots");
37
+ }
24
38
  export function matchesExactDevDeliveryCandidate(existing, attempted) {
25
39
  return (
26
40
  existing.sourceHead === attempted.sourceHead &&
@@ -37,6 +51,29 @@ export function matchesExactDevDeliveryCandidate(existing, attempted) {
37
51
  attempted.releaseBlockerPriority?.claimRoot
38
52
  );
39
53
  }
54
+ export function reuseExactActiveDevDeliverySourceProof(active, input) {
55
+ const phaseCompatible =
56
+ ["provisional", "qualified"].includes(active?.phase) ||
57
+ (!Object.hasOwn(active || {}, "phase") &&
58
+ active?.deliveryClass === "non-native-fast" &&
59
+ !Object.hasOwn(active || {}, "environmentRoot"));
60
+ const exact =
61
+ phaseCompatible &&
62
+ ["sourceRoot", "assignmentRoot", "initiativeRoot"].every(
63
+ (field) => active[field] === input[field],
64
+ ) &&
65
+ [
66
+ "pullRequestNumber",
67
+ "sourceIdentityRoot",
68
+ "deliveryClass",
69
+ "priority",
70
+ ].every((field) => active[field] === input[field]) &&
71
+ matchesExactDevDeliveryCandidate(active, {
72
+ ...input,
73
+ sourceProofRoot: active.sourceProofRoot,
74
+ });
75
+ return exact ? { ...input, sourceProofRoot: active.sourceProofRoot } : input;
76
+ }
40
77
 
41
78
  export function createDevDeliveryCandidateIdentity(
42
79
  input,
@@ -50,8 +87,7 @@ export function createDevDeliveryCandidateIdentity(
50
87
  input.pullRequestNumber,
51
88
  "pullRequestNumber",
52
89
  ),
53
- assignmentRoot: exactRoot(input.assignmentRoot, "assignmentRoot"),
54
- initiativeRoot: exactRoot(input.initiativeRoot, "initiativeRoot"),
90
+ ...devDeliverySourceBinding(input),
55
91
  sourceIdentityRoot: exactRoot(
56
92
  input.sourceIdentityRoot,
57
93
  "sourceIdentityRoot",
@@ -59,11 +95,10 @@ export function createDevDeliveryCandidateIdentity(
59
95
  deliveryClass: deliveryClass(input.deliveryClass),
60
96
  };
61
97
  if (input.identitySemantics) {
62
- if (text(input.identitySemantics) !== CHAINED_ATTEMPT_IDENTITY) {
98
+ if (text(input.identitySemantics) !== CHAINED_ATTEMPT_IDENTITY)
63
99
  throw new Error(
64
100
  `unsupported candidate identity semantics ${input.identitySemantics}`,
65
101
  );
66
- }
67
102
  identity.identitySemantics = CHAINED_ATTEMPT_IDENTITY;
68
103
  identity.predecessorCandidateId = exactRoot(
69
104
  input.predecessorCandidateId,
@@ -72,7 +107,6 @@ export function createDevDeliveryCandidateIdentity(
72
107
  }
73
108
  return { ...identity, candidateId: devDeliveryContentRoot(identity) };
74
109
  }
75
-
76
110
  export function validateDevDeliveryCandidateChain(candidates, terminalStates) {
77
111
  const precedingCandidates = new Map();
78
112
  const latestByPullRequest = new Map();
@@ -12,6 +12,7 @@ import {
12
12
  import {
13
13
  chainedDevDeliveryAttemptInput,
14
14
  createDevDeliveryCandidateIdentity,
15
+ devDeliverySourceBinding,
15
16
  EXACT_DEV_DELIVERY_PROOF_FIELDS,
16
17
  matchesExactDevDeliveryCandidate,
17
18
  validateDevDeliveryCandidateChain,
@@ -86,8 +87,7 @@ export function issueDevDeliveryWarrant(
86
87
  candidateId: candidate.candidateId,
87
88
  pullRequestNumber: candidate.pullRequestNumber,
88
89
  sourceHead: candidate.sourceHead,
89
- assignmentRoot: candidate.assignmentRoot,
90
- initiativeRoot: candidate.initiativeRoot,
90
+ ...devDeliverySourceBinding(candidate),
91
91
  sourceIdentityRoot: candidate.sourceIdentityRoot,
92
92
  sourcePatchRoot: candidate.sourcePatchRoot,
93
93
  sourceProofRoot: candidate.sourceProofRoot,
@@ -413,8 +413,7 @@ function submissionReceipt({ before, after, candidate, action, now }) {
413
413
  candidateId: candidate.candidateId,
414
414
  pullRequestNumber: candidate.pullRequestNumber,
415
415
  sourceHead: candidate.sourceHead,
416
- assignmentRoot: candidate.assignmentRoot,
417
- initiativeRoot: candidate.initiativeRoot,
416
+ ...devDeliverySourceBinding(candidate),
418
417
  sourceIdentityRoot: candidate.sourceIdentityRoot,
419
418
  sourcePatchRoot: candidate.sourcePatchRoot,
420
419
  sourceProofRoot: candidate.sourceProofRoot,
@@ -198,8 +198,8 @@ export function selectDevDeliveryWarrant(
198
198
  candidateId: transaction.result.candidate.candidateId,
199
199
  pullRequestNumber: transaction.result.candidate.pullRequestNumber,
200
200
  sourceHead: transaction.result.candidate.sourceHead,
201
- assignmentRoot: transaction.result.candidate.assignmentRoot,
202
- initiativeRoot: transaction.result.candidate.initiativeRoot,
201
+ ...candidateSourceBinding(transaction.result.candidate),
202
+
203
203
  sourceIdentityRoot: transaction.result.candidate.sourceIdentityRoot,
204
204
  sourcePatchRoot: transaction.result.candidate.sourcePatchRoot,
205
205
  sourceProofRoot: transaction.result.candidate.sourceProofRoot,
@@ -488,6 +488,7 @@ function createDevDeliverySuccessorWake(queue, now) {
488
488
  "candidateId",
489
489
  "pullRequestNumber",
490
490
  "sourceHead",
491
+ "sourceRoot",
491
492
  "assignmentRoot",
492
493
  "initiativeRoot",
493
494
  "sourceIdentityRoot",
@@ -586,3 +587,12 @@ export function observeDevDeliveryQueue(
586
587
  observedAt: currentTime,
587
588
  };
588
589
  }
590
+
591
+ function candidateSourceBinding(candidate) {
592
+ return candidate.sourceRoot
593
+ ? { sourceRoot: candidate.sourceRoot }
594
+ : {
595
+ assignmentRoot: candidate.assignmentRoot,
596
+ initiativeRoot: candidate.initiativeRoot,
597
+ };
598
+ }
@@ -118,12 +118,12 @@ const PROTECTED_AUTHORITY_PATHS = Object.freeze([
118
118
  ".github/CODEOWNERS",
119
119
  ".github/workflows/.publication-authority.yml",
120
120
  ".github/workflows/.release-candidate-promote.yml",
121
- ".github/workflows/buildchain-ref-promotion-recovery.yml",
122
- ".github/workflows/buildchain-ref-promotion.yml",
123
- ".github/workflows/github-governance-audit.yml",
121
+ ".github/workflows/self-ops-promotion-recovery.yml",
122
+ ".github/workflows/self-release-promote.yml",
123
+ ".github/workflows/self-ops-governance-audit.yml",
124
124
  ".github/workflows/paper-release-sealed.yml",
125
125
  ".github/workflows/paper-release.yml",
126
- ".github/workflows/release-line-bootstrap.yml",
126
+ ".github/workflows/self-release-line-open.yml",
127
127
  ".github/workflows/release-candidate-promote.yml",
128
128
  "actions/promote-buildchain-ref/action.yml",
129
129
  "actions/promote-buildchain-ref/dist/index.js",
@@ -21,7 +21,7 @@ export const KFD_LEGACY_SUPPORT_MATRIX_CONTRACT = "kungfu-kfd-support-matrix";
21
21
 
22
22
  const BUILDCHAIN_ADOPTER_ID = "kungfu-systems/buildchain";
23
23
  const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
24
- const SHA_PATTERN = /^[0-9a-f]{40}$/;
24
+ const SHA_PATTERN = /^[0-9a-f]{40}$/, SOURCE_REPOSITORY_PATTERN = /^[^@\s]+$/;
25
25
  const REQUIRED_DECISIONS = Object.freeze(["KFD-1", "KFD-2", "KFD-3", "KFD-4", "KFD-5", "KFD-7"]);
26
26
  const PRODUCT_GATE_STANDARDS = Object.freeze(["kfd-4", "kfd-5", "kfd-7"]);
27
27
  const PRODUCT_GATE_SET = new Set(PRODUCT_GATE_STANDARDS);
@@ -77,29 +77,27 @@ function verifyPublishedManifest(manifest, packageOptions, issues) {
77
77
  }
78
78
  }
79
79
 
80
- function manifestSourceSha(manifest) {
81
- const match = /^kungfu-systems\/buildchain@([0-9a-f]{40})$/.exec(text(manifest?.adopter?.artifact?.coordinate));
82
- return match?.[1] || "";
83
- }
80
+ function adopterExpectation({ expectedAdopterId = BUILDCHAIN_ADOPTER_ID, expectedSourceRepository = "" } = {}) { const adopterId = text(expectedAdopterId) || BUILDCHAIN_ADOPTER_ID; return { adopterId, sourceRepository: text(expectedSourceRepository) || adopterId }; }
84
81
 
85
- function validateManifestIdentity(manifest, packageArtifactRoot, expectedSourceSha, issues) {
86
- if (manifest?.adopter?.id !== BUILDCHAIN_ADOPTER_ID) {
87
- issues.push(issue("adopter-identity", "manifest.adopter.id", `manifest adopter must be ${BUILDCHAIN_ADOPTER_ID}`));
88
- }
82
+ function manifestSource(manifest) { const coordinate = text(manifest?.adopter?.artifact?.coordinate), match = /^([^@\s]+)@([0-9a-f]{40})$/.exec(coordinate); return { coordinate, repository: match?.[1] || "", sha: match?.[2] || "" }; }
83
+
84
+ function validateManifestIdentity(manifest, packageArtifactRoot, expectation, expectedSourceSha, issues) {
85
+ if (manifest?.adopter?.id !== expectation.adopterId) issues.push(issue("adopter-identity", "manifest.adopter.id", `manifest adopter must be ${expectation.adopterId}`));
89
86
  const pinned = manifest?.kfdCut?.package;
90
87
  if (pinned?.name !== kfdPackageJson.name
91
88
  || pinned?.version !== kfdPackageJson.version
92
89
  || pinned?.artifactRoot !== packageArtifactRoot) {
93
90
  issues.push(issue("adopter-package-cut", "manifest.kfdCut.package", "manifest must bind the exact installed KFD package cut"));
94
91
  }
95
- const sourceSha = manifestSourceSha(manifest);
96
- if (manifest?.adopter?.artifact?.kind !== "git-commit" || !sourceSha
97
- || !ROOT_PATTERN.test(text(manifest?.adopter?.artifact?.root))) {
98
- issues.push(issue("adopter-source", "manifest.adopter.artifact", "Buildchain adopter authority must bind one exact git commit and artifact root"));
99
- } else if (expectedSourceSha && sourceSha !== expectedSourceSha) {
92
+ const source = manifestSource(manifest);
93
+ if (!SOURCE_REPOSITORY_PATTERN.test(expectation.sourceRepository) || manifest?.adopter?.artifact?.kind !== "git-commit" || !source.sha || !ROOT_PATTERN.test(text(manifest?.adopter?.artifact?.root))) {
94
+ issues.push(issue("adopter-source", "manifest.adopter.artifact", "adopter authority must bind one exact repository, git commit, and artifact root"));
95
+ } else if (source.repository !== expectation.sourceRepository) {
96
+ issues.push(issue("adopter-source", "manifest.adopter.artifact.coordinate", `manifest source repository must be ${expectation.sourceRepository}`));
97
+ } else if (expectedSourceSha && source.sha !== expectedSourceSha) {
100
98
  issues.push(issue("adopter-source", "manifest.adopter.artifact.coordinate", "manifest source does not match the requested release source"));
101
99
  }
102
- return sourceSha;
100
+ return source;
103
101
  }
104
102
 
105
103
  function validateRequiredDecisions(decisions, issues) {
@@ -131,7 +129,7 @@ function collectProductGates(gateResults, decisions, verificationCut, issues) {
131
129
  for (const [index, gate] of gateResults.entries()) {
132
130
  const validation = validateKfdProductGateResult(gate, verificationCut);
133
131
  if (!validation.valid) {
134
- issues.push(issue("adopter-gate-invalid", `gateResults[${index}]`, "Buildchain product gate is invalid", {
132
+ issues.push(issue("adopter-gate-invalid", `gateResults[${index}]`, "adopter product gate is invalid", {
135
133
  gateIssues: validation.issues,
136
134
  }));
137
135
  }
@@ -141,15 +139,16 @@ function collectProductGates(gateResults, decisions, verificationCut, issues) {
141
139
  if (gates.has(gate?.standard)) {
142
140
  issues.push(issue("adopter-gate-duplicate", `gateResults[${index}].standard`, `${gate.standard} gate is duplicated`));
143
141
  }
142
+ if (gate?.source?.repository !== verificationCut.expectedSourceRepository) issues.push(issue("adopter-gate-source", `gateResults[${index}].source.repository`, "product gate repository must match the adopter source repository"));
144
143
  gates.set(gate?.standard, gate);
145
144
  }
146
145
  for (const standard of PRODUCT_GATE_STANDARDS) {
147
146
  const gate = gates.get(standard);
148
147
  const id = standard.toUpperCase();
149
148
  if (!gate) {
150
- issues.push(issue("adopter-gate-missing", `gateResults.${standard}`, `${id} requires its existing Buildchain product gate`));
149
+ issues.push(issue("adopter-gate-missing", `gateResults.${standard}`, `${id} requires its exact adopter product gate`));
151
150
  } else if (!(decisions.get(id)?.verificationEvidence || []).some((entry) => entry?.root === gate.gateRoot)) {
152
- issues.push(issue("adopter-gate-unbound", `manifest.decisions.${id}.verificationEvidence`, `${id} must bind the exact Buildchain gate root`));
151
+ issues.push(issue("adopter-gate-unbound", `manifest.decisions.${id}.verificationEvidence`, `${id} must bind the exact adopter gate root`));
153
152
  }
154
153
  }
155
154
  return gates;
@@ -159,12 +158,12 @@ function projectedProductGates(gates) {
159
158
  return PRODUCT_GATE_STANDARDS.map((standard) => {
160
159
  const gate = gates.get(standard);
161
160
  return gate
162
- ? { standard, sourceSha: gate.source.sha, gateRoot: gate.gateRoot, status: gate.status }
163
- : { standard, sourceSha: "", gateRoot: "", status: "missing" };
161
+ ? { standard, sourceRepository: gate.source.repository, sourceSha: gate.source.sha, gateRoot: gate.gateRoot, status: gate.status }
162
+ : { standard, sourceRepository: "", sourceSha: "", gateRoot: "", status: "missing" };
164
163
  });
165
164
  }
166
165
 
167
- function createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, sourceSha, checkedAt, maxAgeSeconds, issues }) {
166
+ function createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, source, checkedAt, maxAgeSeconds, issues }) {
168
167
  const gate = {
169
168
  schemaVersion: 1,
170
169
  contract: KFD_ADOPTER_MANIFEST_GATE_CONTRACT,
@@ -174,10 +173,8 @@ function createGateDocument({ manifest, bundle, gates, authorityPath, packageArt
174
173
  contract: text(manifest?.contract),
175
174
  manifestRoot: bundle?.roots?.manifestRoot || "",
176
175
  },
177
- source: {
178
- sha: sourceSha,
179
- artifactRoot: text(manifest?.adopter?.artifact?.root),
180
- },
176
+ adopter: { id: text(manifest?.adopter?.id) },
177
+ source: { repository: source.repository, coordinate: source.coordinate, sha: source.sha, artifactRoot: text(manifest?.adopter?.artifact?.root) },
181
178
  verificationCut: { checkedAt, maxAgeSeconds },
182
179
  standardPackage: {
183
180
  name: kfdPackageJson.name,
@@ -211,28 +208,28 @@ export function createKfdAdopterManifestGate({
211
208
  packageArtifactRoot = "",
212
209
  gateResults = [],
213
210
  authorityPath = ".buildchain/kfd/adopter-manifest.json",
214
- expectedSourceSha = "",
211
+ expectedAdopterId = BUILDCHAIN_ADOPTER_ID, expectedSourceRepository = "", expectedSourceSha = "",
215
212
  checkedAt = new Date().toISOString(),
216
213
  maxAgeSeconds = 86400,
217
214
  } = {}) {
218
- const issues = [];
215
+ const issues = [], expectation = adopterExpectation({ expectedAdopterId, expectedSourceRepository });
219
216
  const bundle = verifyPublishedManifest(manifest, {
220
217
  packageArtifactRoot,
221
218
  verifiedAt: checkedAt,
222
219
  maxAgeSeconds,
223
220
  }, issues);
224
221
  const decisions = new Map((manifest?.decisions || []).map((row) => [row.id, row]));
225
- let sourceSha = manifestSourceSha(manifest);
222
+ let source = manifestSource(manifest);
226
223
  if (bundle) {
227
- sourceSha = validateManifestIdentity(manifest, packageArtifactRoot, expectedSourceSha, issues);
224
+ source = validateManifestIdentity(manifest, packageArtifactRoot, expectation, expectedSourceSha, issues);
228
225
  validateRequiredDecisions(decisions, issues);
229
226
  validateWarrantWitness(decisions, issues);
230
227
  }
231
- const gates = collectProductGates(gateResults, decisions, { expectedSourceSha: sourceSha || expectedSourceSha, checkedAt }, issues);
232
- return createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, sourceSha, checkedAt, maxAgeSeconds, issues });
228
+ const gates = collectProductGates(gateResults, decisions, { expectedSourceRepository: expectation.sourceRepository, expectedSourceSha: source.sha || expectedSourceSha, checkedAt }, issues);
229
+ return createGateDocument({ manifest, bundle, gates, authorityPath, packageArtifactRoot, source, checkedAt, maxAgeSeconds, issues });
233
230
  }
234
231
 
235
- function validateGateDocument(gate, issues) {
232
+ function validateGateDocument(gate, expectation, issues) {
236
233
  const copy = structuredClone(gate);
237
234
  const root = copy.gateRoot;
238
235
  delete copy.gateRoot;
@@ -244,12 +241,13 @@ function validateGateDocument(gate, issues) {
244
241
  || !ROOT_PATTERN.test(text(gate?.authority?.manifestRoot))) {
245
242
  issues.push(issue("adopter-gate-authority", "authority", "adopter gate must bind the standard manifest contract and root"));
246
243
  }
244
+ if (gate?.adopter?.id !== expectation.adopterId) issues.push(issue("adopter-gate-identity", "adopter.id", `adopter gate must bind ${expectation.adopterId}`));
247
245
  if (gate?.standardPackage?.name !== kfdPackageJson.name || gate?.standardPackage?.version !== kfdPackageJson.version
248
246
  || ![gate?.standardPackage?.artifactRoot, gate?.standardPackage?.registryRoot, gate?.standardPackage?.verifierSetRoot].every((value) => ROOT_PATTERN.test(text(value)))) {
249
247
  issues.push(issue("adopter-gate-package", "standardPackage", "adopter gate uses stale KFD package metadata"));
250
248
  }
251
- if (!SHA_PATTERN.test(text(gate?.source?.sha)) || !ROOT_PATTERN.test(text(gate?.source?.artifactRoot))) {
252
- issues.push(issue("adopter-gate-source", "source", "adopter gate must bind one exact Buildchain source commit and artifact root"));
249
+ if (gate?.source?.repository !== expectation.sourceRepository || gate?.source?.coordinate !== `${expectation.sourceRepository}@${gate?.source?.sha || ""}` || !SHA_PATTERN.test(text(gate?.source?.sha)) || !ROOT_PATTERN.test(text(gate?.source?.artifactRoot))) {
250
+ issues.push(issue("adopter-gate-source", "source", "adopter gate must bind the expected repository, exact source commit, coordinate, and artifact root"));
253
251
  }
254
252
  if (gate?.decisionWitness?.rootAlgorithm !== "sha256-buildchain-stable-json-v1"
255
253
  || !ROOT_PATTERN.test(text(gate?.decisionWitness?.root))
@@ -259,7 +257,7 @@ function validateGateDocument(gate, issues) {
259
257
  }
260
258
  }
261
259
 
262
- function validateProjectedGates(gate, expectedSourceSha, issues) {
260
+ function validateProjectedGates(gate, expectation, expectedSourceSha, issues) {
263
261
  const seenStandards = new Set();
264
262
  for (const [index, productGate] of (gate.gateResults || []).entries()) {
265
263
  if (!PRODUCT_GATE_SET.has(productGate?.standard) || productGate?.status !== "passed" || !ROOT_PATTERN.test(text(productGate?.gateRoot))) {
@@ -267,7 +265,7 @@ function validateProjectedGates(gate, expectedSourceSha, issues) {
267
265
  }
268
266
  if (seenStandards.has(productGate?.standard)) issues.push(issue("adopter-gate-result-set", `gateResults[${index}]`, "product gate standards must be unique"));
269
267
  seenStandards.add(productGate?.standard);
270
- if (productGate?.sourceSha !== gate?.source?.sha || (expectedSourceSha && productGate?.sourceSha !== expectedSourceSha)) {
268
+ if (productGate?.sourceRepository !== gate?.source?.repository || productGate?.sourceRepository !== expectation.sourceRepository || productGate?.sourceSha !== gate?.source?.sha || (expectedSourceSha && productGate?.sourceSha !== expectedSourceSha)) {
271
269
  issues.push(issue("adopter-gate-source", `gateResults[${index}].sourceSha`, "product gate source must match the release source"));
272
270
  }
273
271
  }
@@ -277,15 +275,15 @@ function validateProjectedGates(gate, expectedSourceSha, issues) {
277
275
  }
278
276
 
279
277
  export function validateKfdAdopterManifestGate(gate, {
280
- expectedSourceSha = "",
278
+ expectedAdopterId = BUILDCHAIN_ADOPTER_ID, expectedSourceRepository = "", expectedSourceSha = "",
281
279
  checkedAt = gate?.checkedAt || new Date().toISOString(),
282
280
  } = {}) {
283
- const issues = [];
281
+ const issues = [], expectation = adopterExpectation({ expectedAdopterId, expectedSourceRepository });
284
282
  if (!gate || gate.schemaVersion !== 1 || gate.contract !== KFD_ADOPTER_MANIFEST_GATE_CONTRACT) {
285
283
  return { valid: false, issues: [issue("adopter-gate-contract", "", `gate must use ${KFD_ADOPTER_MANIFEST_GATE_CONTRACT} v1`)] };
286
284
  }
287
- validateGateDocument(gate, issues);
288
- validateProjectedGates(gate, expectedSourceSha, issues);
285
+ validateGateDocument(gate, expectation, issues);
286
+ validateProjectedGates(gate, expectation, expectedSourceSha, issues);
289
287
  if (gate.checkedAt !== checkedAt || gate?.verificationCut?.checkedAt !== checkedAt || !Number.isFinite(Date.parse(checkedAt))
290
288
  || !Number.isSafeInteger(gate?.verificationCut?.maxAgeSeconds) || gate.verificationCut.maxAgeSeconds < 0) {
291
289
  issues.push(issue("adopter-gate-time", "verificationCut", "adopter gate verification cut does not match the requested cut"));
@@ -300,7 +298,7 @@ function legacyStatus(row) {
300
298
  return row.state;
301
299
  }
302
300
 
303
- function legacyRow(row, gates) {
301
+ function legacyRow(row, gates, adopterId) {
304
302
  const key = row.id.toLowerCase();
305
303
  const productGate = gates.get(key);
306
304
  return {
@@ -318,7 +316,7 @@ function legacyRow(row, gates) {
318
316
  releaseQualification: { shippedSupport: false },
319
317
  claimClass: "standard-adopter-manifest-projection",
320
318
  knownLimitations: [...row.gaps],
321
- owner: BUILDCHAIN_ADOPTER_ID,
319
+ owner: adopterId,
322
320
  nextGate: "Release Passport artifact binding and independent release decision",
323
321
  declaration: { state: row.state, usage: row.usage, root: kfdProductGateDigest(decisionWitnessRow(row)) },
324
322
  };
@@ -326,7 +324,7 @@ function legacyRow(row, gates) {
326
324
 
327
325
  export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate } = {}) {
328
326
  const validation = validateKfdAdopterManifestGate(manifestGate, {
329
- expectedSourceSha: manifestGate?.source?.sha,
327
+ expectedAdopterId: manifestGate?.adopter?.id, expectedSourceRepository: manifestGate?.source?.repository, expectedSourceSha: manifestGate?.source?.sha,
330
328
  checkedAt: manifestGate?.checkedAt,
331
329
  });
332
330
  if (!validation.valid || manifest?.contract !== "kfd.adopter-conformance-manifest/v1") {
@@ -338,9 +336,9 @@ export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate
338
336
  verifiedAt: manifestGate.verificationCut.checkedAt,
339
337
  maxAgeSeconds: manifestGate.verificationCut.maxAgeSeconds,
340
338
  }, authorityIssues);
341
- const sourceSha = validateManifestIdentity(manifest, manifestGate.standardPackage.artifactRoot, manifestGate.source.sha, authorityIssues);
339
+ const source = validateManifestIdentity(manifest, manifestGate.standardPackage.artifactRoot, { adopterId: manifestGate.adopter.id, sourceRepository: manifestGate.source.repository }, manifestGate.source.sha, authorityIssues);
342
340
  const decisionRoot = kfdProductGateDigest(manifest.decisions.map(decisionWitnessRow));
343
- if (!bundle || sourceSha !== manifestGate.source.sha || manifest.adopter.artifact.root !== manifestGate.source.artifactRoot
341
+ if (!bundle || source.sha !== manifestGate.source.sha || source.repository !== manifestGate.source.repository || source.coordinate !== manifestGate.source.coordinate || manifest.adopter.artifact.root !== manifestGate.source.artifactRoot
344
342
  || bundle.roots.manifestRoot !== manifestGate.authority.manifestRoot
345
343
  || bundle.roots.verificationReportRoot !== manifestGate.manifestVerificationReportRoot
346
344
  || bundle.bundleRoot !== manifestGate.manifestBundleRoot
@@ -364,7 +362,8 @@ export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate
364
362
  path: manifestGate.authority.path,
365
363
  contract: manifest.contract,
366
364
  root: manifestGate.authority.manifestRoot,
367
- gateRoot: manifestGate.gateRoot,
365
+ gateRoot: manifestGate.gateRoot, adopterId: manifestGate.adopter.id,
366
+ sourceRepository: manifestGate.source.repository, sourceSha: manifestGate.source.sha,
368
367
  },
369
368
  upstream: {
370
369
  package: manifestGate.standardPackage.name,
@@ -374,7 +373,7 @@ export function createKfdLegacySupportMatrixProjection({ manifest, manifestGate
374
373
  verifierSetRoot: manifestGate.standardPackage.verifierSetRoot,
375
374
  standardsSha256: standardsFileDigest(),
376
375
  },
377
- rows: manifest.decisions.map((row) => legacyRow(row, gates)),
376
+ rows: manifest.decisions.map((row) => legacyRow(row, gates, manifestGate.adopter.id)),
378
377
  };
379
378
  }
380
379
 
@@ -243,8 +243,8 @@ invalid.
243
243
 
244
244
  ## Public self-dogfood and adoption
245
245
 
246
- \`.github/workflows/v4-public-consumer-dogfood.yml\` remains the exact thin
247
- consumer of the public \`v4-stage-capsule-canary.yml@v4-alpha\` contract. Its
246
+ \`.github/workflows/self-build-public-consumer-dogfood.yml\` remains the exact thin
247
+ consumer of the public \`public-build-stage-capsule-canary.yml@v4-alpha\` contract. Its
248
248
  declared \`lifecycle.verify\` runs \`pnpm run check\`, so the same public path
249
249
  executes pure recovery tests that inject a transient durable-state write
250
250
  failure, restore the adapter operation on a fresh runner, supersede stale work
@@ -159,15 +159,14 @@ function normalizeModel(value = {}) {
159
159
  return model;
160
160
  }
161
161
 
162
- function normalizeCompletedAlpha(value = {}) {
162
+ function normalizeCompletedAlpha(value = {}, model) {
163
163
  const version = String(value.version || "").trim();
164
- if (!ALPHA.test(version))
164
+ const expression = model?.strategy === "anchored" ? /-alpha\.(0|[1-9]\d*)$/u : ALPHA;
165
+ if (!SEMVER.test(version) || !expression.test(version))
165
166
  throw new Error("completedAlpha.version must be an alpha semantic version");
166
167
  const exactTag = String(value.exactTag || "").trim();
167
168
  if (exactTag !== `v${version}`)
168
- throw new Error(
169
- "completedAlpha.exactTag must match completedAlpha.version",
170
- );
169
+ throw new Error("completedAlpha.exactTag must match completedAlpha.version");
171
170
  const normalized = {
172
171
  outcome: String(value.outcome || "").trim(),
173
172
  version,
@@ -251,8 +250,8 @@ function initialStateRoot(status, idempotencyKey) {
251
250
  }
252
251
 
253
252
  export function createNextDevelopmentTransition(input = {}) {
254
- const completedAlpha = normalizeCompletedAlpha(input.completedAlpha);
255
253
  const model = normalizeModel(input.model);
254
+ const completedAlpha = normalizeCompletedAlpha(input.completedAlpha, model);
256
255
  const adapter = normalizeAdapter(input);
257
256
  const declaredPaths = [
258
257
  ...adapter.sourcePaths,