project-tiny-context-harness 0.8.13 → 0.8.17

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 (103) hide show
  1. package/README.md +53 -28
  2. package/assets/README.md +52 -25
  3. package/assets/README.zh-CN.md +55 -26
  4. package/assets/agents/AGENTS_CORE.md +2 -2
  5. package/assets/agents/long-task-implementation.toml +1 -1
  6. package/assets/context_templates/screen-contract.md +16 -0
  7. package/assets/skills/context_development_engineer/SKILL.md +16 -30
  8. package/assets/skills/context_development_engineer/references/engineering-design-reasoning.md +93 -0
  9. package/assets/skills/context_surface_contract/SKILL.md +13 -2
  10. package/assets/skills/context_uiux_design/SKILL.md +7 -3
  11. package/assets/skills/context_uiux_design/references/task-uiux-analysis.md +82 -0
  12. package/assets/skills/design-resource-authoring/SKILL.md +14 -8
  13. package/assets/skills/design-resource-authoring/references/downstream-handoff.md +18 -6
  14. package/assets/skills/design-resource-authoring/references/formal-selected-web-app-handoff.md +26 -8
  15. package/assets/skills/design-resource-authoring/references/implementation-feasibility.md +111 -0
  16. package/assets/skills/design-resource-authoring/references/open-design-provider.md +45 -7
  17. package/assets/skills/design-resource-authoring/references/recovery-and-writeback.md +7 -1
  18. package/assets/skills/design-resource-authoring/references/resource-selection.md +115 -3
  19. package/assets/skills/long-task-workflow/SKILL.md +9 -7
  20. package/assets/skills/long-task-workflow/agents/openai.yaml +1 -1
  21. package/assets/skills/long-task-workflow/references/authority-lifecycle.md +1 -1
  22. package/dist/commands/design-resource.js +9 -2
  23. package/dist/commands/long-task-revision.js +4 -4
  24. package/dist/index.d.ts +1 -1
  25. package/dist/lib/design-resource-handoff-bundle.js +5 -0
  26. package/dist/lib/design-resource-handoff-input-types.d.ts +1 -1
  27. package/dist/lib/design-resource-handoff-manifest-projection.js +1 -0
  28. package/dist/lib/design-resource-handoff-set-integrity.js +1 -0
  29. package/dist/lib/design-resource-handoff-shape.js +6 -2
  30. package/dist/lib/design-resource-handoff-types.d.ts +5 -0
  31. package/dist/lib/design-resource-handoff-validation.js +12 -0
  32. package/dist/lib/design-resource-implementation-feasibility-model.d.ts +35 -0
  33. package/dist/lib/design-resource-implementation-feasibility-model.js +62 -0
  34. package/dist/lib/design-resource-implementation-feasibility-shape-sections.d.ts +43 -0
  35. package/dist/lib/design-resource-implementation-feasibility-shape-sections.js +142 -0
  36. package/dist/lib/design-resource-implementation-feasibility-shape.d.ts +4 -0
  37. package/dist/lib/design-resource-implementation-feasibility-shape.js +154 -0
  38. package/dist/lib/design-resource-implementation-feasibility-source-decision-projection.d.ts +24 -0
  39. package/dist/lib/design-resource-implementation-feasibility-source-decision-projection.js +106 -0
  40. package/dist/lib/design-resource-implementation-feasibility-source-decision.d.ts +21 -0
  41. package/dist/lib/design-resource-implementation-feasibility-source-decision.js +75 -0
  42. package/dist/lib/design-resource-implementation-feasibility-types.d.ts +135 -0
  43. package/dist/lib/design-resource-implementation-feasibility-types.js +37 -0
  44. package/dist/lib/design-resource-implementation-feasibility-validation-cells.d.ts +5 -0
  45. package/dist/lib/design-resource-implementation-feasibility-validation-cells.js +125 -0
  46. package/dist/lib/design-resource-implementation-feasibility-validation-document.d.ts +4 -0
  47. package/dist/lib/design-resource-implementation-feasibility-validation-document.js +167 -0
  48. package/dist/lib/design-resource-implementation-feasibility-validation-facts.d.ts +3 -0
  49. package/dist/lib/design-resource-implementation-feasibility-validation-facts.js +76 -0
  50. package/dist/lib/design-resource-implementation-feasibility-validation-realizations.d.ts +8 -0
  51. package/dist/lib/design-resource-implementation-feasibility-validation-realizations.js +76 -0
  52. package/dist/lib/design-resource-implementation-feasibility-validation-support.d.ts +10 -0
  53. package/dist/lib/design-resource-implementation-feasibility-validation-support.js +75 -0
  54. package/dist/lib/design-resource-implementation-feasibility-validation.d.ts +3 -0
  55. package/dist/lib/design-resource-implementation-feasibility-validation.js +138 -0
  56. package/dist/lib/design-resource-symbolic-fact-shape.js +3 -1
  57. package/dist/lib/design-resource-symbolic-fact-types.d.ts +5 -0
  58. package/dist/lib/design-resource-symbolic-fact-validation.js +13 -0
  59. package/dist/lib/long-task-activation-validation.js +77 -7
  60. package/dist/lib/long-task-authority-revision-brief.js +3 -1
  61. package/dist/lib/long-task-authority-revision-summary.js +6 -2
  62. package/dist/lib/long-task-authority-revision-types.d.ts +2 -0
  63. package/dist/lib/long-task-check-runner.js +3 -296
  64. package/dist/lib/long-task-command-process.d.ts +9 -0
  65. package/dist/lib/long-task-command-process.js +175 -0
  66. package/dist/lib/long-task-delivery-validation.d.ts +5 -2
  67. package/dist/lib/long-task-delivery-validation.js +4 -4
  68. package/dist/lib/long-task-design-feasibility-binding-owners.d.ts +6 -0
  69. package/dist/lib/long-task-design-feasibility-binding-owners.js +52 -0
  70. package/dist/lib/long-task-design-feasibility-binding.d.ts +13 -0
  71. package/dist/lib/long-task-design-feasibility-binding.js +114 -0
  72. package/dist/lib/long-task-design-feasibility-source-closure.d.ts +11 -0
  73. package/dist/lib/long-task-design-feasibility-source-closure.js +78 -0
  74. package/dist/lib/long-task-design-resource-handoff.d.ts +3 -3
  75. package/dist/lib/long-task-design-resource-handoff.js +39 -3
  76. package/dist/lib/long-task-design-resource-method-binding.js +16 -4
  77. package/dist/lib/long-task-freshness.js +1 -1
  78. package/dist/lib/long-task-git.d.ts +26 -0
  79. package/dist/lib/long-task-git.js +130 -0
  80. package/dist/lib/long-task-paths.js +1 -1
  81. package/dist/lib/long-task-process-runtime-closure.js +74 -42
  82. package/dist/lib/long-task-process-table.d.ts +12 -0
  83. package/dist/lib/long-task-process-table.js +119 -0
  84. package/dist/lib/long-task-process-tree.d.ts +9 -0
  85. package/dist/lib/long-task-process-tree.js +138 -0
  86. package/dist/lib/long-task-protected-files.d.ts +1 -1
  87. package/dist/lib/long-task-protected-files.js +1 -1
  88. package/dist/lib/long-task-ui-surface-policy.d.ts +2 -1
  89. package/dist/lib/long-task-ui-surface-policy.js +7 -4
  90. package/dist/lib/long-task-ui-surface-validation.d.ts +3 -1
  91. package/dist/lib/long-task-ui-surface-validation.js +2 -2
  92. package/dist/lib/long-task-verifier-authority.js +2 -0
  93. package/dist/lib/long-task-workspace-manifest.d.ts +8 -0
  94. package/dist/lib/long-task-workspace-manifest.js +173 -0
  95. package/dist/lib/long-task-workspace-snapshot.d.ts +8 -0
  96. package/dist/lib/long-task-workspace-snapshot.js +176 -0
  97. package/dist/lib/long-task-workspace.d.ts +4 -25
  98. package/dist/lib/long-task-workspace.js +3 -432
  99. package/dist/lib/repository-path-safety.d.ts +2 -0
  100. package/dist/lib/repository-path-safety.js +36 -0
  101. package/dist/long-task-hook.js +12 -3
  102. package/dist/public-types.d.ts +1 -0
  103. package/package.json +1 -1
