@kungfu-tech/buildchain 3.0.9-alpha.4 → 3.0.9-alpha.6

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 (42) hide show
  1. package/bin/buildchain.mjs +5 -1
  2. package/dist/site/buildchain-contract.json +9 -9
  3. package/dist/site/buildchain-site.json +56 -26
  4. package/dist/site/cli-registry.json +18 -2
  5. package/dist/site/kfd-claims.json +8 -5
  6. package/dist/site/kfd-upstream-aggregate.json +1 -1
  7. package/dist/site/manual-registry.json +6 -6
  8. package/dist/site/node-api-registry.json +290 -49
  9. package/dist/site/page-registry.json +46 -16
  10. package/dist/site/public-surface-audit.json +122 -6
  11. package/dist/site/publication-registry.json +4 -4
  12. package/dist/site/site-manifest.json +10 -10
  13. package/dist/site/workflow-registry.json +12 -6
  14. package/docs/cli-reference.md +79 -13
  15. package/docs/dev-delivery-warrant.md +53 -19
  16. package/docs/kfd-support.md +7 -2
  17. package/docs/node-api-reference.md +59 -51
  18. package/docs/release-governance.md +5 -4
  19. package/docs/release-passport.md +8 -2
  20. package/docs/versioning.md +1 -0
  21. package/package.json +3 -2
  22. package/packages/core/artifact-verification-envelope.js +5 -5
  23. package/packages/core/dev-delivery-native-proof.js +333 -0
  24. package/packages/core/dev-delivery-warrant-settlement.js +5 -1
  25. package/packages/core/dev-delivery-warrant.js +40 -6
  26. package/packages/core/kfd-adopter-manifest.js +46 -47
  27. package/packages/core/release-passport-contract.js +5 -5
  28. package/packages/core/release-passport.js +5 -4
  29. package/scripts/aws-macos-jit-controller-core.mjs +82 -2
  30. package/scripts/aws-macos-jit-controller.mjs +81 -1
  31. package/scripts/aws-macos-jit-source-rebind.mjs +147 -24
  32. package/scripts/buildchain-cli-help.mjs +6 -2
  33. package/scripts/check-internal-architecture.mjs +1 -0
  34. package/scripts/dev-delivery-native-run.mjs +187 -0
  35. package/scripts/dev-delivery-proof.mjs +54 -2
  36. package/scripts/dev-delivery-two-phase.mjs +598 -0
  37. package/scripts/dev-delivery-warrant.mjs +38 -3
  38. package/scripts/dev-pr-delivery-warrant.mjs +6 -1
  39. package/scripts/generate-channel-build-workflow.mjs +4 -0
  40. package/scripts/init-repo.mjs +16 -0
  41. package/scripts/release-candidate-tail-reseal.mjs +426 -0
  42. package/templates/native-dev-delivery.yml +82 -0
@@ -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
 
@@ -405,7 +405,7 @@ export function prepareReleasePassportKfdSections({ kfd1, kfd2Claims, kfd3, kfdA
405
405
  };
406
406
  }
407
407
 
408
- export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], comparisonMatrix, sourceSha = "", checkedAt } = {}) {
408
+ export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], comparisonMatrix, expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", sourceSha = "", checkedAt } = {}) {
409
409
  if (!manifest) {
410
410
  if (comparisonMatrix || gateResults.length > 0) {
411
411
  throw new Error("KFD support and product-gate inputs require --kfd-adopter-manifest-json; --kfd-support-matrix-json is comparison-only");
@@ -414,7 +414,7 @@ export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], c
414
414
  }
415
415
  const manifestGate = createKfdAdopterManifestGate({
416
416
  manifest, packageArtifactRoot: installedKfdPackageArtifactRoot(), gateResults,
417
- authorityPath: "kfd-adopter-manifest.json", expectedSourceSha: sourceSha, checkedAt,
417
+ authorityPath: "kfd-adopter-manifest.json", expectedAdopterId, expectedSourceRepository, expectedSourceSha: sourceSha, checkedAt,
418
418
  });
