@kungfu-tech/buildchain 4.1.3 → 4.1.4-alpha.0

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 (199) hide show
  1. package/AGENTS.md +11 -13
  2. package/CONTRIBUTING.md +32 -9
  3. package/README.md +56 -140
  4. package/actions/release/promotion/ref/README.md +1 -1
  5. package/architecture/action-taxonomy.json +1 -1
  6. package/architecture/agent-change-map.md +32 -45
  7. package/architecture/build-orchestration.json +1 -1
  8. package/architecture/capability-state-machine-manifest.json +5 -5
  9. package/architecture/ci-lane-change-budget.json +92 -92
  10. package/architecture/code-layout.json +2 -12
  11. package/architecture/core-mechanisms.json +38 -39
  12. package/architecture/cross-platform-adopter-qualification.json +1 -1
  13. package/architecture/dev-delivery-orchestration.json +2 -2
  14. package/architecture/floating-consumer-policy.json +3 -3
  15. package/architecture/internal-capabilities.json +27 -28
  16. package/architecture/maintainability-debt.json +60 -94
  17. package/architecture/maintainability-policy.json +7 -13
  18. package/architecture/minimal-consumer-migration.json +43 -5014
  19. package/architecture/platform-stage-checkpoints.json +1 -1
  20. package/architecture/release-tail-contract.json +2 -2
  21. package/architecture/release-topology.json +31 -25
  22. package/architecture/stage-capsule-qualification.json +1 -1
  23. package/architecture/universal-workflow-bootstrap.json +60 -60
  24. package/architecture/universal-workflow-capability-policy.json +3 -3
  25. package/architecture/universal-workflow-fault-campaign.json +7 -7
  26. package/architecture/workflow-taxonomy.json +59 -60
  27. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  28. package/dist/site/buildchain-contract.json +138 -66
  29. package/dist/site/buildchain-site.json +257 -331
  30. package/dist/site/capability-registry.json +4 -4
  31. package/dist/site/cli-registry.json +6 -142
  32. package/dist/site/controller-registry.json +19 -19
  33. package/dist/site/kfd-claims.json +481 -529
  34. package/dist/site/kfd-upstream-aggregate.json +1 -1
  35. package/dist/site/manual-registry.json +22 -22
  36. package/dist/site/node-api-registry.json +82 -339
  37. package/dist/site/page-registry.json +229 -304
  38. package/dist/site/public-surface-audit.json +544 -754
  39. package/dist/site/publication-authority-registry.json +316 -316
  40. package/dist/site/publication-registry.json +1 -1
  41. package/dist/site/site-manifest.json +23 -23
  42. package/dist/site/workflow-registry.json +1071 -1027
  43. package/docs/MAP.md +28 -20
  44. package/docs/auditable-demo.md +17 -49
  45. package/docs/bootstrap-recovery.md +46 -37
  46. package/docs/cli-reference.md +2 -46
  47. package/docs/cli.md +97 -186
  48. package/docs/code-organization.md +7 -5
  49. package/docs/dev-alpha-candidate-patrol.md +19 -42
  50. package/docs/dev-delivery-orchestration.md +15 -11
  51. package/docs/dev-delivery-qualification-landing-adr.md +10 -9
  52. package/docs/dev-delivery-warrant.md +17 -16
  53. package/docs/dev-qualification-patrol.md +25 -20
  54. package/docs/getting-started.md +80 -139
  55. package/docs/github-artifact-attestation.md +31 -81
  56. package/docs/install.md +66 -30
  57. package/docs/lifecycle-protocol.md +19 -57
  58. package/docs/migration-inventory.md +17 -45
  59. package/docs/next-development-transition.md +13 -13
  60. package/docs/node-api-reference.md +122 -138
  61. package/docs/oci-publication.md +13 -10
  62. package/docs/publication-artifacts.md +128 -486
  63. package/docs/publication-authority.md +15 -45
  64. package/docs/publish-transaction.md +13 -11
  65. package/docs/release-candidate.md +126 -355
  66. package/docs/release-discussions.md +36 -67
  67. package/docs/release-flow.md +80 -339
  68. package/docs/release-governance.md +114 -962
  69. package/docs/release-promotion-request.md +45 -43
  70. package/docs/release-propagation.md +13 -27
  71. package/docs/release-tail-provider-plane.md +38 -65
  72. package/docs/reusable-build-surface.md +78 -196
  73. package/docs/runtime-train-validation.md +8 -6
  74. package/docs/stable-candidate-patrol.md +13 -66
  75. package/docs/v4-adopter-delivery.md +15 -15
  76. package/docs/v4-declarative-release-tail-migration.md +19 -17
  77. package/docs/v4-production-release.md +11 -6
  78. package/docs/v4-publication-rehearsal.md +12 -27
  79. package/docs/v4-stage-capsule.md +14 -20
  80. package/docs/v4-tail-reseal.md +34 -20
  81. package/docs/web-surface-deployments.md +26 -264
  82. package/docs/workflow-catalog.md +31 -31
  83. package/docs/workflow-path-migration.md +10 -4
  84. package/package.json +5 -4
  85. package/packages/core/adoption/buildchain-kfd-claims.js +7 -7
  86. package/packages/core/adoption/cli/output.mjs +1 -9
  87. package/packages/core/adoption/cli/product-gate.mjs +1 -15
  88. package/packages/core/adoption/cli/project.mjs +9 -10
  89. package/packages/core/adoption/cli/standards.mjs +1 -14
  90. package/packages/core/adoption/cli/support.mjs +0 -2
  91. package/packages/core/adoption/cli/surface-register.mjs +0 -2
  92. package/packages/core/adoption/commands/init-repo.mjs +77 -485
  93. package/packages/core/adoption/consumer-init.js +279 -0
  94. package/packages/core/adoption/qualification/admission.js +1 -1
  95. package/packages/core/build/cli/cache.mjs +0 -2
  96. package/packages/core/build/cli/facts.mjs +0 -3
  97. package/packages/core/build/cli/homebrew.mjs +0 -4
  98. package/packages/core/build/cli/lifecycle.mjs +0 -3
  99. package/packages/core/build/github-artifact-attestation.js +2 -2
  100. package/packages/core/build/platform-stage-checkpoints.js +1 -1
  101. package/packages/core/build/signing/dispatch.js +1 -1
  102. package/packages/core/consumer/buildchain-config.js +9 -0
  103. package/packages/core/consumer/contract/inspection.js +351 -9
  104. package/packages/core/consumer/contract/javascript-syntax.js +439 -0
  105. package/packages/core/consumer/contract/javascript-values.js +61 -0
  106. package/packages/core/consumer/contract/javascript.js +555 -0
  107. package/packages/core/consumer/contract/local-validation.js +84 -0
  108. package/packages/core/contracts/buildchain-contract.js +39 -8
  109. package/packages/core/contracts/cli/introspection.mjs +1 -9
  110. package/packages/core/contracts/public-surface-cli.js +0 -1
  111. package/packages/core/dev-delivery/dev-delivery-contract-surface.js +2 -2
  112. package/packages/core/governance/buildchain-publication-authority.js +24 -24
  113. package/packages/core/governance/cli/doctor.mjs +58 -42
  114. package/packages/core/governance/commands/architecture.mjs +6 -1
  115. package/packages/core/governance/commands/audit-publication-control-plane.mjs +1 -1
  116. package/packages/core/governance/commands/workflow-friction-report.mjs +1 -1
  117. package/packages/core/governance/github-governance-authority.js +2 -2
  118. package/packages/core/governance/publication/collection.js +1 -1
  119. package/packages/core/index.js +1 -3
  120. package/packages/core/observability/cli/candidate.mjs +0 -2
  121. package/packages/core/observability/cli/context.mjs +0 -5
  122. package/packages/core/observability/cli/diagnostics.mjs +0 -7
  123. package/packages/core/observability/cli/events.mjs +2 -10
  124. package/packages/core/paper/commands/paper-work-fleet-cli.mjs +1 -1
  125. package/packages/core/paper/commands/paper.mjs +33 -422
  126. package/packages/core/paper/consumer-status.js +72 -0
  127. package/packages/core/paper/operations/identity.js +0 -3
  128. package/packages/core/paper/operations/migration.js +460 -0
  129. package/packages/core/paper/operations/preflight-checks.js +8 -68
  130. package/packages/core/paper/operations/scaffold.js +16 -326
  131. package/packages/core/paper/operations/status.js +27 -53
  132. package/packages/core/paper/paper-agent-entry.js +36 -147
  133. package/packages/core/paper/paper-fleet.js +24 -39
  134. package/packages/core/paper/paper-repository.js +16 -0
  135. package/packages/core/paper/paper-runtime-channels.js +1 -1
  136. package/packages/core/paper/paper-scaffold-content.js +153 -162
  137. package/packages/core/paper/paper-work.js +3 -10
  138. package/packages/core/paper/paper.js +43 -5
  139. package/packages/core/providers/github/artifact-attestation.js +1 -1
  140. package/packages/core/providers/github/pipeline-context-artifacts.js +172 -0
  141. package/packages/core/publication/authority/control-plane.js +2 -2
  142. package/packages/core/publication/authority/seal.js +1 -1
  143. package/packages/core/publication/cli/dispatch.mjs +1 -12
  144. package/packages/core/publication/pipeline/actions.js +16 -9
  145. package/packages/core/publication/pipeline/apply.js +17 -9
  146. package/packages/core/publication/pipeline/context.js +29 -0
  147. package/packages/core/publication/pipeline/prepare.js +15 -6
  148. package/packages/core/publication/pipeline/recovery-materials.js +63 -1
  149. package/packages/core/publication/pipeline/recovery-prepare.js +21 -9
  150. package/packages/core/publication/pipeline/stable-entry.js +12 -7
  151. package/packages/core/publication/pipeline/stable-products.js +21 -19
  152. package/packages/core/publication/pipeline/version-actions.js +15 -6
  153. package/packages/core/publication/publication-rehearsal-projection.js +0 -9
  154. package/packages/core/release/cli/dispatch.mjs +1 -4
  155. package/packages/core/release/cli/inspection-handlers.mjs +1 -33
  156. package/packages/core/release/cli/release-handlers.mjs +2 -19
  157. package/packages/core/release/cli/verification-handlers.mjs +4 -25
  158. package/packages/core/release/next-development-projection.js +13 -13
  159. package/packages/core/release/passport/locations.js +13 -8
  160. package/packages/core/release/release-passport.js +70 -5
  161. package/packages/core/web/cli/badges.mjs +0 -4
  162. package/packages/core/web/publication-admission.js +1 -1
  163. package/packages/core/web/publication-capability.js +1 -1
  164. package/packages/core/web/readme-badges.js +1 -1
  165. package/packages/core/workflow/cli/main.mjs +1 -4
  166. package/packages/core/workflow/cli/process.mjs +1 -1
  167. package/packages/core/workflow/commands/buildchain-cli-help.mjs +2 -8
  168. package/packages/core/workflow/engine/conformance.js +1 -1
  169. package/packages/core/workflow/pipeline/build.js +21 -1
  170. package/packages/core/workflow/pipeline/delivery-observation.js +6 -2
  171. package/packages/core/workflow/workflow-taxonomy.mjs +61 -17
  172. package/scripts/check-code-layout.mjs +2 -0
  173. package/scripts/check-core-mechanism-inventory.mjs +29 -13
  174. package/scripts/check-floating-consumer-policy-contract.mjs +3 -3
  175. package/scripts/check-inventory.mjs +27 -27
  176. package/scripts/check-maintainability.mjs +28 -5
  177. package/scripts/check-public-dogfood-contract.mjs +3 -3
  178. package/scripts/check-release-topology.mjs +1 -1
  179. package/scripts/check-self-consumer-contract.mjs +7 -7
  180. package/scripts/check-universal-workflow-bootstrap.mjs +1 -1
  181. package/scripts/generate-channel-promotion-workflow.mjs +1 -1
  182. package/scripts/generate-minimal-consumer-contract.mjs +21 -1
  183. package/scripts/generate-site-bundle.mjs +2 -1
  184. package/scripts/generate-tail-reseal-fixture.mjs +3 -3
  185. package/scripts/inventory/promotion.mjs +1 -1
  186. package/scripts/next-development-self-dogfood-harness.mjs +1 -1
  187. package/scripts/next-development-self-dogfood.mjs +4 -4
  188. package/scripts/site-capability-metadata.mjs +2 -6
  189. package/scripts/source-metric-lineage.mjs +62 -7
  190. package/scripts/verify-golden-path.mjs +31 -35
  191. package/templates/native-dev-delivery.yml +1 -1
  192. package/templates/universal-buildchain-bootstrap.yml +1 -1
  193. package/packages/core/paper/operations/bootstrap.js +0 -75
  194. package/packages/core/paper/operations/plans.js +0 -213
  195. package/packages/core/paper/operations/provisioning.js +0 -113
  196. package/packages/core/paper/operations/scaffold-config.js +0 -77
  197. package/packages/core/paper/operations/scaffold-workflows.js +0 -116
  198. package/packages/core/paper/operations/workspace.js +0 -60
  199. package/packages/core/paper/paper-npm-bootstrap.js +0 -492
