@kungfu-tech/buildchain 4.1.3 → 4.1.4

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
@@ -1,10 +1,14 @@
1
+ import path from "node:path";
2
+ import { createHash } from "node:crypto";
3
+ import { inspectJavaScriptModuleClosure } from "./javascript.js";
1
4
  import { consumerWorkflows } from "./entries.js";
2
5
  import { compileConsumerPlan, CONFIG_PATH } from "./plan.js";
6
+ import { checkWorkflowTaxonomy } from "../../workflow/workflow-taxonomy.mjs";
3
7
 
4
8
  const INTERNAL_WIRING = [
5
9
  /\b(?:request-json|native-roots-json|used-nonces-json|warrant-result|source-proof|integration-proof)\b/iu,
6
10
  /\bbuildchain\s+(?:dev\s+(?:warrant|proof)|release|promote|recover|publish)\b/iu,
7
- /\b(?:npm\s+publish|gh\s+release\s+(?:create|upload)|gh\s+run\s+download)\b/iu,
11
+ /\b(?:(?:npm|pnpm|yarn)\s+publish|gh\s+release\s+(?:create|upload)|gh\s+run\s+download)\b/iu,
8
12
  /actions\/download-artifact@/u,
9
13
  /\.buildchain\/runtime\/|packages\/core\/(?:publication|release|dev-delivery)\//u,
10
14
  /\b(?:fencingToken|candidateRoot|sourceProofRoot|usedNonces|generationRoot|compareAndSwap)\b/u,
@@ -12,7 +16,9 @@ const INTERNAL_WIRING = [
12
16
  ];
13
17
 
14
18
  // The supplied file map must be the complete tracked consumer tree, including
15
- // package scripts and the source files they invoke. Runtime source is never run.
19
+ // package scripts and invoked sources. Product data is not consumer wiring.
20
+ // Unknown product code stays diagnostic. controlIssues is usable only with the
21
+ // runtime's read-only product jobs; it does not certify a resolved source graph.
16
22
  export function inspectConsumerContract(
17
23
  files,
18
24
  { channel = "v4", configPath = CONFIG_PATH } = {},
@@ -28,18 +34,354 @@ export function inspectConsumerContract(
28
34
  for (const [file, expected] of Object.entries(workflows))
29
35
  if (files[file] !== expected)
30
36
  issues.push(`${file}: must match the generated thin caller`);
31
- for (const [file, source] of Object.entries(files)) {
32
- if (
33
- /^\.github\/workflows\/.*\.ya?ml$/u.test(file) &&
34
- !Object.hasOwn(workflows, file)
37
+ if (
38
+ Object.keys(files).some(
39
+ (file) =>
40
+ /^\.github\/workflows\/.*\.ya?ml$/iu.test(file) &&
41
+ !Object.hasOwn(workflows, file),
35
42
  )
36
- issues.push(`${file}: extra consumer workflow is forbidden`);
37
- if (!/\.(?:toml|json|ya?ml|[cm]?js|ts|py|sh|ps1|rb)$/u.test(file)) continue;
43
+ ) {
44
+ try {
45
+ const inventory = checkWorkflowTaxonomy("", {
46
+ files,
47
+ integration: false,
48
+ documentation: false,
49
+ });
50
+ issues.push(
51
+ ...inventory.errors.map(
52
+ (error) => `extra consumer workflow inventory: ${error}`,
53
+ ),
54
+ );
55
+ } catch (error) {
56
+ issues.push(`extra consumer workflow inventory: ${error.message}`);
57
+ }
58
+ }
59
+ for (const file of [configPath, ...Object.keys(workflows)]) {
60
+ const source = files[file];
38
61
  for (const pattern of INTERNAL_WIRING)
39
62
  if (pattern.test(String(source)))
40
63
  issues.push(
41
64
  `${file}: consumer owns internal orchestration (${pattern.source})`,
42
65
  );
43
66
  }
44
- return { ok: issues.length === 0, issues, plan };
67
+ const controlIssues = [...issues];
68
+ const commandClosures = (plan?.products || []).map((product) => ({
69
+ product: product.id,
70
+ ...inspectConsumerCommandClosure({
71
+ files,
72
+ cwd: product.directory || ".",
73
+ commands: ["install", "build", "verify"].flatMap(
74
+ (stage) => product[stage] || [],
75
+ ),
76
+ }),
77
+ }));
78
+ for (const closure of commandClosures)
79
+ for (const problem of closure.problems) {
80
+ const issue = `${problem.file || closure.product}: ${problem.code}: ${problem.message || "consumer owns internal orchestration"}`;
81
+ issues.push(issue);
82
+ if (problem.code === "internal-orchestration") controlIssues.push(issue);
83
+ }
84
+ return {
85
+ ok: issues.length === 0,
86
+ issues,
87
+ controlIssues,
88
+ plan,
89
+ commandClosures,
90
+ };
91
+ }
92
+
93
+ // A bounded source graph, not an approval to execute tools. Every non-source
94
+ // executable remains a boundary until its actual implementation is resolved.
95
+ export function inspectConsumerCommandClosure({
96
+ files,
97
+ commands,
98
+ cwd = ".",
99
+ maxCommands = 256,
100
+ }) {
101
+ if (
102
+ !Array.isArray(commands) ||
103
+ !Number.isSafeInteger(maxCommands) ||
104
+ maxCommands < 1 ||
105
+ maxCommands > 10000
106
+ )
107
+ throw Error(
108
+ "Command traversal requires an array and a budget from 1 to 10000",
109
+ );
110
+ cwd = consumerPath(".", cwd);
111
+ const graph = { edges: [], sources: {}, problems: [], boundaries: [] };
112
+ const pending = commands.map((script) => ({
113
+ kind: "shell",
114
+ script,
115
+ cwd,
116
+ file: "<product>",
117
+ ancestry: [],
118
+ }));
119
+ const seen = new Set();
120
+ while (pending.length && seen.size < maxCommands) {
121
+ const request = pending.shift();
122
+ const key = JSON.stringify([
123
+ request.kind,
124
+ request.script,
125
+ request.executable,
126
+ request.argv,
127
+ request.cwd,
128
+ ]);
129
+ if (request.ancestry.includes(key)) {
130
+ graph.problems.push({ file: request.file, code: "command-cycle" });
131
+ continue;
132
+ }
133
+ if (seen.has(key)) continue;
134
+ seen.add(key);
135
+ const enqueue = (entry) =>
136
+ pending.push({ ...entry, ancestry: [...request.ancestry, key] });
137
+ try {
138
+ expandConsumerCommand(files, request, graph, enqueue);
139
+ } catch (error) {
140
+ graph.problems.push({
141
+ file: request.file,
142
+ code: "unresolved-command",
143
+ message: error.message,
144
+ });
145
+ }
146
+ }
147
+ if (pending.length)
148
+ graph.problems.push({
149
+ code: "command-budget",
150
+ message: "Command graph exceeded its bounded expansion",
151
+ });
152
+ const sourceResolved = graph.problems.length === 0;
153
+ return {
154
+ ...graph,
155
+ sourceResolved,
156
+ commandEdgesResolved: sourceResolved && graph.boundaries.length === 0,
157
+ };
158
+ }
159
+
160
+ function escapedShellCharacter(source, index, quote) {
161
+ if (index >= source.length) throw Error("Incomplete shell escape");
162
+ const char = source[index];
163
+ if (char === "\n") return "";
164
+ return quote === '"' && !/[\\$`"]/.test(char) ? `\\${char}` : char;
165
+ }
166
+
167
+ function commandWords(source) {
168
+ if (typeof source !== "string" || source.length > 1024 * 1024)
169
+ throw Error("Expected bounded shell source");
170
+ const commands = [];
171
+ let words = [],
172
+ word = "",
173
+ quote = "",
174
+ active = false;
175
+ const flushWord = () => {
176
+ if (active) words.push(word);
177
+ word = "";
178
+ active = false;
179
+ };
180
+ const flushCommand = () => {
181
+ flushWord();
182
+ if (words.length) commands.push(words);
183
+ words = [];
184
+ };
185
+ for (let index = 0; index < source.length; index++) {
186
+ const char = source[index];
187
+ if (char === "\\" && quote !== "'") {
188
+ const value = escapedShellCharacter(source, ++index, quote);
189
+ word += value;
190
+ active ||= value.length > 0;
191
+ } else if (quote) {
192
+ if (char === quote) quote = "";
193
+ else {
194
+ if (quote === '"' && /[$`]/u.test(char))
195
+ throw Error("Shell expansion requires resolved execution context");
196
+ word += char;
197
+ }
198
+ } else if (char === '"' || char === "'") {
199
+ quote = char;
200
+ active = true;
201
+ } else if (/[$`(){}<>*?\[\]]/u.test(char))
202
+ throw Error(
203
+ "Shell expansion or control syntax requires explicit resolution",
204
+ );
205
+ else if (char === "#" && !active) {
206
+ while (index < source.length && source[index] !== "\n") index++;
207
+ flushCommand();
208
+ } else if (char === ";" || char === "\n") flushCommand();
209
+ else if (char === "&" || char === "|") {
210
+ if (source[index + 1] !== char)
211
+ throw Error(
212
+ "Pipelines and background execution require explicit resolution",
213
+ );
214
+ flushCommand();
215
+ index++;
216
+ } else if (/\s/u.test(char)) flushWord();
217
+ else {
218
+ word += char;
219
+ active = true;
220
+ }
221
+ }
222
+ if (quote) throw Error("Unclosed shell quote");
223
+ flushCommand();
224
+ return commands;
225
+ }
226
+
227
+ function consumerPath(cwd, file) {
228
+ if (
229
+ typeof file !== "string" ||
230
+ file.includes("\\") ||
231
+ path.posix.isAbsolute(file)
232
+ )
233
+ throw Error(
234
+ "Executable paths must resolve inside the supplied consumer tree",
235
+ );
236
+ const resolved = path.posix.normalize(path.posix.join(cwd, file));
237
+ if (
238
+ path.posix.isAbsolute(resolved) ||
239
+ resolved === ".." ||
240
+ resolved.startsWith("../")
241
+ )
242
+ throw Error("Executable path escapes consumer tree");
243
+ return resolved;
244
+ }
245
+
246
+ function sourceBytes(files, file, graph) {
247
+ if (!Object.hasOwn(files, file) || typeof files[file] !== "string")
248
+ throw Error(`Executable source is absent: ${file}`);
249
+ graph.sources[file] = createHash("sha256").update(files[file]).digest("hex");
250
+ return files[file];
251
+ }
252
+
253
+ function expandPackageCommand(files, request, graph, enqueue) {
254
+ const file = consumerPath(request.cwd, "package.json");
255
+ const manifest = JSON.parse(sourceBytes(files, file, graph));
256
+ const [operation, name, ...args] = request.argv;
257
+ let keys = [`pre${name}`, name, `post${name}`];
258
+ if (operation !== "run") {
259
+ graph.boundaries.push({ ...request, code: "package-manager-operation" });
260
+ if (
261
+ request.executable !== "npm" ||
262
+ request.argv.length !== 1 ||
263
+ !["install", "ci"].includes(operation)
264
+ )
265
+ return;
266
+ keys =
267
+ "preinstall install postinstall prepublish preprepare prepare postprepare dependencies".split(
268
+ " ",
269
+ );
270
+ } else {
271
+ if (!name || args.length)
272
+ throw Error("Package script arguments require explicit resolution");
273
+ if (typeof manifest.scripts?.[name] !== "string")
274
+ throw Error(`Package script is absent: ${name}`);
275
+ }
276
+ // Include lifecycle hooks conservatively even for a manager that disables them.
277
+ for (const key of keys) {
278
+ if (!Object.hasOwn(manifest.scripts || {}, key)) continue;
279
+ const script = manifest.scripts[key];
280
+ if (typeof script !== "string")
281
+ throw Error(`Package script must be text: ${key}`);
282
+ enqueue({ kind: "shell", script, cwd: request.cwd, file });
283
+ }
284
+ }
285
+
286
+ function expandJavaScriptCommand(files, request, graph, enqueue) {
287
+ if (request.argv.length !== 1 || request.argv[0].startsWith("-"))
288
+ throw Error(
289
+ "Node options, test runners and script arguments require explicit resolution",
290
+ );
291
+ const file = consumerPath(request.cwd, request.argv[0]);
292
+ sourceBytes(files, file, graph);
293
+ const report = inspectJavaScriptModuleClosure({
294
+ files,
295
+ entry: file,
296
+ cwd: request.cwd,
297
+ });
298
+ Object.assign(graph.sources, report.sources);
299
+ graph.problems.push(...report.problems);
300
+ for (const edge of report.edges) {
301
+ if (["process", "shell"].includes(edge.kind)) enqueue(edge);
302
+ else {
303
+ graph.edges.push(edge);
304
+ if (edge.kind === "network") graph.boundaries.push(edge);
305
+ }
306
+ }
307
+ }
308
+
309
+ function expandConsumerCommand(files, request, graph, enqueue) {
310
+ if (request.kind === "shell") {
311
+ const cwd = request.cwd;
312
+ for (const [executable, ...argv] of commandWords(request.script)) {
313
+ if (executable === "cd")
314
+ throw Error(
315
+ "Shell directory changes require conditional execution resolution",
316
+ );
317
+ enqueue({ kind: "process", executable, argv, cwd, file: request.file });
318
+ }
319
+ return;
320
+ }
321
+ const { executable, argv, cwd, file } = request;
322
+ graph.edges.push({ kind: "process", executable, argv, cwd, file });
323
+ // Tool operands are data; inspect command positions and protocol switches.
324
+ const invocation = [
325
+ executable,
326
+ ...argv.filter(
327
+ (arg, index) =>
328
+ arg.startsWith("--") ||
329
+ (executable === "node" && index === 0) ||
330
+ (/^(?:npm|pnpm|yarn|gh|buildchain)$/u.test(executable) && index < 3),
331
+ ),
332
+ ].join(" ");
333
+ if (INTERNAL_WIRING.some((pattern) => pattern.test(invocation))) {
334
+ graph.problems.push({
335
+ file,
336
+ code: "internal-orchestration",
337
+ executable,
338
+ argv,
339
+ });
340
+ return;
341
+ }
342
+ if (
343
+ executable === "corepack" &&
344
+ /^(?:npm|pnpm|yarn)(?:@[\d.]+)?$/u.test(argv[0] || "")
345
+ ) {
346
+ enqueue({
347
+ ...request,
348
+ executable: argv[0].split("@")[0],
349
+ argv: argv.slice(1),
350
+ });
351
+ } else if (["npm", "pnpm", "yarn"].includes(executable))
352
+ expandPackageCommand(files, request, graph, enqueue);
353
+ else if (executable === "node")
354
+ expandJavaScriptCommand(files, request, graph, enqueue);
355
+ else if (["sh", "bash"].includes(executable)) {
356
+ if (argv.length === 2 && ["-c", "-lc"].includes(argv[0]))
357
+ enqueue({ kind: "shell", script: argv[1], cwd, file });
358
+ else if (argv.length === 1 && !argv[0].startsWith("-")) {
359
+ const target = consumerPath(cwd, argv[0]);
360
+ enqueue({
361
+ kind: "shell",
362
+ script: sourceBytes(files, target, graph),
363
+ cwd,
364
+ file: target,
365
+ });
366
+ } else
367
+ throw Error(
368
+ "Shell options and script arguments require explicit resolution",
369
+ );
370
+ } else if (executable.startsWith("./") || executable.startsWith("../")) {
371
+ const target = consumerPath(cwd, executable);
372
+ const source = sourceBytes(files, target, graph);
373
+ if (/^#!\/usr\/bin\/env node(?:\r?\n|$)/u.test(source))
374
+ enqueue({ ...request, executable: "node", argv: [executable, ...argv] });
375
+ else if (/^#!\/(?:usr\/bin\/env |bin\/)(?:ba)?sh(?:\r?\n|$)/u.test(source))
376
+ enqueue({ ...request, executable: "bash", argv: [executable, ...argv] });
377
+ else throw Error(`Executable interpreter is unresolved: ${target}`);
378
+ } else
379
+ graph.boundaries.push({
380
+ kind: "process",
381
+ executable,
382
+ argv,
383
+ cwd,
384
+ file,
385
+ code: "external-executable",
386
+ });
45
387
  }