@kungfu-tech/buildchain 4.0.1-alpha.5 → 4.0.1-alpha.55

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 (159) hide show
  1. package/README.md +9 -6
  2. package/actions/release-tail/README.md +8 -0
  3. package/actions/v4-release-candidate-promote/README.md +3 -0
  4. package/architecture/agent-change-map.md +1015 -0
  5. package/architecture/ci-lane-change-budget.json +256 -0
  6. package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
  7. package/architecture/internal-capabilities.json +220 -15
  8. package/architecture/maintainability-debt.json +3229 -0
  9. package/architecture/maintainability-policy.json +489 -132
  10. package/architecture/release-tail-contract-inventory.json +5 -2
  11. package/architecture/v3-core-mechanism-inventory.json +4 -3
  12. package/architecture/v3-v4-capability-closure.md +59 -0
  13. package/architecture/v3-v4-live-capability-inventory.json +102404 -0
  14. package/architecture/v4-adopter-delivery-parity.json +32 -5
  15. package/architecture/v4-cross-platform-adopter-qualification.json +52 -0
  16. package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
  17. package/architecture/v4-floating-consumer-policy.json +6 -0
  18. package/architecture/v4-publication-rehearsal-parity.json +84 -0
  19. package/architecture/v4-runtime-semantic-closure.json +710 -0
  20. package/bin/buildchain.mjs +7 -1
  21. package/bin/internal/adopter-delivery-cli.mjs +70 -0
  22. package/bin/internal/command-registry.mjs +1 -0
  23. package/bin/internal/trust-release-release-handlers.mjs +5 -0
  24. package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
  25. package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
  26. package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
  27. package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
  28. package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
  29. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
  30. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
  31. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
  32. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
  33. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
  34. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
  35. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
  36. package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
  37. package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
  38. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  39. package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
  40. package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
  41. package/contracts/v4-publication-qualification-v1.schema.json +51 -0
  42. package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  43. package/dist/site/agent-index.json +4 -0
  44. package/dist/site/artifact-schemas.json +8 -0
  45. package/dist/site/buildchain-contract.json +125 -34
  46. package/dist/site/buildchain-site.json +308 -33
  47. package/dist/site/capability-registry.json +13 -12
  48. package/dist/site/cli-registry.json +57 -2
  49. package/dist/site/controller-registry.json +41 -5
  50. package/dist/site/kfd-claims.json +438 -24
  51. package/dist/site/kfd-upstream-aggregate.json +1 -1
  52. package/dist/site/manual-registry.json +22 -7
  53. package/dist/site/node-api-registry.json +12843 -9314
  54. package/dist/site/page-registry.json +288 -21
  55. package/dist/site/public-surface-audit.json +281 -21
  56. package/dist/site/publication-authority-registry.json +66 -8
  57. package/dist/site/publication-registry.json +4 -4
  58. package/dist/site/release-provenance.json +20 -0
  59. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
  60. package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
  61. package/dist/site/schemas/v4-publication-qualification-v1.schema.json +51 -0
  62. package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  63. package/dist/site/site-manifest.json +19 -11
  64. package/dist/site/workflow-registry.json +195 -17
  65. package/docs/MAP.md +4 -2
  66. package/docs/cli-reference.md +70 -2
  67. package/docs/cli.md +8 -0
  68. package/docs/dev-delivery-warrant.md +34 -10
  69. package/docs/node-api-reference.md +524 -279
  70. package/docs/publish-transaction.md +11 -4
  71. package/docs/release-candidate.md +7 -0
  72. package/docs/release-tail-provider-plane.md +30 -0
  73. package/docs/v4-adopter-delivery.md +152 -0
  74. package/docs/v4-declarative-release-tail-migration.md +43 -0
  75. package/docs/v4-publication-rehearsal.md +130 -0
  76. package/package.json +24 -4
  77. package/packages/core/adopter-delivery-passport.js +295 -0
  78. package/packages/core/adopter-delivery-vectors.js +158 -0
  79. package/packages/core/adopter-delivery-vectors.json +77 -0
  80. package/packages/core/buildchain-agent-manuals.js +1 -0
  81. package/packages/core/buildchain-config.js +84 -0
  82. package/packages/core/buildchain-contract.js +31 -1
  83. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  84. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  85. package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
  86. package/packages/core/buildchain-publication-authority.js +2 -0
  87. package/packages/core/ci-lane-change-budget.js +247 -0
  88. package/packages/core/dev-delivery-native-proof.js +43 -27
  89. package/packages/core/dev-delivery-warrant-settlement.js +239 -3
  90. package/packages/core/dev-delivery-warrant-state.js +46 -10
  91. package/packages/core/dev-delivery-warrant.js +29 -7
  92. package/packages/core/github-governance-authority.js +5 -4
  93. package/packages/core/index.js +6 -0
  94. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  95. package/packages/core/paper-scaffold-content.js +401 -0
  96. package/packages/core/paper.js +12 -378
  97. package/packages/core/publication-control-plane-audit.js +135 -111
  98. package/packages/core/publication-rehearsal-projection.js +104 -0
  99. package/packages/core/publication-rehearsal-runtime.js +190 -0
  100. package/packages/core/published-delivery-authority.js +266 -0
  101. package/packages/core/release-candidate-recovery.js +71 -15
  102. package/packages/core/release-candidate.js +2 -2
  103. package/packages/core/release-passport.js +95 -170
  104. package/packages/core/v4-adopter-delivery-parity.js +3 -3
  105. package/packages/core/v4-adopter-delivery.js +359 -0
  106. package/packages/core/v4-cross-platform-adopter-qualification.js +343 -0
  107. package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
  108. package/packages/core/v4-floating-consumer-evidence.js +18 -4
  109. package/packages/core/v4-floating-consumer-policy.js +139 -35
  110. package/packages/core/v4-floating-consumer-release-passport.js +154 -8
  111. package/packages/core/v4-publication-qualification.js +222 -0
  112. package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
  113. package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
  114. package/packages/core/v4-publication-rehearsal.js +481 -0
  115. package/packages/core/workflow-yaml-contract.js +24 -2
  116. package/scripts/artifact-signing-controller-core.mjs +1 -1
  117. package/scripts/audit-publication-control-plane.mjs +17 -17
  118. package/scripts/buildchain-cli-help.mjs +13 -0
  119. package/scripts/check-ci-lane-change-budget.mjs +100 -0
  120. package/scripts/check-internal-architecture.mjs +23 -3
  121. package/scripts/check-inventory.mjs +24 -6
  122. package/scripts/check-maintainability.mjs +145 -4
  123. package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
  124. package/scripts/check-v3-v4-capability-inventory.mjs +574 -0
  125. package/scripts/check-v4-floating-consumer-policy-contract.mjs +30 -4
  126. package/scripts/check-v4-runtime-semantic-closure.mjs +266 -0
  127. package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
  128. package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
  129. package/scripts/dev-delivery-warrant-options.mjs +27 -11
  130. package/scripts/dev-delivery-warrant-store.mjs +10 -6
  131. package/scripts/dev-delivery-warrant.mjs +50 -6
  132. package/scripts/dev-pr-auto-merge.mjs +25 -11
  133. package/scripts/dev-pr-delivery-warrant.mjs +45 -0
  134. package/scripts/dev-pr-prequeue-guard.mjs +0 -18
  135. package/scripts/dev-qualification-patrol.mjs +9 -9
  136. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  137. package/scripts/generate-agent-change-map.mjs +118 -0
  138. package/scripts/generate-channel-promotion-workflow.mjs +2 -2
  139. package/scripts/generate-release-candidate-passport.mjs +177 -0
  140. package/scripts/generate-site-bundle.mjs +36 -0
  141. package/scripts/maintainability-domain-metrics.mjs +237 -0
  142. package/scripts/maintainability-governance.mjs +473 -0
  143. package/scripts/maintainability-metrics.mjs +28 -6
  144. package/scripts/project-cut-merge-queue-admission.mjs +394 -0
  145. package/scripts/promotion-channel-router.mjs +4 -4
  146. package/scripts/promotion-routing-evidence.mjs +35 -6
  147. package/scripts/release-candidate-anchor-provenance.mjs +99 -0
  148. package/scripts/release-candidate-resolver.mjs +44 -0
  149. package/scripts/release-line-policy.mjs +25 -15
  150. package/scripts/release-tail.mjs +68 -3
  151. package/scripts/resume-from-candidate-run.mjs +595 -65
  152. package/scripts/run-lifecycle-core.mjs +341 -376
  153. package/scripts/site-capability-metadata.mjs +21 -0
  154. package/scripts/stable-candidate-qualification.mjs +9 -10
  155. package/scripts/v3-v4-capability-catalog.mjs +439 -0
  156. package/scripts/v4-consumer-policy.mjs +4 -0
  157. package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
  158. package/scripts/v4-declarative-promotion-admission.mjs +42 -0
  159. package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
