@kungfu-tech/buildchain 4.0.0 → 4.0.1-alpha.10

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 (228) hide show
  1. package/AGENTS.md +13 -5
  2. package/actions/promote-buildchain-ref/README.md +11 -6
  3. package/actions/release-tail/README.md +8 -0
  4. package/architecture/ci-lane-change-budget.json +187 -0
  5. package/architecture/decisions/0002-next-development-transition.md +101 -0
  6. package/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
  7. package/architecture/internal-capabilities.json +219 -7
  8. package/architecture/maintainability-policy.json +246 -29
  9. package/architecture/release-tail-contract-inventory.json +5 -2
  10. package/architecture/v3-core-mechanism-inventory.json +91 -0
  11. package/architecture/v4-adopter-delivery-parity.json +32 -5
  12. package/architecture/v4-capability-state-machine-manifest.json +73 -0
  13. package/architecture/v4-compatibility-facts-parity.json +77 -0
  14. package/architecture/v4-delivery-authority-parity.json +250 -0
  15. package/architecture/v4-delivery-warrant-shadow-fixtures.json +41 -9
  16. package/architecture/v4-floating-consumer-policy.json +81 -0
  17. package/architecture/v4-floating-consumer-policy.md +32 -0
  18. package/architecture/v4-next-development-parity.json +192 -0
  19. package/architecture/v4-publication-rehearsal-parity.json +84 -0
  20. package/architecture/v4-release-train-parity.json +1 -0
  21. package/architecture/v4-runtime-ref-resume-authority.json +64 -0
  22. package/architecture/v4-stage-capsule-qualification.json +2 -2
  23. package/architecture/v4-tail-reseal-parity.json +150 -0
  24. package/bin/buildchain.mjs +32 -3
  25. package/bin/internal/adopter-delivery-cli.mjs +70 -0
  26. package/bin/internal/command-registry.mjs +3 -0
  27. package/bin/internal/compatibility-facts-cli.mjs +101 -0
  28. package/bin/internal/trust-release-release-handlers.mjs +5 -0
  29. package/contracts/dev-delivery-authority-v2.schema.json +662 -0
  30. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
  31. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
  32. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -0
  33. package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
  34. package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
  35. package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
  36. package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
  37. package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
  38. package/contracts/fixtures/v4-compatibility-facts-v1/shared.json +1129 -0
  39. package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +59 -0
  40. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
  41. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
  42. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
  43. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
  44. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
  45. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
  46. package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
  47. package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
  48. package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
  49. package/contracts/fixtures/v4-runtime-ref-resume-authority-v1/scenario.json +15 -0
  50. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +229 -0
  51. package/contracts/next-development-request-v1.schema.json +66 -0
  52. package/contracts/next-development-transition-v1.schema.json +292 -0
  53. package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
  54. package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
  55. package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +107 -0
  56. package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  57. package/contracts/v4-runtime-ref-resume-authority-v1.schema.json +235 -0
  58. package/contracts/v4-tail-reseal-v1.schema.json +276 -0
  59. package/dist/site/agent-index.json +5 -0
  60. package/dist/site/artifact-schemas.json +10 -0
  61. package/dist/site/buildchain-contract.json +1562 -44
  62. package/dist/site/buildchain-site.json +684 -42
  63. package/dist/site/capability-registry.json +17 -14
  64. package/dist/site/cli-registry.json +205 -3
  65. package/dist/site/controller-registry.json +62 -6
  66. package/dist/site/kfd-claims.json +551 -22
  67. package/dist/site/kfd-upstream-aggregate.json +1 -1
  68. package/dist/site/manual-registry.json +55 -9
  69. package/dist/site/node-api-registry.json +15247 -8690
  70. package/dist/site/page-registry.json +647 -29
  71. package/dist/site/public-surface-audit.json +876 -55
  72. package/dist/site/publication-authority-registry.json +44 -8
  73. package/dist/site/publication-registry.json +4 -4
  74. package/dist/site/release-provenance.json +26 -0
  75. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -0
  76. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
  77. package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
  78. package/dist/site/schemas/v4-compatibility-facts-v1.schema.json +230 -0
  79. package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
  80. package/dist/site/site-manifest.json +37 -13
  81. package/dist/site/workflow-registry.json +200 -22
  82. package/docs/MAP.md +10 -2
  83. package/docs/cli-reference.md +404 -13
  84. package/docs/cli.md +8 -0
  85. package/docs/dev-alpha-candidate-patrol.md +8 -0
  86. package/docs/dev-delivery-qualification-landing-adr.md +251 -0
  87. package/docs/dev-delivery-warrant.md +361 -30
  88. package/docs/lifecycle-protocol.md +41 -0
  89. package/docs/next-development-transition.md +118 -0
  90. package/docs/node-api-reference.md +748 -322
  91. package/docs/release-tail-provider-plane.md +30 -0
  92. package/docs/reusable-build-surface.md +41 -0
  93. package/docs/runtime-train-validation.md +10 -0
  94. package/docs/v4-adopter-delivery.md +133 -0
  95. package/docs/v4-compatibility-facts.md +91 -0
  96. package/docs/v4-publication-rehearsal.md +130 -0
  97. package/docs/v4-runtime-ref-resume-authority.md +69 -0
  98. package/docs/v4-tail-reseal.md +73 -0
  99. package/docs/versioning.md +4 -2
  100. package/package.json +36 -6
  101. package/packages/core/adopter-delivery-passport.js +259 -0
  102. package/packages/core/adopter-delivery-vectors.js +158 -0
  103. package/packages/core/adopter-delivery-vectors.json +77 -0
  104. package/packages/core/artifact-signing.js +61 -0
  105. package/packages/core/buildchain-agent-manuals.js +3 -0
  106. package/packages/core/buildchain-compatibility-authority.js +336 -0
  107. package/packages/core/buildchain-compatibility-fact.js +394 -0
  108. package/packages/core/buildchain-compatibility-facts.json +217 -0
  109. package/packages/core/buildchain-compatibility-proof.js +566 -0
  110. package/packages/core/buildchain-config.js +150 -1
  111. package/packages/core/buildchain-contract.js +96 -143
  112. package/packages/core/buildchain-delivery-bootstrap.js +240 -0
  113. package/packages/core/buildchain-delivery-infrastructure.js +164 -0
  114. package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
  115. package/packages/core/buildchain-publication-authority.js +1 -0
  116. package/packages/core/channel-candidate.js +8 -0
  117. package/packages/core/channel-promotion-baseline.js +55 -0
  118. package/packages/core/ci-lane-change-budget.js +247 -0
  119. package/packages/core/dev-alpha-candidate-selection.js +10 -2
  120. package/packages/core/dev-delivery-authority-candidate.js +270 -0
  121. package/packages/core/dev-delivery-authority-evidence.js +146 -0
  122. package/packages/core/dev-delivery-authority-landing.js +461 -0
  123. package/packages/core/dev-delivery-authority-observation.js +48 -0
  124. package/packages/core/dev-delivery-authority-qualification.js +591 -0
  125. package/packages/core/dev-delivery-authority-settlement.js +213 -0
  126. package/packages/core/dev-delivery-authority-state.js +583 -0
  127. package/packages/core/dev-delivery-candidate-identity.js +13 -0
  128. package/packages/core/dev-delivery-contract-surface.js +76 -0
  129. package/packages/core/dev-delivery-execution-failure.js +133 -0
  130. package/packages/core/dev-delivery-execution-transfer.js +592 -0
  131. package/packages/core/dev-delivery-landing-admission-core.js +119 -0
  132. package/packages/core/dev-delivery-landing-readback.js +598 -0
  133. package/packages/core/dev-delivery-landing-terminal-evidence.js +271 -0
  134. package/packages/core/dev-delivery-landing-testing-port.js +6 -0
  135. package/packages/core/dev-delivery-native-execution.js +110 -0
  136. package/packages/core/dev-delivery-native-proof.js +562 -0
  137. package/packages/core/dev-delivery-process-boundary.js +563 -0
  138. package/packages/core/dev-delivery-proof.js +37 -3
  139. package/packages/core/dev-delivery-provider-attempt.js +127 -0
  140. package/packages/core/dev-delivery-provider-heartbeat.js +382 -0
  141. package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
  142. package/packages/core/dev-delivery-warrant-qualification.js +145 -0
  143. package/packages/core/dev-delivery-warrant-settlement.js +237 -36
  144. package/packages/core/dev-delivery-warrant-state.js +587 -0
  145. package/packages/core/dev-delivery-warrant.js +328 -370
  146. package/packages/core/github-governance-authority.js +1 -0
  147. package/packages/core/index.js +7 -0
  148. package/packages/core/kungfu-temporal-fact.js +557 -0
  149. package/packages/core/legacy-kfd-adopter-driver.js +167 -0
  150. package/packages/core/next-development-candidate-reservation.js +186 -0
  151. package/packages/core/next-development-controller.js +728 -0
  152. package/packages/core/next-development-projection.js +287 -0
  153. package/packages/core/next-development-transition.js +738 -0
  154. package/packages/core/paper-agent-entry.js +11 -5
  155. package/packages/core/paper.js +16 -5
  156. package/packages/core/publication-authority.js +5 -5
  157. package/packages/core/published-delivery-authority.js +266 -0
  158. package/packages/core/release-candidate-recovery.js +66 -11
  159. package/packages/core/release-candidate.js +79 -19
  160. package/packages/core/release-passport.js +271 -38
  161. package/packages/core/v4-adopter-delivery-parity.js +3 -3
  162. package/packages/core/v4-adopter-delivery.js +359 -0
  163. package/packages/core/v4-canonical-contracts.js +5 -0
  164. package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
  165. package/packages/core/v4-floating-consumer-evidence.js +338 -0
  166. package/packages/core/v4-floating-consumer-policy.js +569 -0
  167. package/packages/core/v4-floating-consumer-release-passport.js +153 -0
  168. package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
  169. package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
  170. package/packages/core/v4-publication-rehearsal.js +481 -0
  171. package/packages/core/v4-runtime-ref-resume-authority.js +625 -0
  172. package/packages/core/v4-runtime-selector-persistence.js +228 -0
  173. package/packages/core/v4-tail-reseal-contract.js +26 -0
  174. package/packages/core/v4-tail-reseal-github.js +178 -0
  175. package/packages/core/v4-tail-reseal-receipt.js +225 -0
  176. package/packages/core/v4-tail-reseal.js +594 -0
  177. package/packages/core/workflow-yaml-contract.js +72 -2
  178. package/scripts/audit-publication-control-plane.mjs +31 -31
  179. package/scripts/buildchain-cli-help.mjs +41 -2
  180. package/scripts/check-ci-lane-change-budget.mjs +100 -0
  181. package/scripts/check-inventory.mjs +14 -3
  182. package/scripts/check-v4-floating-consumer-policy-contract.mjs +217 -0
  183. package/scripts/check-v4-public-dogfood-contract.mjs +6 -11
  184. package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
  185. package/scripts/dev-delivery-authority-command-adapters.mjs +206 -0
  186. package/scripts/dev-delivery-authority-provider.mjs +28 -0
  187. package/scripts/dev-delivery-authority.mjs +490 -0
  188. package/scripts/dev-delivery-native-run.mjs +177 -0
  189. package/scripts/dev-delivery-process-boundary.mjs +260 -0
  190. package/scripts/dev-delivery-proof.mjs +67 -2
  191. package/scripts/dev-delivery-provider-heartbeat.mjs +215 -0
  192. package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
  193. package/scripts/dev-delivery-source-proof-reuse.mjs +547 -0
  194. package/scripts/dev-delivery-two-phase-resume.mjs +345 -0
  195. package/scripts/dev-delivery-two-phase.mjs +573 -0
  196. package/scripts/dev-delivery-warrant-options.mjs +266 -0
  197. package/scripts/dev-delivery-warrant-store.mjs +231 -0
  198. package/scripts/dev-delivery-warrant.mjs +232 -194
  199. package/scripts/dev-pr-auto-merge.mjs +37 -48
  200. package/scripts/dev-pr-delivery-warrant.mjs +216 -2
  201. package/scripts/dev-pr-prequeue-guard.mjs +399 -0
  202. package/scripts/dev-qualification-patrol.mjs +9 -9
  203. package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
  204. package/scripts/ensure-github-release.mjs +3 -3
  205. package/scripts/generate-channel-build-workflow.mjs +3 -0
  206. package/scripts/generate-channel-promotion-workflow.mjs +114 -14
  207. package/scripts/generate-next-development-guidance.mjs +49 -0
  208. package/scripts/generate-release-candidate-passport.mjs +192 -32
  209. package/scripts/generate-site-bundle.mjs +69 -4
  210. package/scripts/init-repo.mjs +26 -4
  211. package/scripts/inspect-artifact-signing-requests.mjs +6 -0
  212. package/scripts/next-development-self-dogfood-harness.mjs +460 -0
  213. package/scripts/next-development-self-dogfood.mjs +597 -0
  214. package/scripts/next-development-transition.mjs +47 -0
  215. package/scripts/npm-publish-transaction.mjs +103 -91
  216. package/scripts/promotion-channel-router.mjs +4 -4
  217. package/scripts/release-candidate-anchor-provenance.mjs +99 -0
  218. package/scripts/release-tail.mjs +51 -3
  219. package/scripts/release-train-self-dogfood.mjs +94 -92
  220. package/scripts/resume-from-candidate-run.mjs +565 -41
  221. package/scripts/seal-artifact-signing-requests.mjs +6 -0
  222. package/scripts/site-capability-metadata.mjs +43 -0
  223. package/scripts/stable-candidate-qualification.mjs +9 -10
  224. package/scripts/v4-consumer-policy.mjs +231 -0
  225. package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
  226. package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
  227. package/scripts/v4-tail-reseal.mjs +416 -0
  228. package/templates/native-dev-delivery.yml +143 -0