@@ -1,17 +1,13 @@
1
- import { execFile, spawn } from "node:child_process";
2
1
  import { randomBytes } from "node:crypto";
3
2
  import { access, realpath } from "node:fs/promises";
4
3
  import net from "node:net";
5
4
  import path from "node:path";
6
- import { promisify } from "node:util";
7
5
  import { decodeCheckEvidence, invalidEvidence, } from "./long-task-check-evidence-decoder.js";
6
+ import { spawnCommandOnce } from "./long-task-command-process.js";
8
7
  import { declaredEnvironmentValues, outputContainsDeclaredEnvironmentValue, runnerEnvironment, } from "./long-task-runner-environment.js";
9
8
  import { decodeProductObservationEnvelope } from "./long-task-process-observation.js";
10
9
  import { resolveInsideRepository } from "./long-task-workspace.js";
11
10
  import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
12
- const OUTPUT_LIMIT = 2 * 1024 * 1024;
13
- const PROCESS_TREE_GRACE_MS = 1_000;
14
- const execFileAsync = promisify(execFile);
15
11
  export async function executeCheckRunner(check, snapshotRoot, context) {
16
12
  const started = Date.now();
17
13
  let processAuthorities;
@@ -112,7 +108,7 @@ async function runOnce(check, snapshotRoot, processAuthorities, snapshotSha256,
112
108
  : runner.executable;
113
109
  const argv = [...runner.executable_argv_prefix, ...runner.argv];
114
110
  if (!processAuthorities.length) {
115
- const execution = await spawnOnce(executable, argv, cwd, runner.timeout_ms, runnerEnvironment(check.environment_requirements), false);
111
+ const execution = await spawnCommandOnce(executable, argv, cwd, runner.timeout_ms, runnerEnvironment(check.environment_requirements), false);
116
112
  return {
117
113
  ...execution,
118
114
  process_observation: null,
@@ -120,7 +116,7 @@ async function runOnce(check, snapshotRoot, processAuthorities, snapshotSha256,
120
116
  };
121
117
  }
122
118
  const executionNonce = randomBytes(32).toString("hex");
123
- const execution = await spawnOnce(executable, argv, cwd, runner.timeout_ms, runnerEnvironment(check.environment_requirements, "product_observation"), true);
119
+ const execution = await spawnCommandOnce(executable, argv, cwd, runner.timeout_ms, runnerEnvironment(check.environment_requirements, "product_observation"), true);
124
120
  const processObservation = decodeProductObservationEnvelope({
125
121
  bytes: execution.stdout,
126
122
  authorities: processAuthorities,
@@ -160,295 +156,6 @@ async function runOnce(check, snapshotRoot, processAuthorities, snapshotSha256,
160
156
  },
161
157
  };
162
158
  }
163
- async function spawnOnce(executable, argv, cwd, timeoutMs, environment, containProcessTree) {
164
- return new Promise((resolve, reject) => {
165
- const startedAt = new Date().toISOString();
166
- const child = spawn(executable, argv, {
167
- cwd,
168
- shell: false,
169
- windowsHide: true,
170
- env: environment,
171
- detached: containProcessTree && process.platform !== "win32",
172
- });
173
- const pid = child.pid ?? -1;
174
- let stopTreeMonitor = false;
175
- let treeMonitorError = null;
176
- const observedDescendants = new Set();
177
- const treeMonitor = containProcessTree && pid > 0
178
- ? observeDescendantProcesses(pid, observedDescendants, () => stopTreeMonitor).catch((error) => {
179
- treeMonitorError = error;
180
- })
181
- : Promise.resolve();
182
- const stdout = [];
183
- const stderr = [];
184
- let size = 0;
185
- let settled = false;
186
- let terminationReason = null;
187
- let terminationTask = null;
188
- let forceTimer = null;
189
- let abandonTimer = null;
190
- const capture = (target) => (chunk) => {
191
- size += chunk.length;
192
- if (size > OUTPUT_LIMIT) {
193
- terminate("command_output_limit_exceeded");
194
- return;
195
- }
196
- target.push(Buffer.from(chunk));
197
- };
198
- child.stdout.on("data", capture(stdout));
199
- child.stderr.on("data", capture(stderr));
200
- child.on("error", (error) => {
201
- if (!settled) {
202
- settled = true;
203
- stopTreeMonitor = true;
204
- clearTimeout(timer);
205
- if (forceTimer)
206
- clearTimeout(forceTimer);
207
- if (abandonTimer)
208
- clearTimeout(abandonTimer);
209
- const spawnError = new Error(`command_spawn_error:${message(error)}`);
210
- if (containProcessTree && pid > 0)
211
- void terminateProcessTree(pid, true, [...observedDescendants])
212
- .catch(() => undefined)
213
- .finally(() => reject(spawnError));
214
- else
215
- reject(spawnError);
216
- }
217
- });
218
- const timer = setTimeout(() => {
219
- terminate("command_timeout");
220
- }, timeoutMs);
221
- if (pid < 0)
222
- terminate("command_spawn_pid_unavailable");
223
- child.on("close", async (code) => {
224
- stopTreeMonitor = true;
225
- clearTimeout(timer);
226
- if (forceTimer)
227
- clearTimeout(forceTimer);
228
- if (abandonTimer)
229
- clearTimeout(abandonTimer);
230
- if (settled)
231
- return;
232
- settled = true;
233
- if (terminationReason) {
234
- try {
235
- await terminationTask;
236
- await treeMonitor;
237
- if (containProcessTree)
238
- await forceProcessTreeQuiescence(pid, observedDescendants);
239
- }
240
- catch {
241
- // The original bounded termination reason remains the public error;
242
- // cleanup is best-effort after the hard process-tree kill attempt.
243
- }
244
- reject(terminationReason);
245
- return;
246
- }
247
- if (containProcessTree) {
248
- try {
249
- await treeMonitor;
250
- if (treeMonitorError)
251
- throw treeMonitorError;
252
- await assertProcessTreeQuiescent(pid, observedDescendants);
253
- }
254
- catch (error) {
255
- await terminateProcessTree(pid, true, [...observedDescendants]).catch(() => undefined);
256
- reject(error);
257
- return;
258
- }
259
- }
260
- resolve({
261
- exit_code: code ?? -1,
262
- stdout: Buffer.concat(stdout),
263
- stderr: Buffer.concat(stderr),
264
- pid,
265
- started_at: startedAt,
266
- completed_at: new Date().toISOString(),
267
- });
268
- });
269
- function terminate(reason) {
270
- if (settled || terminationReason)
271
- return;
272
- terminationReason = new Error(reason);
273
- stopTreeMonitor = true;
274
- if (containProcessTree)
275
- terminationTask = terminateProcessTree(pid, false).catch((error) => {
276
- treeMonitorError ??= error;
277
- });
278
- else {
279
- child.kill();
280
- terminationTask = Promise.resolve();
281
- }
282
- forceTimer = setTimeout(() => {
283
- if (containProcessTree)
284
- terminationTask = terminateProcessTree(pid, true, [
285
- ...observedDescendants,
286
- ]).catch((error) => {
287
- treeMonitorError ??= error;
288
- });
289
- else
290
- child.kill("SIGKILL");
291
- }, PROCESS_TREE_GRACE_MS);
292
- forceTimer.unref();
293
- abandonTimer = setTimeout(() => {
294
- if (settled)
295
- return;
296
- settled = true;
297
- child.stdout.destroy();
298
- child.stderr.destroy();
299
- const cleanup = containProcessTree
300
- ? forceProcessTreeQuiescence(pid, observedDescendants).catch(() => undefined)
301
- : Promise.resolve();
302
- void cleanup.finally(() => reject(terminationReason));
303
- }, PROCESS_TREE_GRACE_MS * 2);
304
- abandonTimer.unref();
305
- }
306
- });
307
- }
308
- async function forceProcessTreeQuiescence(rootPid, observed) {
309
- await terminateProcessTree(rootPid, true, [...observed]);
310
- const deadline = Date.now() + PROCESS_TREE_GRACE_MS;
311
- while (Date.now() < deadline) {
312
- const liveObserved = [rootPid, ...observed].some(processIdExists);
313
- const groupAlive = process.platform !== "win32" && processGroupExists(rootPid);
314
- if (!liveObserved && !groupAlive)
315
- return;
316
- await new Promise((resolve) => setTimeout(resolve, process.platform === "win32" ? 100 : 25));
317
- }
318
- throw new Error("process_observer_descendant_process_alive");
319
- }
320
- async function observeDescendantProcesses(rootPid, observed, stopped) {
321
- do {
322
- for (const pid of await descendantProcessIds(rootPid))
323
- observed.add(pid);
324
- if (stopped())
325
- return;
326
- await new Promise((resolve) => setTimeout(resolve, process.platform === "win32" ? 100 : 25));
327
- } while (!stopped());
328
- }
329
- async function assertProcessTreeQuiescent(rootPid, observed) {
330
- const finalDescendants = await descendantProcessIds(rootPid);
331
- const descendants = [...new Set([...observed, ...finalDescendants])].filter(processIdExists);
332
- const processGroupAlive = process.platform !== "win32" && processGroupExists(rootPid);
333
- if (!descendants.length && !processGroupAlive)
334
- return;
335
- await terminateProcessTree(rootPid, true, descendants);
336
- throw new Error("process_observer_descendant_process_alive");
337
- }
338
- function processIdExists(pid) {
339
- try {
340
- process.kill(pid, 0);
341
- return true;
342
- }
343
- catch (error) {
344
- if (nodeErrorCode(error) === "ESRCH")
345
- return false;
346
- throw error;
347
- }
348
- }
349
- async function terminateProcessTree(rootPid, force, knownDescendants) {
350
- if (!Number.isSafeInteger(rootPid) || rootPid <= 0)
351
- return;
352
- if (process.platform === "win32") {
353
- const targets = [
354
- rootPid,
355
- ...[...new Set(knownDescendants ?? [])]
356
- .filter((pid) => pid !== rootPid)
357
- .reverse(),
358
- ];
359
- for (const pid of targets)
360
- await execFileAsync("taskkill.exe", ["/PID", String(pid), "/T", "/F"], {
361
- windowsHide: true,
362
- timeout: PROCESS_TREE_GRACE_MS,
363
- }).catch(() => undefined);
364
- return;
365
- }
366
- const signal = force ? "SIGKILL" : "SIGTERM";
367
- try {
368
- process.kill(-rootPid, signal);
369
- }
370
- catch (error) {
371
- if (nodeErrorCode(error) !== "ESRCH")
372
- throw error;
373
- }
374
- const descendants = knownDescendants ?? (await descendantProcessIds(rootPid));
375
- for (const pid of [...descendants].reverse())
376
- try {
377
- process.kill(pid, signal);
378
- }
379
- catch (error) {
380
- if (nodeErrorCode(error) !== "ESRCH")
381
- throw error;
382
- }
383
- }
384
- async function descendantProcessIds(rootPid) {
385
- let stdout;
386
- try {
387
- const result = process.platform === "win32"
388
- ? await execFileAsync("powershell.exe", [
389
- "-NoLogo",
390
- "-NoProfile",
391
- "-NonInteractive",
392
- "-Command",
393
- 'Get-CimInstance Win32_Process | ForEach-Object { "$($_.ProcessId) $($_.ParentProcessId)" }',
394
- ], {
395
- encoding: "utf8",
396
- windowsHide: true,
397
- timeout: 5_000,
398
- maxBuffer: OUTPUT_LIMIT,
399
- })
400
- : await execFileAsync("ps", ["-A", "-o", "pid=,ppid="], {
401
- encoding: "utf8",
402
- timeout: 5_000,
403
- maxBuffer: OUTPUT_LIMIT,
404
- });
405
- stdout = result.stdout;
406
- }
407
- catch (error) {
408
- throw new Error(`process_observer_process_tree_inspection_unavailable:${message(error)}`);
409
- }
410
- const children = new Map();
411
- for (const line of stdout.split(/\r?\n/u)) {
412
- const [pidText, parentText] = line.trim().split(/\s+/u);
413
- const pid = Number.parseInt(pidText ?? "", 10);
414
- const parent = Number.parseInt(parentText ?? "", 10);
415
- if (!Number.isSafeInteger(pid) || !Number.isSafeInteger(parent))
416
- continue;
417
- const row = children.get(parent);
418
- if (row)
419
- row.push(pid);
420
- else
421
- children.set(parent, [pid]);
422
- }
423
- const result = [];
424
- const pending = [...(children.get(rootPid) ?? [])];
425
- const seen = new Set();
426
- while (pending.length) {
427
- const pid = pending.shift();
428
- if (seen.has(pid))
429
- continue;
430
- seen.add(pid);
431
- result.push(pid);
432
- pending.push(...(children.get(pid) ?? []));
433
- }
434
- return result;
435
- }
436
- function processGroupExists(rootPid) {
437
- try {
438
- process.kill(-rootPid, 0);
439
- return true;
440
- }
441
- catch (error) {
442
- if (nodeErrorCode(error) === "ESRCH")
443
- return false;
444
- throw error;
445
- }
446
- }
447
- function nodeErrorCode(error) {
448
- return error && typeof error === "object" && "code" in error
449
- ? String(error.code)
450
- : null;
451
- }
452
159
  function validateProcessObserverActivation(check, snapshotRoot, authorities, context) {
453
160
  if (!authorities.length)
454
161
  return null;
@@ -0,0 +1,9 @@
1
+ export interface SpawnedCommandExecution {
2
+ exit_code: number;
3
+ stdout: Buffer;
4
+ stderr: Buffer;
5
+ pid: number;
6
+ started_at: string;
7
+ completed_at: string;
8
+ }
9
+ export declare function spawnCommandOnce(executable: string, argv: string[], cwd: string, timeoutMs: number, environment: NodeJS.ProcessEnv, containProcessTree: boolean): Promise<SpawnedCommandExecution>;
@@ -0,0 +1,175 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createProcessTreeObserver, } from "./long-task-process-tree.js";
3
+ const OUTPUT_LIMIT = 2 * 1024 * 1024;
4
+ const PROCESS_TREE_GRACE_MS = 1_000;
5
+ export function spawnCommandOnce(executable, argv, cwd, timeoutMs, environment, containProcessTree) {
6
+ return new Promise((resolve, reject) => {
7
+ const child = spawn(executable, argv, {
8
+ cwd,
9
+ shell: false,
10
+ windowsHide: true,
11
+ env: environment,
12
+ detached: containProcessTree && process.platform !== "win32",
13
+ });
14
+ const pid = child.pid ?? -1;
15
+ const processTree = containProcessTree && pid > 0 ? createProcessTreeObserver(pid) : null;
16
+ const state = {
17
+ child,
18
+ pid,
19
+ processTree,
20
+ treeMonitor: Promise.resolve(),
21
+ treeMonitorError: null,
22
+ stdout: [],
23
+ stderr: [],
24
+ size: 0,
25
+ settled: false,
26
+ terminationReason: null,
27
+ terminationTask: null,
28
+ timer: null,
29
+ forceTimer: null,
30
+ abandonTimer: null,
31
+ startedAt: new Date().toISOString(),
32
+ resolve,
33
+ reject,
34
+ };
35
+ if (processTree)
36
+ state.treeMonitor = processTree.wait().catch((error) => {
37
+ state.treeMonitorError = error;
38
+ });
39
+ child.stdout.on("data", captureOutput(state, state.stdout));
40
+ child.stderr.on("data", captureOutput(state, state.stderr));
41
+ child.on("error", (error) => handleSpawnError(state, error));
42
+ child.on("close", (code) => void handleClose(state, code));
43
+ state.timer = setTimeout(() => terminateExecution(state, "command_timeout"), timeoutMs);
44
+ if (pid < 0)
45
+ terminateExecution(state, "command_spawn_pid_unavailable");
46
+ });
47
+ }
48
+ function captureOutput(state, target) {
49
+ return (chunk) => {
50
+ state.size += chunk.length;
51
+ if (state.size > OUTPUT_LIMIT) {
52
+ terminateExecution(state, "command_output_limit_exceeded");
53
+ return;
54
+ }
55
+ target.push(Buffer.from(chunk));
56
+ };
57
+ }
58
+ function handleSpawnError(state, error) {
59
+ if (state.settled)
60
+ return;
61
+ state.settled = true;
62
+ state.processTree?.stop();
63
+ clearExecutionTimers(state);
64
+ const spawnError = new Error(`command_spawn_error:${message(error)}`);
65
+ if (!state.processTree) {
66
+ state.reject(spawnError);
67
+ return;
68
+ }
69
+ void state.processTree
70
+ .terminate(true)
71
+ .catch(() => undefined)
72
+ .finally(() => state.reject(spawnError));
73
+ }
74
+ async function handleClose(state, code) {
75
+ state.processTree?.stop();
76
+ clearExecutionTimers(state);
77
+ if (state.settled)
78
+ return;
79
+ state.settled = true;
80
+ if (state.terminationReason) {
81
+ await finishBoundedTermination(state);
82
+ state.reject(state.terminationReason);
83
+ return;
84
+ }
85
+ if (!(await assertTreeClosure(state)))
86
+ return;
87
+ state.resolve({
88
+ exit_code: code ?? -1,
89
+ stdout: Buffer.concat(state.stdout),
90
+ stderr: Buffer.concat(state.stderr),
91
+ pid: state.pid,
92
+ started_at: state.startedAt,
93
+ completed_at: new Date().toISOString(),
94
+ });
95
+ }
96
+ async function finishBoundedTermination(state) {
97
+ try {
98
+ await state.terminationTask;
99
+ await state.treeMonitor;
100
+ if (state.processTree)
101
+ await state.processTree.forceQuiescence();
102
+ }
103
+ catch {
104
+ // Preserve the original bounded termination reason after the hard cleanup.
105
+ }
106
+ }
107
+ async function assertTreeClosure(state) {
108
+ if (!state.processTree)
109
+ return true;
110
+ try {
111
+ await state.treeMonitor;
112
+ if (state.treeMonitorError)
113
+ throw state.treeMonitorError;
114
+ await state.processTree.assertQuiescent();
115
+ return true;
116
+ }
117
+ catch (error) {
118
+ await state.processTree.terminate(true).catch(() => undefined);
119
+ state.reject(error);
120
+ return false;
121
+ }
122
+ }
123
+ function terminateExecution(state, reason) {
124
+ if (state.settled || state.terminationReason)
125
+ return;
126
+ state.terminationReason = new Error(reason);
127
+ state.processTree?.stop();
128
+ startGracefulTermination(state);
129
+ state.forceTimer = setTimeout(() => startForcedTermination(state), PROCESS_TREE_GRACE_MS);
130
+ state.forceTimer.unref();
131
+ state.abandonTimer = setTimeout(() => abandonExecution(state), PROCESS_TREE_GRACE_MS * 2);
132
+ state.abandonTimer.unref();
133
+ }
134
+ function startGracefulTermination(state) {
135
+ if (state.processTree)
136
+ state.terminationTask = state.processTree
137
+ .terminate(false)
138
+ .catch((error) => {
139
+ state.treeMonitorError ??= error;
140
+ });
141
+ else {
142
+ state.child.kill();
143
+ state.terminationTask = Promise.resolve();
144
+ }
145
+ }
146
+ function startForcedTermination(state) {
147
+ if (state.processTree)
148
+ state.terminationTask = state.processTree.terminate(true).catch((error) => {
149
+ state.treeMonitorError ??= error;
150
+ });
151
+ else
152
+ state.child.kill("SIGKILL");
153
+ }
154
+ function abandonExecution(state) {
155
+ if (state.settled)
156
+ return;
157
+ state.settled = true;
158
+ state.child.stdout.destroy();
159
+ state.child.stderr.destroy();
160
+ const cleanup = state.processTree
161
+ ? state.processTree.forceQuiescence().catch(() => undefined)
162
+ : Promise.resolve();
163
+ void cleanup.finally(() => state.reject(state.terminationReason));
164
+ }
165
+ function clearExecutionTimers(state) {
166
+ if (state.timer)
167
+ clearTimeout(state.timer);
168
+ if (state.forceTimer)
169
+ clearTimeout(state.forceTimer);
170
+ if (state.abandonTimer)
171
+ clearTimeout(state.abandonTimer);
172
+ }
173
+ function message(error) {
174
+ return error instanceof Error ? error.message : String(error);
175
+ }
@@ -1,3 +1,6 @@
1
1
  import type { DeliveryContractV2 } from "./long-task-delivery-types.js";
2
- export declare function validateDeliveryContractStructure(contract: DeliveryContractV2): void;
3
- export declare function deliveryContractStructureDiagnostics(contract: DeliveryContractV2): string[];
2
+ export interface DeliveryContractStructureValidationOptions {
3
+ allowDeferredDesignComponentBindingClosure?: boolean;
4
+ }
5
+ export declare function validateDeliveryContractStructure(contract: DeliveryContractV2, options?: DeliveryContractStructureValidationOptions): void;
6
+ export declare function deliveryContractStructureDiagnostics(contract: DeliveryContractV2, options?: DeliveryContractStructureValidationOptions): string[];
@@ -10,7 +10,7 @@ import { validateExecutionTargets, validateExternalConfirmationImpacts, } from "
10
10
  import { validateUiSurfaceBindings } from "./long-task-ui-surface-policy.js";
11
11
  import { controlFieldFacts } from "./long-task-control-fields.js";
12
12
  import { validateSemanticAssuranceShape } from "./long-task-semantic-assurance-policy.js";
13
- export function validateDeliveryContractStructure(contract) {
13
+ export function validateDeliveryContractStructure(contract, options = {}) {
14
14
  validateUniqueKeys(contract);
15
15
  validateDependencies(contract);
16
16
  validateControlClosure(contract);
@@ -21,7 +21,7 @@ export function validateDeliveryContractStructure(contract) {
21
21
  allow_uncovered: true,
22
22
  });
23
23
  validateSourceClaimMappings(contract, claims);
24
- validateUiSurfaceBindings(contract, claims);
24
+ validateUiSurfaceBindings(contract, claims, undefined, options);
25
25
  assertCompiledClaimsCovered(claims);
26
26
  validateDeliveryStages(contract);
27
27
  validateExecutionTargets(contract);
@@ -29,7 +29,7 @@ export function validateDeliveryContractStructure(contract) {
29
29
  validateEvidenceCapabilityDeclarations(contract);
30
30
  validateExternalConfirmationImpacts(contract, claims);
31
31
  }
32
- export function deliveryContractStructureDiagnostics(contract) {
32
+ export function deliveryContractStructureDiagnostics(contract, options = {}) {
33
33
  const diagnostics = [];
34
34
  const report = (message) => diagnostics.push(message);
35
35
  validateUniqueKeys(contract, report);
@@ -48,7 +48,7 @@ export function deliveryContractStructureDiagnostics(contract) {
48
48
  });
49
49
  if (claims) {
50
50
  validateSourceClaimMappings(contract, claims, report);
51
- validateUiSurfaceBindings(contract, claims, report);
51
+ validateUiSurfaceBindings(contract, claims, report, options);
52
52
  validateExternalConfirmationImpacts(contract, claims, report);
53
53
  capture(diagnostics, () => assertCompiledClaimsCovered(claims));
54
54
  }
@@ -0,0 +1,6 @@
1
+ import type { DesignResourceImplementationFeasibilityV1 } from "./design-resource-implementation-feasibility-types.js";
2
+ import type { DeliveryBindingV2 } from "./long-task-delivery-types.js";
3
+ type FeasibleRealization = DesignResourceImplementationFeasibilityV1["component_family_cells"][number]["feasible_realizations"][number];
4
+ export declare function matchingFeasibilityBindingKeys(realization: FeasibleRealization, bindings: DeliveryBindingV2[]): Set<string>;
5
+ export declare function validateFeasibilityBindingOwnerRoots(document: DesignResourceImplementationFeasibilityV1, componentBindings: DeliveryBindingV2[], routeBinding: DeliveryBindingV2): void;
6
+ export {};
@@ -0,0 +1,52 @@
1
+ import { invalid } from "./long-task-design-resource-method-binding.js";
2
+ import { matchesRepoPattern, proveRepositoryPatternSubset, } from "./long-task-paths.js";
3
+ export function matchingFeasibilityBindingKeys(realization, bindings) {
4
+ const result = new Set();
5
+ for (const owner of realization.owner_candidates)
6
+ for (const binding of bindings) {
7
+ if (binding.existence !== owner.existence)
8
+ continue;
9
+ const matches = owner.kind === "planned_logical_owner"
10
+ ? binding.key === owner.locator || binding.target === owner.locator
11
+ : (binding.kind === "file" && binding.target === owner.locator) ||
12
+ binding.carrier_paths.some((pattern) => matchesRepoPattern(owner.locator, pattern));
13
+ if (matches)
14
+ result.add(binding.key);
15
+ }
16
+ return result;
17
+ }
18
+ export function validateFeasibilityBindingOwnerRoots(document, componentBindings, routeBinding) {
19
+ const componentRoots = observedOwnerRoots(document, "component_owner_roots");
20
+ const routeRoots = observedOwnerRoots(document, "route_owner_roots");
21
+ if (componentBindings.length && componentRoots.length === 0)
22
+ invalid("feasibility_component_owner_roots_required", document.target_ref);
23
+ for (const binding of componentBindings) {
24
+ if (binding.existence === "planned" && binding.carrier_paths.length === 0)
25
+ invalid("feasibility_planned_binding_carrier_required", binding.key);
26
+ proveBindingPathUniverseWithinObservedRoots(binding, componentRoots, "feasibility_component_binding_outside_owner_roots");
27
+ }
28
+ if (routeRoots.length === 0)
29
+ invalid("feasibility_route_owner_roots_required", document.target_ref);
30
+ proveBindingPathUniverseWithinObservedRoots(routeBinding, routeRoots, "feasibility_route_binding_outside_owner_roots");
31
+ }
32
+ function proveBindingPathUniverseWithinObservedRoots(binding, roots, errorCode) {
33
+ for (const candidate of bindingPathUniverse(binding)) {
34
+ const proofs = roots.map((root) => proveRepositoryPatternSubset(candidate, `${root.replace(/\/+$/u, "")}/**`));
35
+ if (proofs.some((proof) => proof.status === "proven_subset"))
36
+ continue;
37
+ const proof = proofs[0];
38
+ invalid(errorCode, `${binding.key}:${candidate}:${proof?.status ?? "unknown"}:${proof?.reason ?? "owner_roots_empty"}:${roots.join(",")}`);
39
+ }
40
+ }
41
+ function observedOwnerRoots(document, kind) {
42
+ const observation = document.substrate_observations.find((candidate) => candidate.kind === kind);
43
+ return observation?.disposition === "observed" &&
44
+ observation.value?.kind === "repository_paths"
45
+ ? observation.value.paths
46
+ : [];
47
+ }
48
+ function bindingPathUniverse(binding) {
49
+ return binding.kind === "verified"
50
+ ? [...binding.carrier_paths]
51
+ : [binding.target, ...binding.carrier_paths];
52
+ }
@@ -0,0 +1,13 @@
1
+ import type { DesignResourceHandoffPreflightV1 } from "./design-resource-handoff-types.js";
2
+ import type { DesignResourceHandoffPreflightV2 } from "./design-resource-symbolic-fact-types.js";
3
+ import type { CompiledSourceItemV2, DeliveryContractV2 } from "./long-task-delivery-types.js";
4
+ import type { ContractDesignTarget } from "./long-task-design-resource-handoff.js";
5
+ type DesignResourcePreflight = DesignResourceHandoffPreflightV1 | DesignResourceHandoffPreflightV2;
6
+ export interface LongTaskDesignFeasibilityBindingValidationResult {
7
+ consumed_component_binding_refs: string[];
8
+ }
9
+ export interface LongTaskDesignFeasibilityBindingValidationOptions {
10
+ deferComponentBindingAttribution?: boolean;
11
+ }
12
+ export declare function validateLongTaskDesignFeasibilityBindings(contract: DeliveryContractV2, contractTarget: ContractDesignTarget, preflight: DesignResourcePreflight, sourceItems: CompiledSourceItemV2[], options?: LongTaskDesignFeasibilityBindingValidationOptions): LongTaskDesignFeasibilityBindingValidationResult;
13
+ export {};