@kungfu-tech/buildchain 4.0.2-alpha.37 → 4.0.2-alpha.39

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 (48) hide show
  1. package/AGENTS.md +1 -1
  2. package/architecture/agent-change-map.md +2 -2
  3. package/architecture/internal-capabilities.json +2 -2
  4. package/architecture/maintainability-debt.json +8 -8
  5. package/architecture/maintainability-policy.json +3 -3
  6. package/architecture/v4-cross-platform-adopter-qualification.json +1 -1
  7. package/architecture/v4-floating-consumer-policy.json +1 -1
  8. package/architecture/v4-next-development-parity.json +2 -2
  9. package/architecture/v4-release-topology.json +0 -1
  10. package/architecture/v4-stage-capsule-qualification.json +1 -1
  11. package/architecture/v4-tail-reseal-parity.json +8 -6
  12. package/architecture/v4-universal-workflow-train-admission.json +1 -1
  13. package/architecture/workflow-taxonomy.json +18 -15
  14. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  15. package/dist/site/buildchain-contract.json +6 -6
  16. package/dist/site/buildchain-site.json +67 -26
  17. package/dist/site/capability-registry.json +2 -2
  18. package/dist/site/kfd-claims.json +19 -73
  19. package/dist/site/kfd-upstream-aggregate.json +1 -1
  20. package/dist/site/manual-registry.json +5 -5
  21. package/dist/site/node-api-registry.json +30 -30
  22. package/dist/site/page-registry.json +58 -17
  23. package/dist/site/public-surface-audit.json +8 -67
  24. package/dist/site/publication-authority-registry.json +1 -16
  25. package/dist/site/publication-registry.json +4 -4
  26. package/dist/site/site-manifest.json +9 -9
  27. package/dist/site/workflow-registry.json +5 -125
  28. package/docs/kfd-support.md +26 -2
  29. package/docs/next-development-transition.md +1 -1
  30. package/docs/node-api-reference.md +32 -32
  31. package/docs/release-passport.md +27 -2
  32. package/docs/v4-adopter-delivery.md +1 -1
  33. package/docs/v4-stage-capsule.md +1 -1
  34. package/docs/v4-tail-reseal.md +1 -1
  35. package/docs/workflow-catalog.md +5 -3
  36. package/docs/workflow-path-migration.md +109 -0
  37. package/package.json +1 -1
  38. package/packages/core/artifact-verification-envelope.js +5 -5
  39. package/packages/core/kfd-adopter-manifest.js +46 -47
  40. package/packages/core/next-development-projection.js +1 -1
  41. package/packages/core/release-passport-contract.js +5 -5
  42. package/packages/core/release-passport.js +5 -4
  43. package/scripts/check-v4-floating-consumer-policy-contract.mjs +1 -1
  44. package/scripts/check-v4-public-dogfood-contract.mjs +4 -4
  45. package/scripts/check-v4-universal-workflow-bootstrap.mjs +3 -3
  46. package/scripts/generate-v4-universal-workflow-facades.mjs +4 -3
  47. package/scripts/v4-universal-workflow-engine.mjs +2 -1
  48. package/scripts/workflow-taxonomy.mjs +50 -11
@@ -12,6 +12,7 @@ const ROLES = ["public", "component", "self"];
12
12
  const CATEGORIES = ["build", "release", "ops"];
13
13
  const SLUG = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
14
14
  const WORKFLOW = /^\.github\/workflows\/[.a-z0-9-]+\.ya?ml$/u;