419
419
  const legacyProjection = createKfdLegacySupportMatrixProjection({ manifest, manifestGate });
420
420
  if (comparisonMatrix) {
@@ -425,18 +425,18 @@ export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], c
425
425
  }
426
426
  return {
427
427
  manifest, manifestGate, legacyProjection,
428
- binding: createKfdAdopterReleaseBinding({ manifest, manifestGate, legacyProjection, expectedSourceSha: sourceSha }),
428
+ binding: createKfdAdopterReleaseBinding({ manifest, manifestGate, legacyProjection, expectedAdopterId, expectedSourceRepository, expectedSourceSha: sourceSha }),
429
429
  };
430
430
  }
431
431
 
432
- export function validateKfdAdopterReleaseEvidence({ binding, artifactBinding, manifest, manifestGate, legacyProjection, passportLegacyProjection, expectedSourceSha = "" } = {}) {
432
+ export function validateKfdAdopterReleaseEvidence({ binding, artifactBinding, manifest, manifestGate, legacyProjection, passportLegacyProjection, expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", expectedSourceSha = "" } = {}) {
433
433
  if (!binding) {
434
434
  return passportLegacyProjection
435
435
  ? [{ code: "kfdSupport.authority", message: "legacy KFD support projection requires the standard adopter manifest binding" }]
436
436
  : [];
437
437
  }
438
438
  const issues = validateKfdAdopterReleaseBinding(binding, {
439
- manifest, manifestGate, legacyProjection, expectedSourceSha,
439
+ manifest, manifestGate, legacyProjection, expectedAdopterId, expectedSourceRepository, expectedSourceSha,
440
440
  }).issues.map((entry) => ({ code: `kfdAdopter.${entry.path || entry.code}`, message: entry.message, details: entry }));
441
441
  if (!artifactBinding || passportStableJson(artifactBinding) !== passportStableJson(binding)) {
442
442
  issues.push({ code: "kfdAdopter.artifactEvidence", message: "release passport and artifact evidence must bind the same exact KFD adopter closure" });
@@ -1465,7 +1465,7 @@ export function createReleasePassport({
1465
1465
  };
1466
1466
  }
1467
1467
 
1468
- function collectKfdAdopterReleaseInputs({ cwd, manifestJson, supportMatrixJson, productGateJsons, sourceSha, checkedAt }) {
1468
+ function collectKfdAdopterReleaseInputs({ cwd, manifestJson, supportMatrixJson, productGateJsons, repository, sourceSha, checkedAt }) {
1469
1469
  const manifestMeta = parseJsonInputWithMeta(manifestJson, undefined, { cwd, label: "kfdAdopterManifestJson" });
1470
1470
  const supportMatrixMeta = parseJsonInputWithMeta(supportMatrixJson, undefined, { cwd, label: "kfdSupportMatrixJson" });
1471
1471
  const productGateMetas = (productGateJsons || [])
@@ -1476,7 +1476,7 @@ function collectKfdAdopterReleaseInputs({ cwd, manifestJson, supportMatrixJson,
1476
1476
  manifest: manifestMeta.value,
1477
1477
  gateResults: productGateMetas.map((meta) => meta.value),
1478
1478
  comparisonMatrix: supportMatrixMeta.value,
1479
- sourceSha,
1479
+ expectedAdopterId: repository || "kungfu-systems/buildchain", expectedSourceRepository: repository, sourceSha,
1480
1480
  checkedAt,
1481
1481
  });
1482
1482
  }
@@ -1662,7 +1662,7 @@ export function collectGitHubReleasePassport({
1662
1662
  const productMechanism = defaultProductMechanism({ repository, productName });
1663
1663
  const kfdAdopterEvidence = collectKfdAdopterReleaseInputs({
1664
1664
  cwd, manifestJson: kfdAdopterManifestJson, supportMatrixJson: kfdSupportMatrixJson,
1665
- productGateJsons: kfdProductGateJsons, sourceSha, checkedAt: resolvedCheckedAt,
1665
+ productGateJsons: kfdProductGateJsons, repository, sourceSha, checkedAt: resolvedCheckedAt,
1666
1666
  });
1667
1667
  const {
1668
1668
  manifest: kfdAdopterManifest,
@@ -2101,7 +2101,8 @@ function validateReleaseEvidenceContracts({
2101
2101
  manifestGate: kfdAdopterManifestGate,
2102
2102
  legacyProjection: kfdSupportEvidence,
2103
2103
  passportLegacyProjection: passport?.kfdSupport,
2104
- expectedSourceSha: optionalString(passport?.release?.sourceSha),
2104
+ expectedAdopterId: optionalString(passport?.product?.repository) || "kungfu-systems/buildchain",
2105
+ expectedSourceRepository: optionalString(passport?.product?.repository), expectedSourceSha: optionalString(passport?.release?.sourceSha),
2105
2106
  })) {
2106
2107
  issues.push(issue("error", entry.code, entry.message, entry.details));
2107
2108
  }
@@ -88,6 +88,57 @@ function terminalFailureRunIds(value) {
88
88
  return [...normalized].sort((left, right) => Number(left) - Number(right));
89
89
  }
90
90
 
91
+ function preflightFailureRunIds(value) {
92
+ if (!Array.isArray(value) || value.length === 0) {
93
+ throw new Error("preflightFailureRunIds must be a non-empty array");
94
+ }
95
+ const normalized = value.map((entry) =>
96
+ exact(entry, /^[1-9]\d*$/, "preflightFailureRunId"),
97
+ );
98
+ if (new Set(normalized).size !== normalized.length) {
99
+ throw new Error("preflightFailureRunIds must be unique");
100
+ }
101
+ return [...normalized].sort((left, right) => Number(left) - Number(right));
102
+ }
103
+
104
+ function startupFailureRuns(value) {
105
+ if (value === undefined) return [];
106
+ if (!Array.isArray(value)) {
107
+ throw new Error("startupFailureRuns must be an array");
108
+ }
109
+ const normalized = value.map((entry) => ({
110
+ runId: exact(entry?.runId, /^[1-9]\d*$/, "startupFailureRunId"),
111
+ sourceSha: exactSha(entry?.sourceSha),
112
+ }));
113
+ if (
114
+ new Set(normalized.map(({ runId }) => runId)).size !== normalized.length
115
+ ) {
116
+ throw new Error("startupFailureRuns must use unique run ids");
117
+ }
118
+ return [...normalized].sort(
119
+ (left, right) => Number(left.runId) - Number(right.runId),
120
+ );
121
+ }
122
+
123
+ function historicalTerminalFailureRuns(value) {
124
+ if (value === undefined) return [];
125
+ if (!Array.isArray(value)) {
126
+ throw new Error("historicalTerminalFailureRuns must be an array");
127
+ }
128
+ const normalized = value.map((entry) => ({
129
+ runId: exact(entry?.runId, /^[1-9]\d*$/, "historicalTerminalFailureRunId"),
130
+ sourceSha: exactSha(entry?.sourceSha),
131
+ }));
132
+ if (
133
+ new Set(normalized.map(({ runId }) => runId)).size !== normalized.length
134
+ ) {
135
+ throw new Error("historicalTerminalFailureRuns must use unique run ids");
136
+ }
137
+ return [...normalized].sort(
138
+ (left, right) => Number(left.runId) - Number(right.runId),
139
+ );
140
+ }
141
+
91
142
  function commonAws(values) {
92
143
  const region = exact(
93
144
  values.region || MACOS_EC2_JIT.region,
@@ -314,13 +365,35 @@ export function createMacosJitSourceRebindPlan(values = {}) {
314
365
  throw new Error(`workflowId must be ${MACOS_EC2_JIT.workflowId}`);
315
366
  }
316
367
  const priorRunPolicy = values.priorRunPolicy || "unused";
317
- if (!["unused", "terminal-failure"].includes(priorRunPolicy)) {
318
- throw new Error("priorRunPolicy must be unused or terminal-failure");
368
+ if (
369
+ !["unused", "terminal-failure", "preflight-failure"].includes(
370
+ priorRunPolicy,
371
+ )
372
+ ) {
373
+ throw new Error(
374
+ "priorRunPolicy must be unused, terminal-failure, or preflight-failure",
375
+ );
319
376
  }
320
377
  const terminalRunIds =
321
378
  priorRunPolicy === "terminal-failure"
322
379
  ? terminalFailureRunIds(values.terminalFailureRunIds)
323
380
  : [];
381
+ const preflightRunIds =
382
+ priorRunPolicy === "preflight-failure"
383
+ ? preflightFailureRunIds(values.preflightFailureRunIds)
384
+ : [];
385
+ const startupRuns = startupFailureRuns(values.startupFailureRuns);
386
+ const historicalTerminalRuns = historicalTerminalFailureRuns(
387
+ values.historicalTerminalFailureRuns,
388
+ );
389
+ if (priorRunPolicy === "unused" && startupRuns.length !== 0) {
390
+ throw new Error("startupFailureRuns require a failed priorRunPolicy");
391
+ }
392
+ if (priorRunPolicy === "unused" && historicalTerminalRuns.length !== 0) {
393
+ throw new Error(
394
+ "historicalTerminalFailureRuns require a failed priorRunPolicy",
395
+ );
396
+ }
324
397
  const plan = {
325
398
  schemaVersion: 1,
326
399
  contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
@@ -337,6 +410,9 @@ export function createMacosJitSourceRebindPlan(values = {}) {
337
410
  requiredState: "disabled_manually",
338
411
  priorRunPolicy,
339
412
  terminalFailureRunIds: terminalRunIds,
413
+ preflightFailureRunIds: preflightRunIds,
414
+ startupFailureRuns: startupRuns,
415
+ historicalTerminalFailureRuns: historicalTerminalRuns,
340
416
  },
341
417
  aws: {
342
418
  ...aws,
@@ -353,6 +429,10 @@ export function createMacosJitSourceRebindPlan(values = {}) {
353
429
  zeroPriorJobsRequired: priorRunPolicy === "unused",
354
430
  zeroPriorArtifactsRequired: priorRunPolicy === "unused",
355
431
  terminalFailureEvidencePreserved: priorRunPolicy === "terminal-failure",
432
+ preflightFailureEvidencePreserved: priorRunPolicy === "preflight-failure",
433
+ startupFailureEvidencePreserved: startupRuns.length !== 0,
434
+ historicalTerminalFailureEvidencePreserved:
435
+ historicalTerminalRuns.length !== 0,
356
436
  zeroJitResidueRequired: true,
357
437
  zeroEvidenceRequired: true,
358
438
  exactCampaignResourcesRequired: true,
@@ -542,6 +542,66 @@ function confirm(plan) {
542
542
  "--confirm-terminal-failure-run-ids-json must equal the exact failed run inventory",
543
543
  );
544
544
  }
545
+ const confirmedStartupFailures = flag("confirm-startup-failure-runs-json")
546
+ ? jsonArrayArg("confirm-startup-failure-runs-json")
547
+ : [];
548
+ if (
549
+ JSON.stringify(confirmedStartupFailures) !==
550
+ JSON.stringify(plan.github.startupFailureRuns)
551
+ ) {
552
+ throw new Error(
553
+ "--confirm-startup-failure-runs-json must equal the exact startup failure inventory",
554
+ );
555
+ }
556
+ const confirmedHistoricalTerminalFailures = flag(
557
+ "confirm-historical-terminal-failure-runs-json",
558
+ )
559
+ ? jsonArrayArg("confirm-historical-terminal-failure-runs-json")
560
+ : [];
561
+ if (
562
+ JSON.stringify(confirmedHistoricalTerminalFailures) !==
563
+ JSON.stringify(plan.github.historicalTerminalFailureRuns)
564
+ ) {
565
+ throw new Error(
566
+ "--confirm-historical-terminal-failure-runs-json must equal the exact historical terminal failure inventory",
567
+ );
568
+ }
569
+ } else if (plan.github.priorRunPolicy === "preflight-failure") {
570
+ const confirmed = jsonArrayArg(
571
+ "confirm-preflight-failure-run-ids-json",
572
+ ).map(String);
573
+ if (
574
+ JSON.stringify(confirmed) !==
575
+ JSON.stringify(plan.github.preflightFailureRunIds)
576
+ ) {
577
+ throw new Error(
578
+ "--confirm-preflight-failure-run-ids-json must equal the exact preflight failure inventory",
579
+ );
580
+ }
581
+ const confirmedStartupFailures = flag("confirm-startup-failure-runs-json")
582
+ ? jsonArrayArg("confirm-startup-failure-runs-json")
583
+ : [];
584
+ if (
585
+ JSON.stringify(confirmedStartupFailures) !==
586
+ JSON.stringify(plan.github.startupFailureRuns)
587
+ ) {
588
+ throw new Error(
589
+ "--confirm-startup-failure-runs-json must equal the exact startup failure inventory",
590
+ );
591
+ }
592
+ const confirmedHistoricalTerminalFailures = flag(
593
+ "confirm-historical-terminal-failure-runs-json",
594
+ )
595
+ ? jsonArrayArg("confirm-historical-terminal-failure-runs-json")
596
+ : [];
597
+ if (
598
+ JSON.stringify(confirmedHistoricalTerminalFailures) !==
599
+ JSON.stringify(plan.github.historicalTerminalFailureRuns)
600
+ ) {
601
+ throw new Error(
602
+ "--confirm-historical-terminal-failure-runs-json must equal the exact historical terminal failure inventory",
603
+ );
604
+ }
545
605
  }
546
606
  }
547
607
  if (plan.kind === "instance-rehydrate-plan") {
@@ -602,9 +662,12 @@ export function main() {
602
662
  "rebind-campaign",
603
663
  "plan-rebind-after-failure",
604
664
  "rebind-campaign-after-failure",
665
+ "plan-rebind-after-preflight-failure",
666
+ "rebind-campaign-after-preflight-failure",
605
667
  ].includes(mode)
606
668
  ) {
607
669
  const afterFailure = mode.endsWith("after-failure");
670
+ const afterPreflightFailure = mode.endsWith("after-preflight-failure");
608
671
  const plan = createMacosJitSourceRebindPlan({
609
672
  ...commonValues(execute),
610
673
  previousSourceSha: arg("previous-source-sha"),
@@ -612,10 +675,27 @@ export function main() {
612
675
  workflowId: arg("workflow-id"),
613
676
  hostId: arg("host-id"),
614
677
  instanceId: arg("instance-id"),
615
- priorRunPolicy: afterFailure ? "terminal-failure" : "unused",
678
+ priorRunPolicy: afterPreflightFailure
679
+ ? "preflight-failure"
680
+ : afterFailure
681
+ ? "terminal-failure"
682
+ : "unused",
616
683
  terminalFailureRunIds: afterFailure
617
684
  ? jsonArrayArg("terminal-failure-run-ids-json")
618
685
  : [],
686
+ preflightFailureRunIds: afterPreflightFailure
687
+ ? jsonArrayArg("preflight-failure-run-ids-json")
688
+ : [],
689
+ startupFailureRuns:
690
+ (afterFailure || afterPreflightFailure) &&
691
+ flag("startup-failure-runs-json")
692
+ ? jsonArrayArg("startup-failure-runs-json")
693
+ : [],
694
+ historicalTerminalFailureRuns:
695
+ (afterFailure || afterPreflightFailure) &&
696
+ flag("historical-terminal-failure-runs-json")
697
+ ? jsonArrayArg("historical-terminal-failure-runs-json")
698
+ : [],
619
699
  });
620
700
  return emit(plan, mode, execute, () =>
621
701
  executeMacosJitSourceRebind(plan, { profile: arg("aws-profile") }),