@@ -0,0 +1,555 @@
1
+ import { parse } from "acorn";
2
+ import path from "node:path";
3
+ import { isBuiltin } from "node:module";
4
+ import { serialize } from "node:v8";
5
+ import { createHash } from "node:crypto";
6
+ import {
7
+ UNKNOWN,
8
+ TAG,
9
+ tagged,
10
+ namespace,
11
+ concreteArguments,
12
+ processCalls,
13
+ inertModules,
14
+ inertGlobals,
15
+ } from "./javascript-values.js";
16
+ import { evaluateExpression, evaluateStatements } from "./javascript-syntax.js";
17
+ const moduleName = (value) => value.replace(/^node:/u, "");
18
+ class InvocationContext {
19
+ constructor({ file, cwd, maxNodes }) {
20
+ for (const key of Object.getOwnPropertyNames(InvocationContext.prototype))
21
+ if (key !== "constructor") this[key] = this[key].bind(this);
22
+
23
+ const edges = [],
24
+ problems = [],
25
+ exports = new Map(),
26
+ active = new Set();
27
+ const data = () => tagged("data", {});
28
+ this.remaining = maxNodes;
29
+ const report = (node, code, message) =>
30
+ problems.push({ file, line: node?.loc?.start.line || 1, code, message });
31
+ const edge = (node, value) => {
32
+ if (value.arguments && !concreteArguments(value.arguments)) {
33
+ report(
34
+ node,
35
+ "unresolved-edge-arguments",
36
+ "Cross-module or network arguments must resolve as data",
37
+ );
38
+ return;
39
+ }
40
+ edges.push({ file, line: node.loc.start.line, ...value });
41
+ };
42
+ const root = new Map([
43
+ ["require", tagged("require", {})],
44
+ ["undefined", undefined],
45
+ ["process", namespace("process")],
46
+ ]);
47
+ for (const name of inertGlobals) root.set(name, namespace(name));
48
+ root.set("fetch", tagged("network", { name: "fetch" }));
49
+ root.set("eval", tagged("dynamic-code", {}));
50
+ root.set("Function", tagged("dynamic-code", {}));
51
+ // Lexical scopes share binding cells. A copied block/function environment
52
+ // must observe assignments to an outer binding, while declarations shadow it.
53
+ for (const [name, value] of root) root.set(name, { value });
54
+ const get = (scope, name) =>
55
+ scope.has(name) ? scope.get(name).value : UNKNOWN;
56
+ const declare = (scope, name, value) => scope.set(name, { value });
57
+
58
+ Object.assign(this, {
59
+ edges,
60
+ problems,
61
+ exports,
62
+ active,
63
+ data,
64
+ report,
65
+ edge,
66
+ root,
67
+ get,
68
+ declare,
69
+ cwd,
70
+ });
71
+ }
72
+ visit() {
73
+ if (--this.remaining < 0)
74
+ throw Error("JavaScript invocation analysis exceeded its node budget");
75
+ }
76
+ expression(node, scope) {
77
+ return evaluateExpression(this, node, scope);
78
+ }
79
+ statements(nodes, scope) {
80
+ return evaluateStatements(this, nodes, scope);
81
+ }
82
+ checkCoercion(node, values) {
83
+ const ctx = this;
84
+ const { report } = ctx;
85
+
86
+ if (
87
+ values.some(
88
+ (value) =>
89
+ value === UNKNOWN ||
90
+ (value !== null &&
91
+ typeof value === "object" &&
92
+ value[TAG] !== "data" &&
93
+ !(
94
+ value[TAG] === "data-method" && value.receiver?.[TAG] === "data"
95
+ )),
96
+ )
97
+ )
98
+ report(
99
+ node,
100
+ "unresolved-coercion",
101
+ "Object or unknown-value coercion may invoke executable code",
102
+ );
103
+ }
104
+ bind(pattern, value, scope) {
105
+ const ctx = this;
106
+ const { bind, member, expression, report, declare } = ctx;
107
+
108
+ if (!pattern) return;
109
+ if (pattern.type === "Identifier") declare(scope, pattern.name, value);
110
+ else if (pattern.type === "AssignmentPattern")
111
+ bind(
112
+ pattern.left,
113
+ value === undefined ? expression(pattern.right, scope) : value,
114
+ scope,
115
+ );
116
+ else if (pattern.type === "ObjectPattern")
117
+ for (const property of pattern.properties) {
118
+ if (property.type === "RestElement")
119
+ bind(property.argument, UNKNOWN, scope);
120
+ else
121
+ bind(
122
+ property.value,
123
+ member(value, property.key.name ?? property.key.value),
124
+ scope,
125
+ );
126
+ }
127
+ else if (pattern.type === "ArrayPattern")
128
+ pattern.elements.forEach((item, index) =>
129
+ bind(item, Array.isArray(value) ? value[index] : UNKNOWN, scope),
130
+ );
131
+ else
132
+ report(pattern, "unresolved-binding", "Unsupported executable binding");
133
+ }
134
+ member(value, key) {
135
+ const ctx = this;
136
+
137
+ if (typeof key !== "string" && typeof key !== "number") return UNKNOWN;
138
+ if (value?.[TAG] === "data")
139
+ return tagged("data-method", { receiver: value, name: String(key) });
140
+ if (value?.[TAG] === "namespace")
141
+ return tagged("member", {
142
+ specifier: value.specifier,
143
+ name: String(key),
144
+ });
145
+ if (value === UNKNOWN || value == null) return UNKNOWN;
146
+ if (typeof value === "string" || Array.isArray(value)) {
147
+ if (key === "length") return value.length;
148
+ if (/^[0-9]+$/u.test(String(key))) return value[Number(key)];
149
+ return tagged("data-method", { receiver: value, name: String(key) });
150
+ }
151
+ return Object.hasOwn(value, key) ? value[key] : UNKNOWN;
152
+ }
153
+ imported(node, specifier) {
154
+ const ctx = this;
155
+ const { report, edge } = ctx;
156
+
157
+ if (typeof specifier !== "string" || !specifier) {
158
+ report(
159
+ node,
160
+ "unresolved-module",
161
+ "Module specifier must resolve before execution",
162
+ );
163
+ return UNKNOWN;
164
+ }
165
+ edge(node, { kind: "module", specifier });
166
+ return namespace(specifier);
167
+ }
168
+ invokeFunction(node, callable, args) {
169
+ const ctx = this;
170
+ const { bind, expression, statements, report, active } = ctx;
171
+
172
+ if (active.has(callable.node) || active.size >= 32) {
173
+ report(
174
+ node,
175
+ "recursive-invocation",
176
+ "Invocation graph requires a bounded nonrecursive expansion",
177
+ );
178
+ return UNKNOWN;
179
+ }
180
+ active.add(callable.node);
181
+ try {
182
+ const scope = new Map(callable.scope);
183
+ callable.node.params.forEach((parameter, index) =>
184
+ bind(parameter, args[index], scope),
185
+ );
186
+ const value =
187
+ callable.node.body.type === "BlockStatement"
188
+ ? statements(callable.node.body.body, scope).value
189
+ : expression(callable.node.body, scope);
190
+ if (callable.node.async || callable.node.generator) {
191
+ this.report(
192
+ node,
193
+ "unresolved-async-result",
194
+ "Async and generator return behavior requires explicit resolution",
195
+ );
196
+ return UNKNOWN;
197
+ }
198
+ return value;
199
+ } finally {
200
+ active.delete(callable.node);
201
+ }
202
+ }
203
+ subprocess(node, name, args) {
204
+ const ctx = this;
205
+ const { report, edge, cwd } = ctx;
206
+
207
+ const shell = name === "exec" || name === "execSync";
208
+ const argv = Array.isArray(args[1]) ? args[1] : [];
209
+ const suppliedOptions = args[Array.isArray(args[1]) ? 2 : 1];
210
+ const options =
211
+ suppliedOptions?.[TAG] === "function" ? undefined : suppliedOptions;
212
+ if (
213
+ typeof args[0] !== "string" ||
214
+ !argv.every((item) => typeof item === "string") ||
215
+ options === UNKNOWN ||
216
+ (options !== undefined &&
217
+ (!options || typeof options !== "object" || options[TAG])) ||
218
+ (options?.cwd !== undefined && typeof options.cwd !== "string") ||
219
+ options?.env !== undefined ||
220
+ options?.shell
221
+ ) {
222
+ report(
223
+ node,
224
+ "unresolved-process",
225
+ "Process command, arguments, directory and shell mode must resolve",
226
+ );
227
+ return UNKNOWN;
228
+ }
229
+ const directory =
230
+ options?.cwd === undefined
231
+ ? cwd
232
+ : path.posix.isAbsolute(options.cwd)
233
+ ? options.cwd
234
+ : path.posix.join(cwd, options.cwd);
235
+ if (shell) edge(node, { kind: "shell", script: args[0], cwd: directory });
236
+ else
237
+ edge(node, {
238
+ kind: "process",
239
+ executable: name === "fork" ? "node" : args[0],
240
+ argv: name === "fork" ? [args[0], ...argv] : argv,
241
+ cwd: directory,
242
+ });
243
+ return UNKNOWN;
244
+ }
245
+ inspectCallbacks(node, args) {
246
+ const seen = new Set();
247
+ const callbacks = (values) => {
248
+ this.visit();
249
+ for (const value of values) {
250
+ if (value?.[TAG] === "function")
251
+ this.invokeFunction(node, value, [UNKNOWN]);
252
+ else if (
253
+ value &&
254
+ typeof value === "object" &&
255
+ !value[TAG] &&
256
+ !seen.has(value)
257
+ ) {
258
+ seen.add(value);
259
+ callbacks(Object.values(value));
260
+ }
261
+ }
262
+ };
263
+ callbacks(args);
264
+ }
265
+ invokeData(node, callable, args) {
266
+ const { data, report } = this;
267
+ const callbacks = (values) => this.inspectCallbacks(node, values);
268
+ if (
269
+ callable.name === "slice" &&
270
+ args.every((value) => value === undefined || Number.isInteger(value))
271
+ )
272
+ return typeof callable.receiver === "string" ||
273
+ Array.isArray(callable.receiver)
274
+ ? callable.receiver.slice(...args)
275
+ : data();
276
+ if (
277
+ callable.name === "join" &&
278
+ Array.isArray(callable.receiver) &&
279
+ callable.receiver.every((value) => typeof value === "string") &&
280
+ (args[0] === undefined || typeof args[0] === "string")
281
+ )
282
+ return callable.receiver.join(args[0]);
283
+ if (callable.name === "entries" && Array.isArray(callable.receiver))
284
+ return [...callable.receiver.entries()];
285
+ if (callable.name === "push" && Array.isArray(callable.receiver))
286
+ return callable.receiver.push(...args);
287
+ if (
288
+ [
289
+ "pop",
290
+ "shift",
291
+ "unshift",
292
+ "splice",
293
+ "reverse",
294
+ "sort",
295
+ "fill",
296
+ "copyWithin",
297
+ ].includes(callable.name)
298
+ )
299
+ report(
300
+ node,
301
+ "unsupported-mutation",
302
+ `Unsupported data mutation: ${callable.name}`,
303
+ );
304
+ callbacks(args);
305
+ return data();
306
+ }
307
+ invokeMember(node, callable, args) {
308
+ const { data, report, edge, subprocess, cwd } = this;
309
+ const callbacks = (values) => this.inspectCallbacks(node, values);
310
+ const specifier = moduleName(callable.specifier),
311
+ name = callable.name;
312
+ if (specifier === "child_process") {
313
+ callbacks(args);
314
+ if (processCalls.has(name)) return subprocess(node, name, args);
315
+ report(
316
+ node,
317
+ "unresolved-process-api",
318
+ `Unsupported process API: ${name}`,
319
+ );
320
+ } else if (
321
+ ["http", "https", "http2", "net", "tls", "dgram"].includes(specifier)
322
+ )
323
+ edge(node, {
324
+ kind: "network",
325
+ operation: `${specifier}.${name}`,
326
+ arguments: args,
327
+ });
328
+ else if (specifier === "module" && name === "createRequire")
329
+ return tagged("require", {});
330
+ else if (
331
+ specifier === "path" &&
332
+ ["join", "dirname", "basename", "extname", "normalize"].includes(name) &&
333
+ args.every((value) => typeof value === "string")
334
+ )
335
+ return path.posix[name](...args);
336
+ else if (specifier === "process" && name === "cwd") return cwd;
337
+ else if (inertModules.has(specifier) || inertGlobals.has(specifier)) {
338
+ if (
339
+ specifier === "Object" &&
340
+ [
341
+ "defineProperty",
342
+ "defineProperties",
343
+ "setPrototypeOf",
344
+ "create",
345
+ ].includes(name)
346
+ )
347
+ report(
348
+ node,
349
+ "unsupported-object-runtime",
350
+ `Unsupported object execution semantics: ${name}`,
351
+ );
352
+ callbacks(args);
353
+ return data();
354
+ } else if (
355
+ !specifier.startsWith("node:") &&
356
+ !["process", "vm", "module"].includes(specifier)
357
+ )
358
+ edge(node, {
359
+ kind: "module-call",
360
+ specifier: callable.specifier,
361
+ exportName: name,
362
+ arguments: args,
363
+ });
364
+ else
365
+ report(
366
+ node,
367
+ "unresolved-runtime-api",
368
+ `Unsupported executable API: ${specifier}.${name}`,
369
+ );
370
+ return UNKNOWN;
371
+ }
372
+ invoke(node, callable, args) {
373
+ switch (callable?.[TAG]) {
374
+ case "function":
375
+ return this.invokeFunction(node, callable, args);
376
+ case "require":
377
+ return this.imported(node, args[0]);
378
+ case "namespace":
379
+ if (inertGlobals.has(callable.specifier)) {
380
+ this.inspectCallbacks(node, args);
381
+ return this.data();
382
+ }
383
+ break;
384
+ case "data-method":
385
+ return this.invokeData(node, callable, args);
386
+ case "member":
387
+ return this.invokeMember(node, callable, args);
388
+ case "network":
389
+ this.edge(node, {
390
+ kind: "network",
391
+ operation: callable.name,
392
+ arguments: args,
393
+ });
394
+ return UNKNOWN;
395
+ case "dynamic-code":
396
+ this.report(
397
+ node,
398
+ "dynamic-code",
399
+ "Dynamic code cannot establish a closed invocation graph",
400
+ );
401
+ return UNKNOWN;
402
+ }
403
+ this.report(
404
+ node,
405
+ "unresolved-call",
406
+ "Call target cannot be resolved from source",
407
+ );
408
+ return UNKNOWN;
409
+ }
410
+ }
411
+ // No consumer code is executed. Callers must expand every returned edge and
412
+ // reject problems before treating this local analysis as a complete graph.
413
+ export function inspectJavaScriptInvocations({
414
+ source,
415
+ file,
416
+ cwd = ".",
417
+ calledExports = [],
418
+ maxNodes = 50000,
419
+ }) {
420
+ const context = new InvocationContext({ file, cwd, maxNodes });
421
+ try {
422
+ if (!Number.isSafeInteger(maxNodes) || maxNodes < 1 || maxNodes > 1000000)
423
+ throw Error(
424
+ "JavaScript node budget must be an integer from 1 to 1000000",
425
+ );
426
+ if (typeof source !== "string" || source.length > 1024 * 1024)
427
+ throw Error("JavaScript source exceeds the bounded input size");
428
+ const program = parse(source, {
429
+ ecmaVersion: "latest",
430
+ sourceType: file.endsWith(".cjs") ? "commonjs" : "module",
431
+ locations: true,
432
+ });
433
+ context.statements(program.body, context.root);
434
+ for (const request of calledExports)
435
+ context.invoke(
436
+ program,
437
+ context.exports.get(request.name),
438
+ request.arguments || [],
439
+ );
440
+ } catch (error) {
441
+ context.report(null, "analysis-failed", error.message);
442
+ }
443
+ return {
444
+ ok: context.problems.length === 0,
445
+ edges: context.edges,
446
+ problems: context.problems,
447
+ };
448
+ }
449
+
450
+ function resolveSourceModule(files, from, specifier) {
451
+ if (!specifier.startsWith("./") && !specifier.startsWith("../"))
452
+ throw Error(`Dependency requires an exact source resolution: ${specifier}`);
453
+ const target = path.posix.normalize(
454
+ path.posix.join(path.posix.dirname(from), specifier),
455
+ );
456
+ if (target === ".." || target.startsWith("../") || target.includes("\\"))
457
+ throw Error(`Module escapes the supplied source tree: ${specifier}`);
458
+ if (!Object.hasOwn(files, target) || typeof files[target] !== "string")
459
+ throw Error(`Module source is absent: ${target}`);
460
+ if (!/\.[cm]?js$/u.test(target))
461
+ throw Error(`Unsupported executable module: ${target}`);
462
+ return target;
463
+ }
464
+
465
+ // This walks a supplied source snapshot. Package resolution, shell commands,
466
+ // and generated executable files remain explicit boundaries for the caller;
467
+ // an unresolved dependency cannot be silently omitted from a passing graph.
468
+ export function inspectJavaScriptModuleClosure({
469
+ files,
470
+ entry,
471
+ cwd = ".",
472
+ maxInvocations = 256,
473
+ maxNodes = 50000,
474
+ }) {
475
+ const edges = [],
476
+ problems = [],
477
+ sources = new Map(),
478
+ visited = new Set();
479
+ const queue = [{ file: entry, calledExports: [], ancestry: [] }];
480
+ while (queue.length && visited.size < maxInvocations) {
481
+ const request = queue.shift();
482
+ const key = serialize([request.file, request.calledExports]).toString(
483
+ "base64",
484
+ );
485
+ if (request.ancestry.includes(key)) {
486
+ problems.push({
487
+ file: request.file,
488
+ code: "cyclic-module-invocation",
489
+ message: "Module calls require a bounded expansion",
490
+ });
491
+ continue;
492
+ }
493
+ if (visited.has(key)) continue;
494
+ visited.add(key);
495
+ const source = Object.hasOwn(files, request.file)
496
+ ? files[request.file]
497
+ : undefined;
498
+ const report = inspectJavaScriptInvocations({
499
+ source,
500
+ file: request.file,
501
+ cwd,
502
+ calledExports: request.calledExports,
503
+ maxNodes,
504
+ });
505
+ problems.push(...report.problems);
506
+ if (typeof source === "string")
507
+ sources.set(
508
+ request.file,
509
+ createHash("sha256").update(source).digest("hex"),
510
+ );
511
+ for (const edge of report.edges) {
512
+ edges.push(edge);
513
+ if (!["module", "module-call"].includes(edge.kind)) continue;
514
+ if (isBuiltin(edge.specifier)) {
515
+ if (edge.kind === "module-call")
516
+ problems.push({
517
+ ...edge,
518
+ code: "unresolved-builtin",
519
+ message: "Builtin execution was not resolved locally",
520
+ });
521
+ continue;
522
+ }
523
+ try {
524
+ const target = resolveSourceModule(files, request.file, edge.specifier);
525
+ queue.push({
526
+ file: target,
527
+ calledExports:
528
+ edge.kind === "module-call"
529
+ ? [{ name: edge.exportName, arguments: edge.arguments }]
530
+ : [],
531
+ ancestry: [...request.ancestry, key],
532
+ });
533
+ } catch (error) {
534
+ problems.push({
535
+ file: request.file,
536
+ line: edge.line,
537
+ code: "unresolved-module-source",
538
+ message: error.message,
539
+ });
540
+ }
541
+ }
542
+ }
543
+ if (queue.length)
544
+ problems.push({
545
+ file: entry,
546
+ code: "invocation-budget",
547
+ message: "Module closure exceeded its invocation budget",
548
+ });
549
+ return {
550
+ ok: problems.length === 0,
551
+ sources: Object.fromEntries(sources),
552
+ edges,
553
+ problems,
554
+ };
555
+ }
@@ -0,0 +1,84 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { consumerWorkflows } from "./entries.js";
4
+ import { checkWorkflowTaxonomy } from "../../workflow/workflow-taxonomy.mjs";
5
+
6
+ export function validateConsumerWiring(cwd, configPath) {
7
+ const expected = consumerWorkflows("v4", configPath);
8
+ const alpha = consumerWorkflows("v4-alpha", configPath);
9
+ const directory = path.join(cwd, ".github/workflows");
10
+ const files = fs.existsSync(directory)
11
+ ? fs.readdirSync(directory, { withFileTypes: true })
12
+ : [];
13
+ if (files.some((file) => file.isSymbolicLink() || file.isDirectory()))
14
+ throw new Error("Consumer workflows must be regular top-level files");
15
+ const names = files
16
+ .filter((file) => /\.ya?ml$/iu.test(file.name))
17
+ .map((file) => `.github/workflows/${file.name}`)
18
+ .sort();
19
+ if (names.some((file) => !Object.hasOwn(expected, file))) {
20
+ const inventory = checkWorkflowTaxonomy(cwd, {
21
+ integration: false,
22
+ documentation: false,
23
+ });
24
+ if (!inventory.ok)
25
+ throw new Error(
26
+ `Invalid workflow implementation inventory: ${inventory.errors.join("; ")}`,
27
+ );
28
+ }
29
+ const callers = Object.keys(expected);
30
+ const actual = Object.fromEntries(
31
+ callers.map((file) => [
32
+ file,
33
+ fs.readFileSync(path.join(cwd, file), "utf8"),
34
+ ]),
35
+ );
36
+ const matches = (templates) =>
37
+ callers.every((file) => templates[file] === actual[file]);
38
+ if (!matches(expected) && !matches(alpha))
39
+ throw new Error(
40
+ "Consumer workflow bytes differ from the shared normal/recovery template or mix channels",
41
+ );
42
+ return { channel: matches(expected) ? "v4" : "v4-alpha", workflows: callers };
43
+ }
44
+
45
+ export function consumerConfigurationSummary(
46
+ loaded,
47
+ versionFiles,
48
+ derivedVersionMaterial,
49
+ requireLifecycleStages,
50
+ ) {
51
+ const config = loaded.config;
52
+ for (const product of config.products)
53
+ for (const stage of requireLifecycleStages)
54
+ if (!Array.isArray(product[stage]) || !product[stage].length)
55
+ throw new Error(
56
+ `required lifecycle stage missing for product ${product.id}: ${stage}`,
57
+ );
58
+ const lifecycleStages = config.products.flatMap((product) =>
59
+ ["install", "build", "verify"]
60
+ .filter((name) => product[name]?.length)
61
+ .map((name) => ({
62
+ name,
63
+ product: product.id,
64
+ mode: "commands",
65
+ commandCount: product[name].length,
66
+ })),
67
+ );
68
+ return {
69
+ config: { path: loaded.path, filePath: loaded.filePath, schema: 2 },
70
+ products: config.products,
71
+ channels: config.channels,
72
+ review: config.review,
73
+ version: { strategy: config.version.strategy, next: config.version.next },
74
+ versionFiles: versionFiles.map((file) => ({
75
+ path: file.path,
76
+ type: file.type,
77
+ key: file.key,
78
+ })),
79
+ derivedVersionMaterial: derivedVersionMaterial.map((file) => ({
80
+ path: file.path,
81
+ })),
82
+ lifecycleStages,
83
+ };
84
+ }