@@ -0,0 +1,295 @@
1
+ import {
2
+ ADOPTER_DELIVERY_GATE_RESULT_CONTRACT,
3
+ adopterDeliveryGateDigest,
4
+ } from "./adopter-delivery-gate.js";
5
+
6
+ export const ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT =
7
+ "kungfu-buildchain-adopter-delivery-passport-binding";
8
+
9
+ const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
10
+ const EXACT_SHA_PATTERN = /^[0-9a-f]{40}$/u;
11
+
12
+ export function releasePassportKfdAdopterSourceSha({
13
+ passportSourceSha = "",
14
+ release = undefined,
15
+ } = {}) {
16
+ const exactSha = (value) =>
17
+ EXACT_SHA_PATTERN.test(String(value || "").trim());
18
+ const candidateSourceSha = String(release?.candidateSourceSha || "").trim();
19
+ const builtSourceSha = String(release?.builtSourceSha || "").trim();
20
+ const promotionChannelSha = String(release?.promotionChannelSha || "").trim();
21
+ const candidateSourceTreeSha = String(
22
+ release?.candidateSourceTreeSha || "",
23
+ ).trim();
24
+ const builtSourceTreeSha = String(release?.builtSourceTreeSha || "").trim();
25
+ const promotionChannelTreeSha = String(
26
+ release?.promotionChannelTreeSha || "",
27
+ ).trim();
28
+ if (
29
+ release?.treeEquivalent !== true ||
30
+ !exactSha(passportSourceSha) ||
31
+ !exactSha(candidateSourceSha) ||
32
+ !exactSha(builtSourceSha) ||
33
+ !exactSha(promotionChannelSha) ||
34
+ candidateSourceSha !== builtSourceSha ||
35
+ promotionChannelSha !== passportSourceSha ||
36
+ !exactSha(candidateSourceTreeSha) ||
37
+ !exactSha(builtSourceTreeSha) ||
38
+ !exactSha(promotionChannelTreeSha) ||
39
+ candidateSourceTreeSha !== builtSourceTreeSha ||
40
+ candidateSourceTreeSha !== promotionChannelTreeSha
41
+ ) {
42
+ return passportSourceSha;
43
+ }
44
+ return candidateSourceSha;
45
+ }
46
+
47
+ function issue(code, message, details = {}) {
48
+ return { code, message, details };
49
+ }
50
+
51
+ function validateRootedJson(value, rootField, code, label) {
52
+ const root = value?.[rootField];
53
+ if (!ROOT_PATTERN.test(root ?? "")) {
54
+ return [issue(code, `${label} must be a canonical sha256 root`)];
55
+ }
56
+ try {
57
+ const preimage = structuredClone(value);
58
+ delete preimage[rootField];
59
+ return adopterDeliveryGateDigest(preimage) === root
60
+ ? []
61
+ : [issue(code, `${label} does not match its exact JSON closure`)];
62
+ } catch {
63
+ return [issue(code, `${label} requires finite acyclic JSON`)];
64
+ }
65
+ }
66
+
67
+ function validateGateIdentity(result) {
68
+ const issues = [];
69
+ if (
70
+ result.schemaVersion !== 1 ||
71
+ result.contract !== ADOPTER_DELIVERY_GATE_RESULT_CONTRACT
72
+ ) {
73
+ issues.push(
74
+ issue(
75
+ "adopterDelivery.gateResult.contract",
76
+ "adopter delivery gate result contract is unsupported",
77
+ ),
78
+ );
79
+ }
80
+ if (
81
+ !result.project ||
82
+ !result.artifact ||
83
+ !result.protocol ||
84
+ !result.artifactProfile
85
+ ) {
86
+ issues.push(
87
+ issue(
88
+ "adopterDelivery.gateResult.closure",
89
+ "gate result must retain project, artifact, protocol, and artifact-profile bindings",
90
+ ),
91
+ );
92
+ }
93
+ if (!ROOT_PATTERN.test(result.artifact?.root ?? "")) {
94
+ issues.push(
95
+ issue(
96
+ "adopterDelivery.gateResult.artifact.root",
97
+ "delivery artifact root must be a canonical sha256 root",
98
+ ),
99
+ );
100
+ }
101
+ if (result.qualifying !== false || result.selfCertified !== false) {
102
+ issues.push(
103
+ issue(
104
+ "adopterDelivery.gateResult.nonClaims",
105
+ "delivery evidence must remain non-qualifying and non-self-certifying",
106
+ ),
107
+ );
108
+ }
109
+ return issues;
110
+ }
111
+
112
+ function validatePassedGate(result) {
113
+ if (result.status !== "passed") {
114
+ return result.status === "failed"
115
+ ? []
116
+ : [
117
+ issue(
118
+ "adopterDelivery.gateResult.status",
119
+ "gate status must be passed or failed",
120
+ ),
121
+ ];
122
+ }
123
+ const passed =
124
+ result.protocol?.loaded === true &&
125
+ ROOT_PATTERN.test(result.protocol?.reportRoot ?? "") &&
126
+ result.artifactProfile?.loaded === true &&
127
+ result.artifactProfile?.valid === true &&
128
+ result.semanticReport !== null &&
129
+ (result.issues?.length ?? -1) === 0;
130
+ return passed
131
+ ? []
132
+ : [
133
+ issue(
134
+ "adopterDelivery.gateResult.status",
135
+ "a passed gate must retain successful exact driver and artifact-profile evidence",
136
+ ),
137
+ ];
138
+ }
139
+
140
+ export function validateAdopterDeliveryGateResult(result) {
141
+ if (!result || typeof result !== "object" || Array.isArray(result)) {
142
+ return {
143
+ valid: false,
144
+ passed: false,
145
+ issues: [
146
+ issue(
147
+ "adopterDelivery.gateResult",
148
+ "adopter delivery gate result must be an object",
149
+ ),
150
+ ],
151
+ };
152
+ }
153
+ const issues = [
154
+ ...validateGateIdentity(result),
155
+ ...validateRootedJson(
156
+ result,
157
+ "gateRoot",
158
+ "adopterDelivery.gateResult.gateRoot",
159
+ "adopter delivery gate root",
160
+ ),
161
+ ...validatePassedGate(result),
162
+ ];
163
+ const valid = issues.length === 0;
164
+ return { valid, passed: valid && result.status === "passed", issues };
165
+ }
166
+
167
+ export function createAdopterDeliveryPassportBinding(gateResult) {
168
+ const normalized = structuredClone(gateResult);
169
+ const validation = validateAdopterDeliveryGateResult(normalized);
170
+ const binding = {
171
+ schemaVersion: 1,
172
+ contract: ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT,
173
+ valid: validation.passed,
174
+ qualifying: false,
175
+ selfCertified: false,
176
+ gateResult: normalized,
177
+ validationIssues: validation.issues,
178
+ nonClaims: [
179
+ "A passing adopter delivery gate does not grant runtime permission or release authority.",
180
+ "The selected protocol driver retains semantic authority; Buildchain only binds its exact result into delivery evidence.",
181
+ ],
182
+ };
183
+ binding.bindingRoot = adopterDeliveryGateDigest(binding);
184
+ return binding;
185
+ }
186
+
187
+ export function normalizeAdopterDeliveryPassportBinding(value) {
188
+ if (!value) return undefined;
189
+ return value.contract === ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT
190
+ ? structuredClone(value)
191
+ : createAdopterDeliveryPassportBinding(value);
192
+ }
193
+
194
+ export function validateAdopterDeliveryPassportBinding(
195
+ binding,
196
+ { expectedProjectRepository = "" } = {},
197
+ ) {
198
+ if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
199
+ return [
200
+ issue(
201
+ "adopterDelivery.binding",
202
+ "adopter delivery passport binding must be an object",
203
+ ),
204
+ ];
205
+ }
206
+ const gateValidation = validateAdopterDeliveryGateResult(binding.gateResult);
207
+ const issues = [
208
+ ...validateRootedJson(
209
+ binding,
210
+ "bindingRoot",
211
+ "adopterDelivery.binding.bindingRoot",
212
+ "adopter delivery binding root",
213
+ ),
214
+ ...gateValidation.issues,
215
+ ];
216
+ if (
217
+ binding.schemaVersion !== 1 ||
218
+ binding.contract !== ADOPTER_DELIVERY_PASSPORT_BINDING_CONTRACT
219
+ ) {
220
+ issues.push(
221
+ issue(
222
+ "adopterDelivery.binding.contract",
223
+ "adopter delivery passport binding contract is unsupported",
224
+ ),
225
+ );
226
+ }
227
+ if (
228
+ binding.valid !== gateValidation.passed ||
229
+ binding.qualifying !== false ||
230
+ binding.selfCertified !== false
231
+ ) {
232
+ issues.push(
233
+ issue(
234
+ "adopterDelivery.binding.status",
235
+ "binding status must preserve a passing non-authoritative gate result",
236
+ ),
237
+ );
238
+ }
239
+ if (!gateValidation.passed) {
240
+ issues.push(
241
+ issue(
242
+ "adopterDelivery.status",
243
+ "release passport requires a passed adopter delivery gate",
244
+ ),
245
+ );
246
+ }
247
+ if (
248
+ expectedProjectRepository &&
249
+ binding.gateResult?.project?.adopterId !== expectedProjectRepository
250
+ ) {
251
+ issues.push(
252
+ issue(
253
+ "adopterDelivery.project",
254
+ "delivery project must match the release passport product repository",
255
+ ),
256
+ );
257
+ }
258
+ return issues;
259
+ }
260
+
261
+ function sameJson(left, right) {
262
+ try {
263
+ return adopterDeliveryGateDigest(left) === adopterDeliveryGateDigest(right);
264
+ } catch {
265
+ return false;
266
+ }
267
+ }
268
+
269
+ export function validateAdopterDeliveryReleaseEvidence({
270
+ binding,
271
+ artifactBinding,
272
+ expectedProjectRepository = "",
273
+ } = {}) {
274
+ if (!binding && !artifactBinding) return [];
275
+ if (!binding) {
276
+ return [
277
+ issue(
278
+ "adopterDelivery.passport",
279
+ "artifact evidence cannot introduce delivery authority absent from the passport",
280
+ ),
281
+ ];
282
+ }
283
+ const issues = validateAdopterDeliveryPassportBinding(binding, {
284
+ expectedProjectRepository,
285
+ });
286
+ if (!artifactBinding || !sameJson(artifactBinding, binding)) {
287
+ issues.push(
288
+ issue(
289
+ "adopterDelivery.artifactEvidence",
290
+ "passport and artifact evidence must bind the same exact delivery result",
291
+ ),
292
+ );
293
+ }
294
+ return issues;
295
+ }
@@ -0,0 +1,158 @@
1
+ import vectorSuite from "./adopter-delivery-vectors.json" with { type: "json" };
2
+
3
+ import { adopterDeliveryGateDigest } from "./adopter-delivery-gate.js";
4
+
5
+ export const ADOPTER_DELIVERY_VECTOR_SUITE_CONTRACT =
6
+ "kungfu-buildchain-adopter-delivery-vectors/v1";
7
+
8
+ const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
9
+ const REQUIRED_CASES = new Map([
10
+ ["golden-two-driver-offline-replay", "golden"],
11
+ ["negative-driver-mismatch", "negative"],
12
+ ["negative-category-conflict", "negative"],
13
+ ["negative-evidence-substitution", "negative"],
14
+ ["negative-stale-package-cut", "negative"],
15
+ ["fault-driver-throw", "fault"],
16
+ ]);
17
+
18
+ function freezeJson(value) {
19
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
20
+ Object.freeze(value);
21
+ for (const child of Object.values(value)) freezeJson(child);
22
+ }
23
+ return value;
24
+ }
25
+
26
+ function issue(code, path, message) {
27
+ return { code, path, message };
28
+ }
29
+
30
+ function suiteDigest(suite) {
31
+ const preimage = structuredClone(suite);
32
+ delete preimage.suiteRoot;
33
+ return adopterDeliveryGateDigest(preimage);
34
+ }
35
+
36
+ export function validateAdopterDeliveryVectorSuite(suite) {
37
+ const issues = [];
38
+ if (!suite || typeof suite !== "object" || Array.isArray(suite)) {
39
+ return {
40
+ valid: false,
41
+ issues: [
42
+ issue(
43
+ "adopter-delivery-vectors.invalid",
44
+ "/",
45
+ "Vector suite must be a JSON object.",
46
+ ),
47
+ ],
48
+ };
49
+ }
50
+ if (
51
+ suite.schemaVersion !== 1 ||
52
+ suite.contract !== ADOPTER_DELIVERY_VECTOR_SUITE_CONTRACT
53
+ ) {
54
+ issues.push(
55
+ issue(
56
+ "adopter-delivery-vectors.contract",
57
+ "/contract",
58
+ "Vector suite contract is unsupported.",
59
+ ),
60
+ );
61
+ }
62
+ if (!Array.isArray(suite.cases)) {
63
+ issues.push(
64
+ issue(
65
+ "adopter-delivery-vectors.cases",
66
+ "/cases",
67
+ "Vector suite cases must be an array.",
68
+ ),
69
+ );
70
+ } else {
71
+ const seen = new Set();
72
+ for (const [index, entry] of suite.cases.entries()) {
73
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
74
+ issues.push(
75
+ issue(
76
+ "adopter-delivery-vectors.case",
77
+ `/cases/${index}`,
78
+ "Vector case must be an object.",
79
+ ),
80
+ );
81
+ continue;
82
+ }
83
+ if (typeof entry.id !== "string" || seen.has(entry.id)) {
84
+ issues.push(
85
+ issue(
86
+ "adopter-delivery-vectors.case-id",
87
+ `/cases/${index}/id`,
88
+ "Vector case id must be non-empty and unique.",
89
+ ),
90
+ );
91
+ }
92
+ seen.add(entry.id);
93
+ if (!entry.expected || typeof entry.expected !== "object") {
94
+ issues.push(
95
+ issue(
96
+ "adopter-delivery-vectors.expected",
97
+ `/cases/${index}/expected`,
98
+ "Vector case must declare its expected result.",
99
+ ),
100
+ );
101
+ }
102
+ }
103
+ for (const [id, className] of REQUIRED_CASES) {
104
+ const entry = suite.cases.find((candidate) => candidate?.id === id);
105
+ if (!entry || entry.class !== className) {
106
+ issues.push(
107
+ issue(
108
+ "adopter-delivery-vectors.required-case",
109
+ "/cases",
110
+ `Required ${className} vector ${id} is missing.`,
111
+ ),
112
+ );
113
+ }
114
+ }
115
+ }
116
+ if (!ROOT_PATTERN.test(suite.suiteRoot ?? "")) {
117
+ issues.push(
118
+ issue(
119
+ "adopter-delivery-vectors.root",
120
+ "/suiteRoot",
121
+ "Vector suite root must be a canonical sha256 root.",
122
+ ),
123
+ );
124
+ } else {
125
+ try {
126
+ if (suiteDigest(suite) !== suite.suiteRoot) {
127
+ issues.push(
128
+ issue(
129
+ "adopter-delivery-vectors.root",
130
+ "/suiteRoot",
131
+ "Vector suite root does not match the exact JSON closure.",
132
+ ),
133
+ );
134
+ }
135
+ } catch {
136
+ issues.push(
137
+ issue(
138
+ "adopter-delivery-vectors.root",
139
+ "/suiteRoot",
140
+ "Vector suite must contain finite acyclic JSON.",
141
+ ),
142
+ );
143
+ }
144
+ }
145
+ return { valid: issues.length === 0, issues };
146
+ }
147
+
148
+ export const ADOPTER_DELIVERY_VECTOR_SUITE = freezeJson(
149
+ structuredClone(vectorSuite),
150
+ );
151
+
152
+ export function getAdopterDeliveryVector(id) {
153
+ const entry = ADOPTER_DELIVERY_VECTOR_SUITE.cases.find(
154
+ (candidate) => candidate.id === id,
155
+ );
156
+ if (!entry) throw new RangeError(`Unknown adopter delivery vector: ${id}`);
157
+ return structuredClone(entry);
158
+ }
@@ -0,0 +1,77 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "contract": "kungfu-buildchain-adopter-delivery-vectors/v1",
4
+ "rootAlgorithm": "sha256-buildchain-canonical-json-v1",
5
+ "authorities": {
6
+ "kfdPackage": "@kungfu-tech/kfd@1.0.0-alpha.65",
7
+ "categoryProfileVectors": "@kungfu-tech/kfd/adopter-conformance/category-profile-vectors.json",
8
+ "categoryInstanceVectors": "@kungfu-tech/kfd/adopter-conformance/category-instance-vectors.json"
9
+ },
10
+ "cases": [
11
+ {
12
+ "id": "golden-two-driver-offline-replay",
13
+ "class": "golden",
14
+ "surface": "protocol-neutral-gate",
15
+ "operation": "evaluate identical exact requests with two isolated protocol drivers and replay each request offline",
16
+ "expected": {
17
+ "statuses": ["passed", "passed"],
18
+ "issueCodes": [],
19
+ "deterministicRoots": true,
20
+ "qualifying": false,
21
+ "selfCertified": false
22
+ }
23
+ },
24
+ {
25
+ "id": "negative-driver-mismatch",
26
+ "class": "negative",
27
+ "surface": "protocol-neutral-gate",
28
+ "operation": "select an exact protocol identity and version that are not registered",
29
+ "expected": {
30
+ "status": "failed",
31
+ "issueCodes": ["delivery-driver-unknown"]
32
+ }
33
+ },
34
+ {
35
+ "id": "negative-category-conflict",
36
+ "class": "negative",
37
+ "surface": "published-kfd-category-driver",
38
+ "operation": "append a duplicate published category profile to the exact KFD instance selection",
39
+ "expected": {
40
+ "status": "failed",
41
+ "issueCodes": ["acp-composition-invalid"]
42
+ }
43
+ },
44
+ {
45
+ "id": "negative-evidence-substitution",
46
+ "class": "negative",
47
+ "surface": "release-passport-binding",
48
+ "operation": "substitute the artifact root in artifact evidence after binding the passport",
49
+ "expected": {
50
+ "status": "failed",
51
+ "issueCodes": ["adopterDelivery.artifactEvidence"]
52
+ }
53
+ },
54
+ {
55
+ "id": "negative-stale-package-cut",
56
+ "class": "negative",
57
+ "surface": "published-kfd-category-driver",
58
+ "operation": "replace the KFD instance package version with an uninstalled stale cut",
59
+ "expected": {
60
+ "status": "failed",
61
+ "issueCodes": ["delivery-kfd-package-cut-mismatch"]
62
+ }
63
+ },
64
+ {
65
+ "id": "fault-driver-throw",
66
+ "class": "fault",
67
+ "surface": "protocol-neutral-gate",
68
+ "operation": "throw a private exception from the selected protocol driver",
69
+ "expected": {
70
+ "status": "failed",
71
+ "issueCodes": ["delivery-driver-error"],
72
+ "privateDetailRedacted": true
73
+ }
74
+ }
75
+ ],
76
+ "suiteRoot": "sha256:c978707556406ffda4ef4192032332c01c2da1cebb6ad8c4f0edfc65d5cef0c7"
77
+ }
@@ -30,6 +30,7 @@ export const BUILDCHAIN_AGENT_MANUALS = Object.freeze([
30
30
  { id: "kfd-agent-hub", title: "KFD Agent Hub Builder flow", path: "docs/kfd-agent-hub.md", plane: "use" },
31
31
  { id: "lifecycle-protocol", title: "Lifecycle protocol", path: "docs/lifecycle-protocol.md", plane: "use" },
32
32
  { id: "reusable-build-surface", title: "Reusable build surface", path: "docs/reusable-build-surface.md", plane: "use" },
33
+ { id: "v4-adopter-delivery", title: "V4 public adopter delivery", path: "docs/v4-adopter-delivery.md", plane: "verify" },
33
34
  { id: "publish-transaction", title: "Publish transaction", path: "docs/publish-transaction.md", plane: "verify" },
34
35
  { id: "release-tail-contract", title: "Declarative release-tail contract", path: "docs/release-tail-contract.md", plane: "verify" },
35
36
  { id: "release-tail-provider-plane", title: "Declarative release-tail provider plane", path: "docs/release-tail-provider-plane.md", plane: "use" },
@@ -236,6 +236,16 @@ export function normalizeBuildchainConfig(config) {
236
236
  if (normalized.publication !== undefined) {
237
237
  normalized.publication = normalizePublicationSection(normalized.publication);
238
238
  }
239
+ if (normalized.publication_rehearsal !== undefined) {
240
+ normalized.publication_rehearsal = normalizePublicationRehearsalSection(
241
+ normalized.publication_rehearsal,
242
+ );
243
+ }
244
+ if (normalized.adopter_delivery !== undefined) {
245
+ normalized.adopter_delivery = normalizeAdopterDeliverySection(
246
+ normalized.adopter_delivery,
247
+ );
248
+ }
239
249
  if (normalized.consumers !== undefined) {
240
250
  normalized.consumers = normalizeConsumersSection(normalized.consumers);
241
251
  }
@@ -700,6 +710,80 @@ function normalizePublicationSection(publication) {
700
710
  };
701
711
  }
702
712
 
713
+ function normalizePublicationRehearsalSection(rehearsal) {
714
+ assertPlainObject(rehearsal, "publication_rehearsal");
715
+ const allowed = ["contract", "capsule_path", "candidate_root", "state_path", "evidence_path", "effect_default"];
716
+ const unknown = Object.keys(rehearsal).filter((key) => !allowed.includes(key));
717
+ if (unknown.length > 0) {
718
+ throw new Error(`publication_rehearsal contains unsupported fields: ${unknown.join(", ")}`);
719
+ }
720
+ const normalized = {
721
+ contract: assertString(rehearsal.contract, "publication_rehearsal.contract"),
722
+ capsulePath: posixPath(assertString(rehearsal.capsule_path, "publication_rehearsal.capsule_path")),
723
+ candidateRoot: posixPath(assertString(rehearsal.candidate_root, "publication_rehearsal.candidate_root")),
724
+ statePath: posixPath(assertString(rehearsal.state_path, "publication_rehearsal.state_path")),
725
+ evidencePath: posixPath(assertString(rehearsal.evidence_path, "publication_rehearsal.evidence_path")),
726
+ effectDefault: assertString(rehearsal.effect_default, "publication_rehearsal.effect_default"),
727
+ };
728
+ if (
729
+ normalized.contract !== "buildchain-v4-publication-rehearsal-capsule/v1" ||
730
+ normalized.effectDefault !== "disabled"
731
+ ) {
732
+ throw new Error("publication_rehearsal must use the v4 capsule contract with effect_default = disabled");
733
+ }
734
+ for (const [key, value] of Object.entries({
735
+ capsule_path: normalized.capsulePath,
736
+ candidate_root: normalized.candidateRoot,
737
+ state_path: normalized.statePath,
738
+ evidence_path: normalized.evidencePath,
739
+ })) {
740
+ if (value.startsWith("/") || value.includes("\\") || value.split("/").includes("..")) {
741
+ throw new Error(`publication_rehearsal.${key} must be repository-relative`);
742
+ }
743
+ }
744
+ return normalized;
745
+ }
746
+
747
+ function normalizeAdopterDeliverySection(delivery) {
748
+ assertPlainObject(delivery, "adopter_delivery");
749
+ const allowed = ["contract", "input_path", "readback_path", "bootstrap_path", "archive_path", "result_path", "driver_selector", "artifact_profile_selector"];
750
+ const unknown = Object.keys(delivery).filter((key) => !allowed.includes(key));
751
+ if (unknown.length > 0) {
752
+ throw new Error(`adopter_delivery contains unsupported fields: ${unknown.join(", ")}`);
753
+ }
754
+ const normalized = {
755
+ contract: assertString(delivery.contract, "adopter_delivery.contract"),
756
+ inputPath: posixPath(assertString(delivery.input_path, "adopter_delivery.input_path")),
757
+ readbackPath: posixPath(assertString(delivery.readback_path, "adopter_delivery.readback_path")),
758
+ bootstrapPath: posixPath(assertString(delivery.bootstrap_path, "adopter_delivery.bootstrap_path")),
759
+ archivePath: posixPath(assertString(delivery.archive_path, "adopter_delivery.archive_path")),
760
+ resultPath: posixPath(assertString(delivery.result_path, "adopter_delivery.result_path")),
761
+ driverSelector: assertString(delivery.driver_selector, "adopter_delivery.driver_selector"),
762
+ artifactProfileSelector: assertString(delivery.artifact_profile_selector, "adopter_delivery.artifact_profile_selector"),
763
+ };
764
+ if (normalized.contract !== "kungfu-buildchain-v4-adopter-delivery/v1") {
765
+ throw new Error("adopter_delivery contract is unsupported");
766
+ }
767
+ if (
768
+ !["json-assertion", "kfd-category", "legacy-kfd"].includes(normalized.driverSelector) ||
769
+ !["git-commit", "package"].includes(normalized.artifactProfileSelector)
770
+ ) {
771
+ throw new Error("adopter_delivery selector is unsupported");
772
+ }
773
+ for (const [key, value] of Object.entries({
774
+ input_path: normalized.inputPath,
775
+ readback_path: normalized.readbackPath,
776
+ bootstrap_path: normalized.bootstrapPath,
777
+ archive_path: normalized.archivePath,
778
+ result_path: normalized.resultPath,
779
+ })) {
780
+ if (value.startsWith("/") || value.includes("\\") || value.split("/").includes("..")) {
781
+ throw new Error(`adopter_delivery.${key} must be repository-relative`);
782
+ }
783
+ }
784
+ return normalized;
785
+ }
786
+
703
787
  function normalizePublicationArchive(archive = undefined) {
704
788
  if (archive === undefined) {
705
789
  return undefined;
@@ -166,6 +166,36 @@ function declarativeAuditableDemoSurface(root, pkg, majorLine) {
166
166
  });
167
167
  }
168
168
 
169
+ function v4ReleaseCandidatePromoteSurface(root, majorLine) {
170
+ return surface(root, {
171
+ id: "v4-release-candidate-promote-action",
172
+ kind: "action",
173
+ path: "actions/v4-release-candidate-promote/action.yml",
174
+ publicRef: `kungfu-systems/buildchain/actions/v4-release-candidate-promote@${majorLine}`,
175
+ requiredInputs: [
176
+ "token", "repository", "source-sha", "version", "tag", "channel",
177
+ "candidate-passport-path", "stage-capsules-path",
178
+ "publication-qualification-path",
179
+ ],
180
+ requiredOutputs: [
181
+ "release-passport-path", "release-passport-root", "transaction-state",
182
+ "declaration-root", "transaction-root", "state-root",
183
+ "receipt-roots-json",
184
+ ],
185
+ breakingDefaults: {
186
+ statePath: ".buildchain/release-tail/state.json",
187
+ executionBoundary: "built-in-provider-plane-only",
188
+ },
189
+ optionalInputs: ["artifact-paths", "state-path", "failure-after-capability"],
190
+ guarantees: [
191
+ "v4 promotion consumes only sealed candidate, Stage Capsule, qualification, and artifact evidence",
192
+ "GitHub Release, signed-channel commit, activation, readback, and released-evidence synthesis use built-in provider adapters",
193
+ "durable provider checkpoints resume completed capabilities without replay",
194
+ "no consumer command, script, shell, checkout, or executable tail hook is accepted",
195
+ ],
196
+ });
197
+ }
198
+
169
199
  export function createBuildchainContractWorld({
170
200
  root = process.cwd(),
171
201
  packageJson = undefined,
@@ -484,7 +514,7 @@ export function createBuildchainContractWorld({
484
514
  "Build Images owns encoding, Buildchain owns qualification and receipts, and site repositories own browser loading and accessibility behavior",
485
515
  "media qualification does not claim browser playback, responsive layout, reduced-motion behavior, accessibility, or production deployment",
486
516
  ],
487
- }), declarativeAuditableDemoSurface(root, pkg, majorLine),
517
+ }), declarativeAuditableDemoSurface(root, pkg, majorLine), v4ReleaseCandidatePromoteSurface(root, majorLine),
488
518
  surface(root, {
489
519
  id: "promote-buildchain-ref-action",
490
520
  kind: "action",