15
+ const VERSION_TOKEN = /(?:^|[.-])v\d+(?:[.-]|$)/iu;
15
16
  const PROTECTED = [
16
17
  TAXONOMY_PATH,
17
18
  "scripts/workflow-taxonomy.mjs",
@@ -40,6 +41,10 @@ function validateEntryRole(entry, errors) {
40
41
  errors.push(`${entry.id}: invalid category`);
41
42
  if (!SLUG.test(entry.purpose || ""))
42
43
  errors.push(`${entry.id}: invalid purpose slug`);
44
+ if (VERSION_TOKEN.test(path.posix.basename(workflowPath(entry))))
45
+ errors.push(
46
+ `${entry.id}: workflow filenames cannot contain version tokens`,
47
+ );
43
48
  for (const field of ["summary", "owner", "rationale"]) {
44
49
  if (typeof entry[field] !== "string" || !entry[field].trim())
45
50
  errors.push(`${entry.id}: missing ${field}`);
@@ -57,6 +62,15 @@ function validateEntryRole(entry, errors) {
57
62
  }
58
63
 
59
64
  function validateMigration(policy, entry, declared, errors) {
65
+ if (entry.retiredAlias) {
66
+ if (
67
+ entry.compatibility ||
68
+ entry.retiredAlias.path !== entry.migration?.previousPath ||
69
+ !entry.retiredAlias.reason?.trim() ||
70
+ entry.retiredAlias.migrationGuide !== "docs/workflow-path-migration.md"
71
+ )
72
+ errors.push(`${entry.id}: invalid alias retirement record`);
73
+ }
60
74
  if (entry.migration) {
61
75
  if (
62
76
  !WORKFLOW.test(entry.migration.previousPath || "") ||
@@ -73,6 +87,10 @@ function validateMigration(policy, entry, declared, errors) {
73
87
  }
74
88
  if (entry.compatibility) {
75
89
  const alias = entry.compatibility;
90
+ if (VERSION_TOKEN.test(path.posix.basename(alias.path || "")))
91
+ errors.push(
92
+ `${entry.id}: version-prefixed compatibility aliases are retired`,
93
+ );
76
94
  if (entry.role === "self")
77
95
  errors.push(
78
96
  `${entry.id}: repository event wrappers cannot have duplicate aliases`,
@@ -167,6 +185,14 @@ function validateWorkflow(root, entry, declared, errors) {
167
185
  const absolute = path.join(root, file);
168
186
  if (!fs.existsSync(absolute) || !fs.lstatSync(absolute).isFile()) return;
169
187
  const text = fs.readFileSync(absolute, "utf8");
188
+ if (
189
+ entry.retiredAlias &&
190
+ text.includes("BUILDCHAIN_INVOKED_WORKFLOW:") &&
191
+ !text.includes(`BUILDCHAIN_INVOKED_WORKFLOW: ${file}\n`)
192
+ )
193
+ errors.push(
194
+ `${file}: consumer admission must bind the canonical invoked workflow`,
195
+ );
170
196
  const document = parseWorkflowDocument(text);
171
197
  if (entry.role === "self")
172
198
  validateRepositoryReferences(file, document, text, declared, errors);
@@ -195,6 +221,13 @@ function validateWorkflow(root, entry, declared, errors) {
195
221
  );
196
222
  }
197
223
  for (const call of parseYamlUses(text)) {
224
+ const remote = call.value.match(
225
+ /^kungfu-systems\/buildchain\/(\.github\/workflows\/[^@]+)@/u,
226
+ );
227
+ if (remote && VERSION_TOKEN.test(path.posix.basename(remote[1])))
228
+ errors.push(
229
+ `${file}:${call.line}: retired version-prefixed workflow call ${remote[1]}`,
230
+ );
198
231
  const local = call.value.match(/^\.\/(\.github\/workflows\/[^@]+)$/u);
199
232
  if (local && !declared.has(local[1]))
200
233
  errors.push(
@@ -276,6 +309,8 @@ export function checkWorkflowTaxonomy(
276
309
  );
277
310
  const observed = new Set(discoverWorkflowFiles(root));
278
311
  for (const file of observed) {
312
+ if (VERSION_TOKEN.test(path.posix.basename(file)))
313
+ errors.push(`workflow filenames cannot contain version tokens: ${file}`);
279
314
  if (!declared.has(file)) errors.push(`unregistered workflow: ${file}`);
280
315
  if (!fs.lstatSync(path.join(root, file)).isFile())
281
316
  errors.push(`workflow must be a regular file: ${file}`);
@@ -334,6 +369,8 @@ export function writeWorkflowSource(root, relative, text) {
334
369
  (item) =>
335
370
  workflowPath(item) === relative || item.compatibility?.path === relative,
336
371
  );
372
+ if (policy?.entries.some((item) => item.retiredAlias?.path === relative))
373
+ throw new Error(`${relative}: cannot regenerate a retired workflow alias`);
337
374
  const outputs = entry
338
375
  ? [
339
376
  workflowPath(entry),
@@ -345,19 +382,10 @@ export function writeWorkflowSource(root, relative, text) {
345
382
 
346
383
  export function rewriteRepositoryWorkflowPaths(root, text) {
347
384
  for (const entry of readWorkflowTaxonomy(root)?.entries || []) {
348
- if (
349
- ["v4-adopter-delivery", "v4-stage-capsule-canary"].includes(entry.id) &&
350
- entry.compatibility
351
- ) {
352
- const canonical = workflowPath(entry);
353
- const legacy = entry.compatibility.path;
354
- text = text.replaceAll(
355
- `BUILDCHAIN_INVOKED_WORKFLOW: ${legacy}\n`,
356
- `BUILDCHAIN_INVOKED_WORKFLOW: \${{ startsWith(job.workflow_ref, 'kungfu-systems/buildchain/${canonical}@') && '${canonical}' || '${legacy}' }}\n`,
357
- );
358
- }
359
385
  if (entry.role === "self" && entry.migration)
360
386
  text = text.replaceAll(entry.migration.previousPath, workflowPath(entry));
387
+ if (entry.retiredAlias)
388
+ text = text.replaceAll(entry.retiredAlias.path, workflowPath(entry));
361
389
  if (entry.id === "build-surface-fixture")
362
390
  text = text.replaceAll(
363
391
  "build-surface-fixture.yml",
@@ -367,6 +395,15 @@ export function rewriteRepositoryWorkflowPaths(root, text) {
367
395
  return text;
368
396
  }
369
397
 
398
+ // Historical inventories and debt ledgers retain their original identity. File
399
+ // access resolves only explicitly retired aliases to the registered implementation.
400
+ export function currentWorkflowPath(root, relative) {
401
+ const entry = readWorkflowTaxonomy(root)?.entries.find(
402
+ (item) => item.retiredAlias?.path === relative,
403
+ );
404
+ return entry ? workflowPath(entry) : relative;
405
+ }
406
+
370
407
  export function workflowCompatibilityIdentity(root, relative, text) {
371
408
  const entry = readWorkflowTaxonomy(root)?.entries.find(
372
409
  (item) => workflowPath(item) === relative && item.compatibility,
@@ -441,6 +478,8 @@ export function renderWorkflowCatalog(policy) {
441
478
  "must name an exact migration source with a reason and removal condition.",
442
479
  "The policy, generator, checker, tests, required CI chain and CODEOWNERS require",
443
480
  "independent `kungfu-origin` review. No wildcard compatibility admission is allowed.",
481
+ "Workflow filenames cannot contain version tokens such as `v4` or `v5`.",
482
+ "Retired aliases cannot be recreated or called; see [path migration](workflow-path-migration.md).",
444
483
  "",
445
484
  "A role, category or implementation edit must preserve or deliberately update",
446
485
  "all related declarations. Run `pnpm run generate:workflows` and",