@@ -0,0 +1,228 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+
5
+ export const V4_RUNTIME_PERSISTENCE_SCAN_CONTRACT =
6
+ "kungfu-buildchain-v4-runtime-persistence-scan/v1";
7
+
8
+ const EXACT_SHA = /^[0-9a-f]{40}$/u;
9
+ const TRAIN_V4_REF = /^train\/v4\/v4\.\d+\/[A-Za-z0-9._/-]+$/u;
10
+ const AUTHORITY_V4_REF = /^authority\/v4\/v4\.\d+\/[A-Za-z0-9._/-]+$/u;
11
+ const RUNTIME_SELECTOR =
12
+ /(?:buildchain[-_. ]?(?:ref|runtime)|runtime[-_. ]?sha|resume[-_. ]?buildchain)/iu;
13
+ const EXACT_SHA_LITERAL = /\b[0-9a-f]{40}\b/giu;
14
+ const EXTERNAL_AUTHORITY = Object.freeze([
15
+ ["oidc", /(?:id-token\s*:\s*write|oidc)/iu],
16
+ ["iam", /(?:role-to-assume|aws[-_. ]?(?:role|iam)|\biam\b)/iu],
17
+ ["repository-variable", /\$\{\{\s*vars\./u],
18
+ ["secret", /\$\{\{\s*secrets\./u],
19
+ ["environment", /\$\{\{\s*env\./u],
20
+ ]);
21
+
22
+ function stableJson(value) {
23
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
24
+ if (value && typeof value === "object") {
25
+ return `{${Object.keys(value)
26
+ .sort()
27
+ .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
28
+ .join(",")}}`;
29
+ }
30
+ return JSON.stringify(value);
31
+ }
32
+
33
+ function documentRoot(value) {
34
+ return `sha256:${crypto.createHash("sha256").update(stableJson(value)).digest("hex")}`;
35
+ }
36
+
37
+ function normalizeRef(value) {
38
+ return String(value || "")
39
+ .trim()
40
+ .replace(/^refs\/(?:heads|tags)\//u, "");
41
+ }
42
+
43
+ function transientSelector(value) {
44
+ const normalized = normalizeRef(value);
45
+ return (
46
+ EXACT_SHA.test(normalized.toLowerCase()) ||
47
+ TRAIN_V4_REF.test(normalized) ||
48
+ AUTHORITY_V4_REF.test(normalized) ||
49
+ /\$\{\{\s*(?:vars|secrets|env)\./u.test(value)
50
+ );
51
+ }
52
+
53
+ function listFiles(rootPath, relative, output) {
54
+ const directory = path.join(rootPath, relative);
55
+ if (!fs.existsSync(directory)) return;
56
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
57
+ const child = path.join(relative, entry.name);
58
+ if (entry.isDirectory()) listFiles(rootPath, child, output);
59
+ else if (entry.isFile() && /\.(?:json|toml|ya?ml)$/u.test(entry.name)) {
60
+ output.push(child.split(path.sep).join("/"));
61
+ }
62
+ }
63
+ }
64
+
65
+ function inputDefaultFailures(lines, sourcePath) {
66
+ const failures = [];
67
+ let current;
68
+ for (const [index, line] of lines.entries()) {
69
+ const key = line.match(/^(\s*)([a-z0-9-]+):\s*(?:#.*)?$/u);
70
+ if (key) {
71
+ const name = key[2];
72
+ if (RUNTIME_SELECTOR.test(name))
73
+ current = { indent: key[1].length, name };
74
+ else if (current && key[1].length <= current.indent) current = undefined;
75
+ }
76
+ if (!current) continue;
77
+ const defaultMatch = line.match(/^\s*default:\s*(.*?)\s*(?:#.*)?$/u);
78
+ if (!defaultMatch) continue;
79
+ const value = defaultMatch[1]
80
+ .replace(/^(?:["'])(.*)(?:["'])$/u, "$1")
81
+ .trim();
82
+ if (transientSelector(value)) {
83
+ failures.push({
84
+ code: "persistent-runtime-default",
85
+ path: sourcePath,
86
+ line: index + 1,
87
+ selector: current.name,
88
+ });
89
+ }
90
+ }
91
+ return failures;
92
+ }
93
+
94
+ function jsonSelectorFailures(value, sourcePath, trail = []) {
95
+ if (Array.isArray(value)) {
96
+ return value.flatMap((entry, index) =>
97
+ jsonSelectorFailures(entry, sourcePath, [...trail, index]),
98
+ );
99
+ }
100
+ if (!value || typeof value !== "object") return [];
101
+ return Object.entries(value).flatMap(([key, entry]) => {
102
+ const entryTrail = [...trail, key];
103
+ const selected =
104
+ (RUNTIME_SELECTOR.test(key) ||
105
+ trail.some((part) => RUNTIME_SELECTOR.test(String(part)))) &&
106
+ typeof entry === "string" &&
107
+ transientSelector(entry)
108
+ ? [
109
+ {
110
+ code: "persistent-runtime-json-value",
111
+ path: sourcePath,
112
+ jsonPointer: `/${entryTrail.map((part) => String(part).replaceAll("~", "~0").replaceAll("/", "~1")).join("/")}`,
113
+ },
114
+ ]
115
+ : [];
116
+ return [
117
+ ...selected,
118
+ ...jsonSelectorFailures(entry, sourcePath, entryTrail),
119
+ ];
120
+ });
121
+ }
122
+
123
+ function inspectSource({
124
+ resolvedRoot,
125
+ sourcePath,
126
+ files,
127
+ failures,
128
+ authorityUsage,
129
+ }) {
130
+ const file = path.resolve(resolvedRoot, sourcePath);
131
+ if (!file.startsWith(`${resolvedRoot}${path.sep}`) || !fs.existsSync(file)) {
132
+ throw new Error(
133
+ `runtime persistence scan path is missing or escapes root: ${sourcePath}`,
134
+ );
135
+ }
136
+ const text = fs.readFileSync(file, "utf8");
137
+ const lines = text.split(/\r?\n/u);
138
+ files.push({ path: sourcePath, digest: documentRoot(text) });
139
+ failures.push(...inputDefaultFailures(lines, sourcePath));
140
+ if (sourcePath.endsWith(".json")) {
141
+ try {
142
+ failures.push(...jsonSelectorFailures(JSON.parse(text), sourcePath));
143
+ } catch (error) {
144
+ failures.push({
145
+ code: "runtime-selector-json-invalid",
146
+ path: sourcePath,
147
+ errorRoot: documentRoot(String(error?.message || error)),
148
+ });
149
+ }
150
+ }
151
+ for (const [index, line] of lines.entries()) {
152
+ if (RUNTIME_SELECTOR.test(line)) {
153
+ for (const match of line.matchAll(EXACT_SHA_LITERAL)) {
154
+ failures.push({
155
+ code: "persistent-runtime-exact-sha",
156
+ path: sourcePath,
157
+ line: index + 1,
158
+ selectorRoot: documentRoot(match[0].toLowerCase()),
159
+ });
160
+ }
161
+ if (/\$\{\{\s*(?:vars|secrets|env)\./u.test(line)) {
162
+ failures.push({
163
+ code: "persistent-runtime-external-indirection",
164
+ path: sourcePath,
165
+ line: index + 1,
166
+ });
167
+ }
168
+ }
169
+ for (const [authorityClass, pattern] of EXTERNAL_AUTHORITY) {
170
+ if (pattern.test(line)) {
171
+ authorityUsage.push({
172
+ class: authorityClass,
173
+ path: sourcePath,
174
+ line: index + 1,
175
+ });
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ export function scanV4RuntimeSelectorPersistence({
182
+ root: callerRoot = process.cwd(),
183
+ paths = undefined,
184
+ } = {}) {
185
+ const resolvedRoot = path.resolve(callerRoot);
186
+ const sourcePaths = [];
187
+ if (paths) sourcePaths.push(...paths);
188
+ else {
189
+ for (const relative of [
190
+ ".github/workflows",
191
+ ".github/actions",
192
+ ".buildchain",
193
+ ]) {
194
+ listFiles(resolvedRoot, relative, sourcePaths);
195
+ }
196
+ }
197
+ const excluded = new Set([
198
+ ".buildchain/contract-lock.json",
199
+ ".buildchain/alpha-contract-lock.json",
200
+ ]);
201
+ const files = [];
202
+ const failures = [];
203
+ const authorityUsage = [];
204
+ for (const sourcePath of [...new Set(sourcePaths)].sort()) {
205
+ if (!excluded.has(sourcePath)) {
206
+ inspectSource({
207
+ resolvedRoot,
208
+ sourcePath,
209
+ files,
210
+ failures,
211
+ authorityUsage,
212
+ });
213
+ }
214
+ }
215
+ const payload = {
216
+ schemaVersion: 1,
217
+ contract: V4_RUNTIME_PERSISTENCE_SCAN_CONTRACT,
218
+ status: failures.length === 0 ? "passed" : "rejected",
219
+ files,
220
+ authorityUsage: authorityUsage.sort((left, right) =>
221
+ stableJson(left).localeCompare(stableJson(right)),
222
+ ),
223
+ failures: failures.sort((left, right) =>
224
+ stableJson(left).localeCompare(stableJson(right)),
225
+ ),
226
+ };
227
+ return { ...payload, root: documentRoot(payload) };
228
+ }
@@ -0,0 +1,26 @@
1
+ export const V4_TAIL_RESEAL_REQUEST_CONTRACT =
2
+ "kungfu-buildchain-v4-tail-reseal-request/v1";
3
+ export const V4_TAIL_RESEAL_PLAN_CONTRACT =
4
+ "kungfu-buildchain-v4-tail-reseal-plan/v1";
5
+
6
+ export const V4_TAIL_RESEAL_PLATFORMS = Object.freeze([
7
+ "linux-arm64",
8
+ "linux-x64",
9
+ "macos-arm64",
10
+ "windows-x64",
11
+ ]);
12
+
13
+ export const V4_TAIL_RESEAL_PLATFORM_RUNNERS = Object.freeze({
14
+ "linux-arm64": '["ubuntu-24.04-arm"]',
15
+ "linux-x64": '["ubuntu-24.04"]',
16
+ "macos-arm64": '["macos-15"]',
17
+ "windows-x64": '["windows-2025"]',
18
+ });
19
+
20
+ export const V4_TAIL_RESEAL_REUSED_STAGE_KEYS = Object.freeze(
21
+ V4_TAIL_RESEAL_PLATFORMS.flatMap((platformId) =>
22
+ ["build", "install", "package", "verify"].map(
23
+ (stage) => `${platformId}:${stage}`,
24
+ ),
25
+ ).sort(),
26
+ );
@@ -0,0 +1,178 @@
1
+ import { v4ContentRoot } from "./v4-canonical-contracts.js";
2
+ import {
3
+ V4TailResealFault,
4
+ normalizeV4TailResealRequest,
5
+ } from "./v4-tail-reseal.js";
6
+
7
+ export const V4_TAIL_RESEAL_ADMISSION_CONTRACT =
8
+ "kungfu-buildchain-v4-tail-reseal-admission/v1";
9
+
10
+ const REQUIRED_SUCCESS_JOBS = Object.freeze([
11
+ "build / Linux ARM64",
12
+ "build / Linux x64",
13
+ "build / macOS ARM64",
14
+ "build / Windows x64",
15
+ "build / Control detached signing Linux ARM64",
16
+ "build / Control detached signing Linux x64",
17
+ "build / Control detached signing macOS ARM64",
18
+ "build / Control detached signing Windows x64",
19
+ "build / Finalize signed artifact Linux ARM64",
20
+ "build / Finalize signed artifact Linux x64",
21
+ "build / Finalize signed artifact Windows x64",
22
+ ]);
23
+
24
+ function fault(code, path, message) {
25
+ throw new V4TailResealFault(code, path, message);
26
+ }
27
+
28
+ function exactArtifact(artifacts, name, digest, path) {
29
+ const matches = artifacts.filter((artifact) => artifact.name === name);
30
+ if (matches.length !== 1)
31
+ fault(
32
+ "tail-reseal-artifact-ambiguous",
33
+ path,
34
+ "retained artifact must resolve exactly once",
35
+ );
36
+ const artifact = matches[0];
37
+ if (artifact.expired === true)
38
+ fault("tail-reseal-retention-expired", path, "retained artifact expired");
39
+ if (artifact.digest !== digest)
40
+ fault(
41
+ "tail-reseal-artifact-archive-mismatch",
42
+ path,
43
+ "GitHub artifact archive digest drifted",
44
+ );
45
+ return artifact;
46
+ }
47
+
48
+ function validateSourceIdentity(normalized, sourceCommit, sourceRun) {
49
+ if (
50
+ sourceCommit?.sha !== normalized.source.sha ||
51
+ sourceCommit?.tree?.sha !== normalized.source.treeSha
52
+ )
53
+ fault(
54
+ "tail-reseal-source-mismatch",
55
+ "$/sourceCommit",
56
+ "source commit or tree readback drifted",
57
+ );
58
+ const workflowFile = String(sourceRun?.path || "")
59
+ .split("@")[0]
60
+ .replace(/^\.github\/workflows\//u, "");
61
+ if (
62
+ Number(sourceRun?.id) !== normalized.source.runId ||
63
+ Number(sourceRun?.run_attempt) !== normalized.source.runAttempt ||
64
+ sourceRun?.event !== "workflow_dispatch" ||
65
+ sourceRun?.status !== "completed" ||
66
+ sourceRun?.conclusion !== "failure" ||
67
+ sourceRun?.head_sha !== normalized.source.sha ||
68
+ workflowFile !== normalized.source.workflowFile ||
69
+ sourceRun?.name !== normalized.source.workflowName
70
+ )
71
+ fault(
72
+ "tail-reseal-source-run-mismatch",
73
+ "$/sourceRun",
74
+ "source run is not the exact retained failed Build run",
75
+ );
76
+ }
77
+
78
+ function validateSourceJobs(normalized, sourceJobs) {
79
+ const jobs = Array.isArray(sourceJobs) ? sourceJobs : [];
80
+ const byName = new Map(jobs.map((job) => [job.name, job]));
81
+ for (const name of REQUIRED_SUCCESS_JOBS)
82
+ if (byName.get(name)?.conclusion !== "success")
83
+ fault(
84
+ "tail-reseal-prior-stage-mismatch",
85
+ "$/sourceJobs",
86
+ `required source job did not succeed: ${name}`,
87
+ );
88
+ const failedJob = jobs.find(
89
+ (job) => Number(job.id) === normalized.failure.jobId,
90
+ );
91
+ const failedSteps = (failedJob?.steps || [])
92
+ .filter((step) => step.conclusion === "failure")
93
+ .map((step) => step.name);
94
+ if (
95
+ failedJob?.name !== normalized.failure.jobName ||
96
+ failedJob?.conclusion !== "failure" ||
97
+ JSON.stringify(failedSteps) !==
98
+ JSON.stringify([normalized.failure.stepName])
99
+ )
100
+ fault(
101
+ "tail-reseal-failure-mismatch",
102
+ "$/sourceJobs",
103
+ "observed failure is not the single authorized macOS tail failure",
104
+ );
105
+ }
106
+
107
+ function validateRetainedArtifacts(normalized, sourceArtifacts) {
108
+ for (const [index, platform] of normalized.platforms.entries()) {
109
+ exactArtifact(
110
+ sourceArtifacts || [],
111
+ platform.artifactName,
112
+ platform.artifactArchiveRoot,
113
+ `$/sourceArtifacts/${index}/payload`,
114
+ );
115
+ exactArtifact(
116
+ sourceArtifacts || [],
117
+ platform.manifestArtifactName,
118
+ platform.manifestArchiveRoot,
119
+ `$/sourceArtifacts/${index}/manifest`,
120
+ );
121
+ }
122
+ }
123
+
124
+ function validateSigningAuthority(normalized, signingRun, signingArtifacts) {
125
+ if (
126
+ Number(signingRun?.id) !== normalized.signing.authorityRunId ||
127
+ signingRun?.status !== "completed" ||
128
+ signingRun?.conclusion !== "success" ||
129
+ signingRun?.head_sha !== normalized.signing.runtimeSha
130
+ )
131
+ fault(
132
+ "tail-reseal-signer-authority-mismatch",
133
+ "$/signingRun",
134
+ "signing authority run readback drifted",
135
+ );
136
+ exactArtifact(
137
+ signingArtifacts || [],
138
+ normalized.signing.resultArtifact,
139
+ normalized.signing.resultArtifactRoot,
140
+ "$/signingArtifacts/result",
141
+ );
142
+ }
143
+
144
+ export function validateV4TailResealGitHubEvidence({
145
+ request,
146
+ sourceCommit,
147
+ sourceRun,
148
+ sourceJobs,
149
+ sourceArtifacts,
150
+ signingRun,
151
+ signingArtifacts,
152
+ } = {}) {
153
+ const normalized = normalizeV4TailResealRequest(request);
154
+ validateSourceIdentity(normalized, sourceCommit, sourceRun);
155
+ validateSourceJobs(normalized, sourceJobs);
156
+ validateRetainedArtifacts(normalized, sourceArtifacts);
157
+ validateSigningAuthority(normalized, signingRun, signingArtifacts);
158
+ const payload = {
159
+ schema: V4_TAIL_RESEAL_ADMISSION_CONTRACT,
160
+ repository: normalized.repository,
161
+ sourceRoot: normalized.source.sourceRoot,
162
+ sourceRunId: normalized.source.runId,
163
+ signingAuthorityRunId: normalized.signing.authorityRunId,
164
+ retainedArchiveRoots: normalized.platforms.flatMap((platform) => [
165
+ platform.artifactArchiveRoot,
166
+ platform.manifestArchiveRoot,
167
+ ]),
168
+ priorFailureRoot: normalized.failure.evidenceRoot,
169
+ retentionReadbackRoot: normalized.retention.readbackRoot,
170
+ warrantReadbackRoot: normalized.warrant.stateReadbackRoot,
171
+ };
172
+ return {
173
+ ...payload,
174
+ admissionRoot: v4ContentRoot("tail-reseal-admission", payload),
175
+ };
176
+ }
177
+
178
+ export { REQUIRED_SUCCESS_JOBS as V4_TAIL_RESEAL_REQUIRED_SUCCESS_JOBS };
@@ -0,0 +1,225 @@
1
+ import { v4ContentRoot, validateV4Root } from "./v4-canonical-contracts.js";
2
+ import { validateReleaseCandidatePassport } from "./release-candidate.js";
3
+ import {
4
+ V4_TAIL_RESEAL_PLAN_CONTRACT,
5
+ V4_TAIL_RESEAL_PLATFORMS,
6
+ V4TailResealFault,
7
+ normalizeV4TailResealRequest,
8
+ planV4TailReseal,
9
+ } from "./v4-tail-reseal.js";
10
+
11
+ export const V4_TAIL_RESEAL_RECEIPT_CONTRACT =
12
+ "kungfu-buildchain-v4-tail-reseal-receipt/v1";
13
+
14
+ function fault(code, path, message) {
15
+ throw new V4TailResealFault(code, path, message);
16
+ }
17
+
18
+ function root(value, path) {
19
+ try {
20
+ validateV4Root(value, path);
21
+ } catch (error) {
22
+ fault("invalid-tail-reseal-root", path, error.message);
23
+ }
24
+ return value;
25
+ }
26
+
27
+ function exactKeys(value, expected, path) {
28
+ if (!value || typeof value !== "object" || Array.isArray(value))
29
+ fault("invalid-tail-reseal-shape", path, "object required");
30
+ const actual = Object.keys(value).sort();
31
+ const canonical = [...expected].sort();
32
+ if (
33
+ actual.length !== canonical.length ||
34
+ actual.some((key, index) => key !== canonical[index])
35
+ )
36
+ fault(
37
+ "invalid-tail-reseal-shape",
38
+ path,
39
+ "keys do not match the closed tail-reseal contract",
40
+ );
41
+ }
42
+
43
+ function positiveInteger(value, path) {
44
+ if (!Number.isSafeInteger(value) || value < 1)
45
+ fault(
46
+ "invalid-tail-reseal-integer",
47
+ path,
48
+ "positive safe integer required",
49
+ );
50
+ }
51
+
52
+ function validateReadbacks(readbacks, request) {
53
+ if (
54
+ !Array.isArray(readbacks) ||
55
+ readbacks.length !== V4_TAIL_RESEAL_PLATFORMS.length
56
+ )
57
+ fault(
58
+ "tail-reseal-readback-mismatch",
59
+ "$/readbacks",
60
+ "one readback per platform required",
61
+ );
62
+ const normalized = [...readbacks].sort((left, right) =>
63
+ Buffer.from(left.platformId || "").compare(
64
+ Buffer.from(right.platformId || ""),
65
+ ),
66
+ );
67
+ for (const [index, readback] of normalized.entries()) {
68
+ const path = `$/readbacks/${index}`;
69
+ exactKeys(
70
+ readback,
71
+ [
72
+ "platformId",
73
+ "artifactRoot",
74
+ "manifestRoot",
75
+ "capsuleRoot",
76
+ "byteIdentical",
77
+ "providerReadbackRoot",
78
+ ],
79
+ path,
80
+ );
81
+ const expected = request.platforms[index];
82
+ if (readback.platformId !== expected.id)
83
+ fault(
84
+ "tail-reseal-platform-mismatch",
85
+ `${path}/platformId`,
86
+ "unexpected platform readback",
87
+ );
88
+ for (const field of ["artifactRoot", "manifestRoot", "capsuleRoot"])
89
+ root(readback[field], `${path}/${field}`);
90
+ if (readback.capsuleRoot !== expected.capsuleRoot)
91
+ fault(
92
+ "tail-reseal-capsule-mismatch",
93
+ `${path}/capsuleRoot`,
94
+ "Stage Capsule identity changed during tail execution",
95
+ );
96
+ if (readback.platformId === "macos-arm64") {
97
+ if (readback.byteIdentical !== false)
98
+ fault(
99
+ "tail-reseal-macos-effect-missing",
100
+ `${path}/byteIdentical`,
101
+ "macOS signed bytes must be independently resealed",
102
+ );
103
+ root(readback.providerReadbackRoot, `${path}/providerReadbackRoot`);
104
+ } else if (
105
+ readback.byteIdentical !== true ||
106
+ readback.artifactRoot !== expected.artifactRoot ||
107
+ readback.manifestRoot !== expected.manifestRoot ||
108
+ readback.providerReadbackRoot !== null
109
+ )
110
+ fault(
111
+ "tail-reseal-nontail-byte-drift",
112
+ path,
113
+ "non-macOS retained artifacts must remain byte-identical and effect-free",
114
+ );
115
+ }
116
+ return normalized;
117
+ }
118
+
119
+ export function createV4TailResealReceipt({
120
+ request,
121
+ plan,
122
+ readbacks,
123
+ passport,
124
+ protectedReadbackRoot,
125
+ currentRun,
126
+ } = {}) {
127
+ const normalized = normalizeV4TailResealRequest(request);
128
+ const expectedPlan = planV4TailReseal(normalized);
129
+ const { planRoot: _planRoot, ...planBody } = plan || {};
130
+ if (
131
+ plan?.schema !== V4_TAIL_RESEAL_PLAN_CONTRACT ||
132
+ plan.planRoot !== expectedPlan.planRoot ||
133
+ v4ContentRoot("tail-reseal-plan", planBody) !== expectedPlan.planRoot
134
+ )
135
+ fault(
136
+ "tail-reseal-plan-drift",
137
+ "$/plan",
138
+ "plan does not match the exact request",
139
+ );
140
+ const platformReadbacks = validateReadbacks(readbacks, normalized);
141
+ const passportValidation = validateReleaseCandidatePassport({
142
+ passport,
143
+ repository: normalized.repository,
144
+ targetChannel: normalized.target.channel,
145
+ version: normalized.target.version,
146
+ sourceHeadSha: normalized.source.sha,
147
+ requirePlatforms: true,
148
+ });
149
+ if (!passportValidation.ok)
150
+ fault(
151
+ "tail-reseal-passport-invalid",
152
+ "$/passport",
153
+ passportValidation.errors.join("; "),
154
+ );
155
+ if (
156
+ passport.contract !== "kungfu-buildchain-release-candidate-passport" ||
157
+ passport.buildchain?.sha !== normalized.runtime.sha ||
158
+ passport.consumerPolicy?.receiptRoot !==
159
+ normalized.runtime.consumerPolicyReceiptRoot
160
+ )
161
+ fault(
162
+ "tail-reseal-passport-binding-mismatch",
163
+ "$/passport",
164
+ "standard v4 candidate Passport bindings drifted",
165
+ );
166
+ root(protectedReadbackRoot, "$/protectedReadbackRoot");
167
+ exactKeys(currentRun, ["id", "attempt"], "$/currentRun");
168
+ positiveInteger(currentRun.id, "$/currentRun/id");
169
+ positiveInteger(currentRun.attempt, "$/currentRun/attempt");
170
+ const payload = {
171
+ schema: V4_TAIL_RESEAL_RECEIPT_CONTRACT,
172
+ action: "reused-retained-tail",
173
+ repository: normalized.repository,
174
+ source: normalized.source,
175
+ target: normalized.target,
176
+ runtime: normalized.runtime,
177
+ planRoot: expectedPlan.planRoot,
178
+ warrant: normalized.warrant,
179
+ retention: normalized.retention,
180
+ originalFailure: normalized.failure,
181
+ signingAuthority: normalized.signing,
182
+ releaseTail: normalized.releaseTail,
183
+ platformReadbacks,
184
+ skippedStages: expectedPlan.skippedStages,
185
+ rerunStages: expectedPlan.rerunStages,
186
+ passport: {
187
+ contract: passport.contract,
188
+ candidateHash: passport.candidateHash,
189
+ passportRoot: v4ContentRoot("release-candidate-passport", passport),
190
+ },
191
+ protectedReadbackRoot,
192
+ currentRun,
193
+ };
194
+ return {
195
+ ...payload,
196
+ receiptRoot: v4ContentRoot("tail-reseal-receipt", payload),
197
+ };
198
+ }
199
+
200
+ export function verifyV4TailResealReceipt({ receipt, request, passport } = {}) {
201
+ try {
202
+ const expected = createV4TailResealReceipt({
203
+ request,
204
+ plan: planV4TailReseal(request),
205
+ readbacks: receipt.platformReadbacks,
206
+ passport,
207
+ protectedReadbackRoot: receipt.protectedReadbackRoot,
208
+ currentRun: receipt.currentRun,
209
+ });
210
+ return {
211
+ ok: expected.receiptRoot === receipt.receiptRoot,
212
+ receiptRoot: expected.receiptRoot,
213
+ failures:
214
+ expected.receiptRoot === receipt.receiptRoot
215
+ ? []
216
+ : [{ code: "tail-reseal-receipt-root-mismatch" }],
217
+ };
218
+ } catch (error) {
219
+ return {
220
+ ok: false,
221
+ receiptRoot: "",
222
+ failures: [{ code: error.code || "tail-reseal-receipt-invalid" }],
223
+ };
224
+ }
225
+ }