project-tiny-context-harness 0.8.12 → 0.8.13

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 (158) hide show
  1. package/README.md +43 -19
  2. package/assets/README.md +81 -40
  3. package/assets/README.zh-CN.md +44 -22
  4. package/assets/agents/AGENTS_CORE.md +8 -4
  5. package/assets/skills/context_development_engineer/SKILL.md +9 -1
  6. package/assets/skills/design-resource-authoring/SKILL.md +7 -5
  7. package/assets/skills/design-resource-authoring/references/downstream-handoff.md +2 -2
  8. package/assets/skills/design-resource-authoring/references/open-design-provider.md +22 -2
  9. package/assets/skills/design-resource-authoring/references/recovery-and-writeback.md +199 -0
  10. package/assets/skills/design-resource-authoring/references/resource-selection.md +2 -2
  11. package/assets/skills/long-task-workflow/SKILL.md +6 -6
  12. package/assets/skills/long-task-workflow/references/contract-authoring.md +27 -22
  13. package/assets/skills/long-task-workflow/references/evidence-design.md +49 -32
  14. package/assets/skills/long-task-workflow/references/source-authoring.md +2 -2
  15. package/dist/commands/design-resource-recovery.d.ts +1 -0
  16. package/dist/commands/design-resource-recovery.js +152 -0
  17. package/dist/commands/design-resource.js +20 -1
  18. package/dist/commands/index.js +1 -1
  19. package/dist/lib/design-resource-reconciliation-codec.d.ts +2 -0
  20. package/dist/lib/design-resource-reconciliation-codec.js +174 -0
  21. package/dist/lib/design-resource-reconciliation-types.d.ts +62 -0
  22. package/dist/lib/design-resource-reconciliation-types.js +1 -0
  23. package/dist/lib/design-resource-reconciliation.d.ts +3 -0
  24. package/dist/lib/design-resource-reconciliation.js +224 -0
  25. package/dist/lib/design-resource-recovery-authority-policy.d.ts +8 -0
  26. package/dist/lib/design-resource-recovery-authority-policy.js +123 -0
  27. package/dist/lib/design-resource-recovery-catalog-resources.d.ts +6 -0
  28. package/dist/lib/design-resource-recovery-catalog-resources.js +121 -0
  29. package/dist/lib/design-resource-recovery-catalog-shape.d.ts +3 -0
  30. package/dist/lib/design-resource-recovery-catalog-shape.js +104 -0
  31. package/dist/lib/design-resource-recovery-catalog.d.ts +9 -0
  32. package/dist/lib/design-resource-recovery-catalog.js +103 -0
  33. package/dist/lib/design-resource-recovery-cleanup.d.ts +2 -0
  34. package/dist/lib/design-resource-recovery-cleanup.js +11 -0
  35. package/dist/lib/design-resource-recovery-codec-primitives.d.ts +19 -0
  36. package/dist/lib/design-resource-recovery-codec-primitives.js +92 -0
  37. package/dist/lib/design-resource-recovery-codec.d.ts +4 -0
  38. package/dist/lib/design-resource-recovery-codec.js +93 -0
  39. package/dist/lib/design-resource-recovery-current.d.ts +9 -0
  40. package/dist/lib/design-resource-recovery-current.js +41 -0
  41. package/dist/lib/design-resource-recovery-delta-policy.d.ts +5 -0
  42. package/dist/lib/design-resource-recovery-delta-policy.js +108 -0
  43. package/dist/lib/design-resource-recovery-files.d.ts +37 -0
  44. package/dist/lib/design-resource-recovery-files.js +204 -0
  45. package/dist/lib/design-resource-recovery-final-disposition-shape.d.ts +2 -0
  46. package/dist/lib/design-resource-recovery-final-disposition-shape.js +47 -0
  47. package/dist/lib/design-resource-recovery-patch-types.d.ts +54 -0
  48. package/dist/lib/design-resource-recovery-patch-types.js +1 -0
  49. package/dist/lib/design-resource-recovery-replay.d.ts +7 -0
  50. package/dist/lib/design-resource-recovery-replay.js +112 -0
  51. package/dist/lib/design-resource-recovery-repository-bindings.d.ts +9 -0
  52. package/dist/lib/design-resource-recovery-repository-bindings.js +49 -0
  53. package/dist/lib/design-resource-recovery-schema.d.ts +5 -0
  54. package/dist/lib/design-resource-recovery-schema.js +5 -0
  55. package/dist/lib/design-resource-recovery-shape.d.ts +10 -0
  56. package/dist/lib/design-resource-recovery-shape.js +184 -0
  57. package/dist/lib/design-resource-recovery-source-authority.d.ts +5 -0
  58. package/dist/lib/design-resource-recovery-source-authority.js +219 -0
  59. package/dist/lib/design-resource-recovery-text.d.ts +17 -0
  60. package/dist/lib/design-resource-recovery-text.js +186 -0
  61. package/dist/lib/design-resource-recovery-types.d.ts +197 -0
  62. package/dist/lib/design-resource-recovery-types.js +1 -0
  63. package/dist/lib/design-resource-recovery-writeback-policy.d.ts +5 -0
  64. package/dist/lib/design-resource-recovery-writeback-policy.js +326 -0
  65. package/dist/lib/design-resource-recovery-writeback-shape.d.ts +6 -0
  66. package/dist/lib/design-resource-recovery-writeback-shape.js +155 -0
  67. package/dist/lib/design-resource-recovery.d.ts +44 -0
  68. package/dist/lib/design-resource-recovery.js +216 -0
  69. package/dist/lib/long-task-acceptance-shape.js +8 -2
  70. package/dist/lib/long-task-activation-validation.js +13 -3
  71. package/dist/lib/long-task-admitted-observation-records.d.ts +25 -0
  72. package/dist/lib/long-task-admitted-observation-records.js +40 -0
  73. package/dist/lib/long-task-admitted-observation.d.ts +93 -0
  74. package/dist/lib/long-task-admitted-observation.js +201 -0
  75. package/dist/lib/long-task-artifacts.d.ts +1 -0
  76. package/dist/lib/long-task-artifacts.js +1 -0
  77. package/dist/lib/long-task-authority-material-diff.js +1 -0
  78. package/dist/lib/long-task-authority-policy.d.ts +2 -0
  79. package/dist/lib/long-task-authority-policy.js +2 -0
  80. package/dist/lib/long-task-authority-revision-details.js +3 -1
  81. package/dist/lib/long-task-authority.js +2 -1
  82. package/dist/lib/long-task-check-execution-policy.js +5 -0
  83. package/dist/lib/long-task-check-runner.d.ts +2 -2
  84. package/dist/lib/long-task-check-runner.js +440 -23
  85. package/dist/lib/long-task-claims.d.ts +18 -0
  86. package/dist/lib/long-task-claims.js +35 -0
  87. package/dist/lib/long-task-compact-structure-targets.js +1 -0
  88. package/dist/lib/long-task-conformance-policy.js +3 -1
  89. package/dist/lib/long-task-counterfactual-claim-policy.d.ts +3 -4
  90. package/dist/lib/long-task-counterfactual-claim-policy.js +11 -3
  91. package/dist/lib/long-task-counterfactual-sandbox.js +1 -0
  92. package/dist/lib/long-task-counterfactual-types.d.ts +2 -0
  93. package/dist/lib/long-task-delivery-compiler.js +2 -0
  94. package/dist/lib/long-task-evidence-capability-policy.d.ts +3 -2
  95. package/dist/lib/long-task-evidence-capability-policy.js +114 -8
  96. package/dist/lib/long-task-evidence-capability-runtime.d.ts +5 -2
  97. package/dist/lib/long-task-evidence-capability-runtime.js +149 -5
  98. package/dist/lib/long-task-evidence-sensitivity-policy.d.ts +9 -0
  99. package/dist/lib/long-task-evidence-sensitivity-policy.js +31 -1
  100. package/dist/lib/long-task-evidence-v2.d.ts +4 -4
  101. package/dist/lib/long-task-evidence-v2.js +321 -43
  102. package/dist/lib/long-task-exact-comparison.d.ts +16 -0
  103. package/dist/lib/long-task-exact-comparison.js +28 -0
  104. package/dist/lib/long-task-execution-observation.d.ts +24 -0
  105. package/dist/lib/long-task-execution-observation.js +332 -0
  106. package/dist/lib/long-task-explain-acceptance-link.d.ts +4 -0
  107. package/dist/lib/long-task-explain-acceptance-link.js +4 -0
  108. package/dist/lib/long-task-explain-claim-links.d.ts +2 -0
  109. package/dist/lib/long-task-explain-claim-links.js +2 -0
  110. package/dist/lib/long-task-explain-source-links.d.ts +6 -0
  111. package/dist/lib/long-task-final-v2.js +7 -4
  112. package/dist/lib/long-task-json-pointer-observation.d.ts +59 -0
  113. package/dist/lib/long-task-json-pointer-observation.js +184 -0
  114. package/dist/lib/long-task-observation-artifact.d.ts +8 -0
  115. package/dist/lib/long-task-observation-artifact.js +58 -0
  116. package/dist/lib/long-task-observation-authority.d.ts +17 -0
  117. package/dist/lib/long-task-observation-authority.js +348 -0
  118. package/dist/lib/long-task-paths.js +1 -1
  119. package/dist/lib/long-task-process-observation.d.ts +6 -0
  120. package/dist/lib/long-task-process-observation.js +117 -0
  121. package/dist/lib/long-task-process-runtime-closure.d.ts +13 -0
  122. package/dist/lib/long-task-process-runtime-closure.js +175 -0
  123. package/dist/lib/long-task-progress.js +2 -0
  124. package/dist/lib/long-task-protected-files.d.ts +1 -1
  125. package/dist/lib/long-task-protected-files.js +1 -30
  126. package/dist/lib/long-task-risk.js +8 -3
  127. package/dist/lib/long-task-root-shape.js +10 -1
  128. package/dist/lib/long-task-runner-environment.d.ts +1 -1
  129. package/dist/lib/long-task-runner-environment.js +4 -4
  130. package/dist/lib/long-task-runner-freeze.d.ts +4 -2
  131. package/dist/lib/long-task-runner-freeze.js +128 -28
  132. package/dist/lib/long-task-runtime-types.d.ts +111 -0
  133. package/dist/lib/long-task-scoped-binding.d.ts +13 -0
  134. package/dist/lib/long-task-scoped-binding.js +11 -0
  135. package/dist/lib/long-task-semantic-contract-types.d.ts +1 -0
  136. package/dist/lib/long-task-semantic-fact-contract-closure.js +3 -3
  137. package/dist/lib/long-task-semantic-fact-evidence.d.ts +3 -3
  138. package/dist/lib/long-task-semantic-fact-evidence.js +75 -58
  139. package/dist/lib/long-task-source-claim-validation.js +4 -1
  140. package/dist/lib/long-task-source-target-continuity.d.ts +2 -2
  141. package/dist/lib/long-task-source-target-continuity.js +35 -2
  142. package/dist/lib/long-task-source-target-index.d.ts +3 -0
  143. package/dist/lib/long-task-source-target-index.js +19 -0
  144. package/dist/lib/long-task-stage-policy.js +10 -4
  145. package/dist/lib/long-task-static-observation-freeze.d.ts +83 -0
  146. package/dist/lib/long-task-static-observation-freeze.js +428 -0
  147. package/dist/lib/long-task-status-projection.js +5 -1
  148. package/dist/lib/long-task-target-policy.d.ts +1 -1
  149. package/dist/lib/long-task-target-policy.js +3 -0
  150. package/dist/lib/long-task-verifier-dependency-closure.js +7 -3
  151. package/dist/lib/long-task-verifier-v2.js +70 -9
  152. package/dist/lib/long-task-workspace.d.ts +1 -1
  153. package/dist/lib/long-task-workspace.js +1 -12
  154. package/dist/lib/repository-path-safety.d.ts +14 -0
  155. package/dist/lib/repository-path-safety.js +126 -0
  156. package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +53 -8
  157. package/migrations/README.md +77 -0
  158. package/package.json +1 -1
@@ -1,14 +1,29 @@
1
- import { spawn } from "node:child_process";
2
- import { access } from "node:fs/promises";
1
+ import { execFile, spawn } from "node:child_process";
2
+ import { randomBytes } from "node:crypto";
3
+ import { access, realpath } from "node:fs/promises";
3
4
  import net from "node:net";
4
5
  import path from "node:path";
6
+ import { promisify } from "node:util";
5
7
  import { decodeCheckEvidence, invalidEvidence, } from "./long-task-check-evidence-decoder.js";
6
8
  import { declaredEnvironmentValues, outputContainsDeclaredEnvironmentValue, runnerEnvironment, } from "./long-task-runner-environment.js";
9
+ import { decodeProductObservationEnvelope } from "./long-task-process-observation.js";
7
10
  import { resolveInsideRepository } from "./long-task-workspace.js";
8
- import { sha256Hex } from "./strict-codec.js";
11
+ import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
9
12
  const OUTPUT_LIMIT = 2 * 1024 * 1024;
10
- export async function executeCheckRunner(check, snapshotRoot) {
13
+ const PROCESS_TREE_GRACE_MS = 1_000;
14
+ const execFileAsync = promisify(execFile);
15
+ export async function executeCheckRunner(check, snapshotRoot, context) {
11
16
  const started = Date.now();
17
+ let processAuthorities;
18
+ try {
19
+ processAuthorities = processObservationAuthorities(check.observation_authorities ?? [], context?.observation_authorities ?? []);
20
+ }
21
+ catch (error) {
22
+ return invalidRawExecution(check, started, message(error));
23
+ }
24
+ const processObserverIssue = validateProcessObserverActivation(check, snapshotRoot, processAuthorities, context);
25
+ if (processObserverIssue)
26
+ return invalidRawExecution(check, started, processObserverIssue);
12
27
  const unavailable = await probeEnvironment(check.environment_requirements, snapshotRoot);
13
28
  if (unavailable)
14
29
  return {
@@ -31,11 +46,27 @@ export async function executeCheckRunner(check, snapshotRoot) {
31
46
  const maximumAttempts = retryAllowed ? 2 : 1;
32
47
  for (let attempt = 1; attempt <= maximumAttempts; attempt += 1) {
33
48
  try {
34
- const raw = await runOnce(check, snapshotRoot);
49
+ const raw = await runOnce(check, snapshotRoot, processAuthorities, context?.snapshot_sha256, context?.process_runtime_closure_identity);
35
50
  const secrets = declaredEnvironmentValues(check.environment_requirements);
36
- const decoded = outputContainsDeclaredEnvironmentValue(raw, secrets)
51
+ let decoded = outputContainsDeclaredEnvironmentValue(raw, secrets)
37
52
  ? invalidEvidence(raw.exit_code, "check_evidence_contains_declared_environment_value")
38
- : decodeCheckEvidence(check, raw.exit_code, raw.stdout, raw.stderr);
53
+ : raw.process_observation
54
+ ? {
55
+ execution_status: "completed",
56
+ exit_code: raw.exit_code,
57
+ observations: {},
58
+ evidence_records: [],
59
+ error: null,
60
+ }
61
+ : decodeCheckEvidence(check, raw.exit_code, raw.stdout, raw.stderr);
62
+ if (raw.process_observation && raw.exit_code !== 0)
63
+ decoded = invalidEvidence(raw.exit_code, "process_observer_nonzero_exit");
64
+ else if (raw.process_observation &&
65
+ decoded.execution_status !== "completed")
66
+ decoded = invalidEvidence(raw.exit_code, "project_submitted_execution_status_disagrees_with_harness");
67
+ if (raw.process_observation &&
68
+ packageObservationContainsDeclaredEnvironmentValue(raw.process_observation, secrets))
69
+ decoded = invalidEvidence(raw.exit_code, "check_evidence_contains_declared_environment_value");
39
70
  return {
40
71
  raw_execution_identity: check.raw_execution_identity,
41
72
  execution_identity: check.raw_execution_identity,
@@ -44,6 +75,8 @@ export async function executeCheckRunner(check, snapshotRoot) {
44
75
  stderr_sha256: sha256Hex(raw.stderr),
45
76
  attempts: attempt,
46
77
  duration_ms: Date.now() - started,
78
+ package_observations: raw.process_observation?.package_observations ?? [],
79
+ host_execution_attestation: raw.host_execution_attestation,
47
80
  };
48
81
  }
49
82
  catch (error) {
@@ -53,7 +86,9 @@ export async function executeCheckRunner(check, snapshotRoot) {
53
86
  return {
54
87
  raw_execution_identity: check.raw_execution_identity,
55
88
  execution_identity: check.raw_execution_identity,
56
- execution_status: "infrastructure_error",
89
+ execution_status: isProcessObservationError(reason)
90
+ ? "invalid_evidence"
91
+ : "infrastructure_error",
57
92
  exit_code: -1,
58
93
  observations: {},
59
94
  evidence_records: [],
@@ -62,37 +97,100 @@ export async function executeCheckRunner(check, snapshotRoot) {
62
97
  attempts: attempt,
63
98
  duration_ms: Date.now() - started,
64
99
  error: reason,
100
+ package_observations: [],
101
+ host_execution_attestation: null,
65
102
  };
66
103
  }
67
104
  }
68
105
  throw new Error("unreachable");
69
106
  }
70
- async function runOnce(check, snapshotRoot) {
107
+ async function runOnce(check, snapshotRoot, processAuthorities, snapshotSha256, processRuntimeClosureIdentity) {
71
108
  const runner = check.runner;
72
109
  const cwd = resolveInsideRepository(snapshotRoot, runner.resolved_cwd || ".", "runner.resolved_cwd");
73
110
  const executable = runner.type === "project_binary"
74
111
  ? resolveInsideRepository(snapshotRoot, runner.resolved_target, "runner.resolved_target")
75
112
  : runner.executable;
76
113
  const argv = [...runner.executable_argv_prefix, ...runner.argv];
114
+ if (!processAuthorities.length) {
115
+ const execution = await spawnOnce(executable, argv, cwd, runner.timeout_ms, runnerEnvironment(check.environment_requirements), false);
116
+ return {
117
+ ...execution,
118
+ process_observation: null,
119
+ host_execution_attestation: null,
120
+ };
121
+ }
122
+ const executionNonce = randomBytes(32).toString("hex");
123
+ const execution = await spawnOnce(executable, argv, cwd, runner.timeout_ms, runnerEnvironment(check.environment_requirements, "product_observation"), true);
124
+ const processObservation = decodeProductObservationEnvelope({
125
+ bytes: execution.stdout,
126
+ authorities: processAuthorities,
127
+ });
128
+ const declaredRoot = resolveInsideRepository(snapshotRoot, processAuthorities[0].runtime_requirements.declared_root_entrypoint, "process_observer.declared_root_entrypoint");
129
+ const [actualExecutable, actualDeclaredRoot] = await Promise.all([
130
+ realpath(executable),
131
+ realpath(declaredRoot),
132
+ ]);
133
+ const directRootMatch = sameHostPath(actualExecutable, actualDeclaredRoot) &&
134
+ sameStringArray(argv, processAuthorities[0].runtime_requirements.declared_root_argv ?? []);
135
+ if (!directRootMatch)
136
+ throw new Error("process_observer_direct_root_required");
137
+ return {
138
+ exit_code: execution.exit_code,
139
+ stdout: execution.stdout,
140
+ stderr: execution.stderr,
141
+ process_observation: processObservation,
142
+ host_execution_attestation: {
143
+ raw_execution_identity: check.raw_execution_identity,
144
+ executable_path: actualExecutable,
145
+ declared_root_entrypoint: actualDeclaredRoot,
146
+ actual_argv: [...argv],
147
+ declared_root_argv: [
148
+ ...(processAuthorities[0].runtime_requirements.declared_root_argv ??
149
+ []),
150
+ ],
151
+ direct_root_match: directRootMatch,
152
+ pid: execution.pid,
153
+ started_at: execution.started_at,
154
+ completed_at: execution.completed_at,
155
+ exit_code: execution.exit_code,
156
+ snapshot_sha256: snapshotSha256,
157
+ observation_execution_nonce: executionNonce,
158
+ observation_artifact_sha256: processObservation.artifact_sha256,
159
+ process_runtime_closure_identity: processRuntimeClosureIdentity,
160
+ },
161
+ };
162
+ }
163
+ async function spawnOnce(executable, argv, cwd, timeoutMs, environment, containProcessTree) {
77
164
  return new Promise((resolve, reject) => {
165
+ const startedAt = new Date().toISOString();
78
166
  const child = spawn(executable, argv, {
79
167
  cwd,
80
168
  shell: false,
81
169
  windowsHide: true,
82
- env: runnerEnvironment(check.environment_requirements),
170
+ env: environment,
171
+ detached: containProcessTree && process.platform !== "win32",
83
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();
84
182
  const stdout = [];
85
183
  const stderr = [];
86
184
  let size = 0;
87
185
  let settled = false;
186
+ let terminationReason = null;
187
+ let terminationTask = null;
188
+ let forceTimer = null;
189
+ let abandonTimer = null;
88
190
  const capture = (target) => (chunk) => {
89
191
  size += chunk.length;
90
192
  if (size > OUTPUT_LIMIT) {
91
- child.kill();
92
- if (!settled) {
93
- settled = true;
94
- reject(new Error("command_output_limit_exceeded"));
95
- }
193
+ terminate("command_output_limit_exceeded");
96
194
  return;
97
195
  }
98
196
  target.push(Buffer.from(chunk));
@@ -102,29 +200,348 @@ async function runOnce(check, snapshotRoot) {
102
200
  child.on("error", (error) => {
103
201
  if (!settled) {
104
202
  settled = true;
105
- reject(new Error(`command_spawn_error:${message(error)}`));
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);
106
216
  }
107
217
  });
108
218
  const timer = setTimeout(() => {
109
- child.kill();
110
- if (!settled) {
111
- settled = true;
112
- reject(new Error("command_timeout"));
113
- }
114
- }, runner.timeout_ms);
115
- child.on("close", (code) => {
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;
116
225
  clearTimeout(timer);
226
+ if (forceTimer)
227
+ clearTimeout(forceTimer);
228
+ if (abandonTimer)
229
+ clearTimeout(abandonTimer);
117
230
  if (settled)
118
231
  return;
119
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
+ }
120
260
  resolve({
121
261
  exit_code: code ?? -1,
122
262
  stdout: Buffer.concat(stdout),
123
263
  stderr: Buffer.concat(stderr),
264
+ pid,
265
+ started_at: startedAt,
266
+ completed_at: new Date().toISOString(),
124
267
  });
125
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
+ }
126
306
  });
127
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
+ function validateProcessObserverActivation(check, snapshotRoot, authorities, context) {
453
+ if (!authorities.length)
454
+ return null;
455
+ if (!context?.snapshot_sha256)
456
+ return "process_observer_snapshot_identity_required";
457
+ if (!context.process_runtime_closure_identity ||
458
+ !check.process_runtime_closure ||
459
+ context.process_runtime_closure_identity !==
460
+ check.process_runtime_closure.closure_identity)
461
+ return "process_runtime_closure_identity_mismatch";
462
+ if (check.environment_requirements.some((requirement) => requirement.kind === "env_var" &&
463
+ (requirement.target.toUpperCase() === "TY_CONTEXT_OBSERVATION_OUTPUT" ||
464
+ requirement.target.toUpperCase() ===
465
+ "TY_CONTEXT_OBSERVATION_CHALLENGE" ||
466
+ requirement.target.toUpperCase() === "TY_CONTEXT_CHECK_PROTOCOL")))
467
+ return "process_observer_reserved_environment_requirement";
468
+ if (check.runner.type !== "project_binary" ||
469
+ check.execution_target.entrypoint !== "root")
470
+ return "process_observer_direct_root_required";
471
+ const executable = resolveInsideRepository(snapshotRoot, check.runner.resolved_target, "process_observer.runner_target");
472
+ const actualArgv = [
473
+ ...check.runner.executable_argv_prefix,
474
+ ...check.runner.argv,
475
+ ];
476
+ for (const authority of authorities) {
477
+ const requirements = authority.runtime_requirements;
478
+ if (requirements.runtime_family !== "process" ||
479
+ requirements.target_role !== "product" ||
480
+ requirements.entrypoint !== "root" ||
481
+ requirements.runner_type !== "project_binary" ||
482
+ requirements.resolved_runner_target !== check.runner.resolved_target ||
483
+ requirements.declared_root_argv === null ||
484
+ !sameStringArray(requirements.resolved_runner_argv, check.runner.argv) ||
485
+ !sameStringArray(requirements.declared_root_argv, actualArgv) ||
486
+ check.runner.executable_argv_prefix.length !== 0 ||
487
+ !requirements.direct_root_match)
488
+ return "process_observer_direct_root_required";
489
+ const declaredRoot = resolveInsideRepository(snapshotRoot, requirements.declared_root_entrypoint, "process_observer.declared_root_entrypoint");
490
+ if (!sameHostPath(executable, declaredRoot))
491
+ return "process_observer_direct_root_required";
492
+ }
493
+ return null;
494
+ }
495
+ function processObservationAuthorities(checkAuthorities, groupedAuthorities) {
496
+ const byIdentity = new Map();
497
+ for (const authority of [...checkAuthorities, ...groupedAuthorities]) {
498
+ if (authority.authority !== "package_process_json_exact")
499
+ continue;
500
+ const identity = `${authority.assertion_ref}\0${authority.obligation_ref}\0${authority.method}`;
501
+ const existing = byIdentity.get(identity);
502
+ if (existing &&
503
+ canonicalValueJson(existing) !== canonicalValueJson(authority))
504
+ throw new Error("process_observation_authority_group_mismatch");
505
+ byIdentity.set(identity, authority);
506
+ }
507
+ return [...byIdentity.values()];
508
+ }
509
+ function invalidRawExecution(check, started, reason) {
510
+ return {
511
+ raw_execution_identity: check.raw_execution_identity,
512
+ execution_identity: check.raw_execution_identity,
513
+ execution_status: "invalid_evidence",
514
+ exit_code: -1,
515
+ observations: {},
516
+ evidence_records: [],
517
+ stdout_sha256: sha256Hex(""),
518
+ stderr_sha256: sha256Hex(reason),
519
+ attempts: 0,
520
+ duration_ms: Date.now() - started,
521
+ error: reason,
522
+ package_observations: [],
523
+ host_execution_attestation: null,
524
+ };
525
+ }
526
+ function packageObservationContainsDeclaredEnvironmentValue(observation, values) {
527
+ if (!values.length)
528
+ return false;
529
+ const serialized = JSON.stringify(observation.observations);
530
+ return values.some((value) => serialized.includes(value));
531
+ }
532
+ function isProcessObservationError(reason) {
533
+ return (reason.startsWith("process_observation_") ||
534
+ reason.startsWith("process_observer_"));
535
+ }
536
+ function sameHostPath(left, right) {
537
+ return process.platform === "win32"
538
+ ? path.resolve(left).toLowerCase() === path.resolve(right).toLowerCase()
539
+ : path.resolve(left) === path.resolve(right);
540
+ }
541
+ function sameStringArray(left, right) {
542
+ return (left.length === right.length &&
543
+ left.every((value, index) => value === right[index]));
544
+ }
128
545
  async function probeEnvironment(requirements, snapshotRoot) {
129
546
  for (const requirement of requirements) {
130
547
  let available = false;
@@ -4,7 +4,25 @@ export interface CompiledClaimsV2 {
4
4
  by_outcome: Record<string, ProductClaimV2[]>;
5
5
  summary: ClaimCoverageSummaryV2;
6
6
  }
7
+ interface ExternalConfirmationProjectionInput {
8
+ global: {
9
+ acceptance: {
10
+ external_confirmations: readonly {
11
+ blocks_target: boolean;
12
+ impact_claims: readonly string[];
13
+ }[];
14
+ };
15
+ };
16
+ }
17
+ interface AllOutcomeExternalConfirmationProjectionInput extends ExternalConfirmationProjectionInput {
18
+ outcomes: readonly {
19
+ key: string;
20
+ }[];
21
+ }
22
+ export declare function outcomeResultExternallyBlocked(contract: ExternalConfirmationProjectionInput, outcomeKey: string): boolean;
23
+ export declare function allOutcomeResultsExternallyBlocked(contract: AllOutcomeExternalConfirmationProjectionInput): boolean;
7
24
  export declare function compileProductClaimCoverage(contract: DeliveryContractV2, options?: {
8
25
  allow_uncovered?: boolean;
9
26
  }): CompiledClaimsV2;
10
27
  export declare function assertCompiledClaimsCovered(compiled: CompiledClaimsV2): void;
28
+ export {};
@@ -2,6 +2,15 @@ import { assertAllClaimsCovered, assertAllGlobalClaimsCovered, generateClaims, g
2
2
  import { fail } from "./long-task-delivery-shape.js";
3
3
  import { validateClaimAssertionOperator, validateGlobalAssertionOperator, } from "./long-task-claim-proof-policy.js";
4
4
  import { matchesRepoPattern } from "./long-task-paths.js";
5
+ export function outcomeResultExternallyBlocked(contract, outcomeKey) {
6
+ const resultClaim = `${outcomeKey}.result`;
7
+ return contract.global.acceptance.external_confirmations.some((confirmation) => confirmation.blocks_target &&
8
+ confirmation.impact_claims.includes(resultClaim));
9
+ }
10
+ export function allOutcomeResultsExternallyBlocked(contract) {
11
+ return (contract.outcomes.length > 0 &&
12
+ contract.outcomes.every((outcome) => outcomeResultExternallyBlocked(contract, outcome.key)));
13
+ }
5
14
  export function compileProductClaimCoverage(contract, options = {}) {
6
15
  const byGlobal = generateGlobalClaims(contract.global);
7
16
  const byOutcome = Object.fromEntries(contract.outcomes.map((outcome) => [outcome.key, generateClaims(outcome)]));
@@ -69,6 +78,8 @@ function compileGlobalScope(contract, claims, profiles) {
69
78
  validateGlobalAssertionOperator(proof.assertion, proof.check.key);
70
79
  addProof(proofs, claimKey, claimProof);
71
80
  }
81
+ for (const claim of claims)
82
+ addBlockingExternalConfirmationProofs(contract, claim, proofs);
72
83
  validateProfileUse("GLOBAL", profiles, claims);
73
84
  return Object.fromEntries(claims.map((claim) => {
74
85
  const claimProofs = proofs.get(claim.local_key) ?? [];
@@ -141,6 +152,9 @@ function compileOutcomeScope(contract, outcomeKey, claims, profiles, outcomeKeys
141
152
  applicability_ref: factBinding.applicability_ref,
142
153
  });
143
154
  }
155
+ for (const claim of claims)
156
+ if (claim.kind !== "semantic_fact")
157
+ addBlockingExternalConfirmationProofs(contract, claim, proofs);
144
158
  validatePopulationReferences(outcome, claimMap);
145
159
  validateCounterfactualReferences(outcome, claimMap);
146
160
  validateProfileUse(outcomeKey, profiles, claims);
@@ -254,6 +268,7 @@ function validateCounterfactualReferences(outcome, claims) {
254
268
  for (const assertion of [
255
269
  ...control.expected_assertion_failures,
256
270
  ...control.preserved_assertions,
271
+ ...control.allowed_fanout_assertions,
257
272
  ])
258
273
  if (!assertions.has(assertion))
259
274
  fail("counterfactual_assertion_unknown", `${outcome.key}:${control.key}:${assertion}`);
@@ -306,6 +321,26 @@ function addProof(proofs, claim, proof) {
306
321
  rows.push(proof);
307
322
  proofs.set(claim, rows);
308
323
  }
324
+ function addBlockingExternalConfirmationProofs(contract, claim, proofs) {
325
+ const confirmations = contract.global.acceptance.external_confirmations.filter((confirmation) => confirmation.blocks_target &&
326
+ confirmation.impact_claims.includes(claim.id));
327
+ if (!confirmations.length)
328
+ return;
329
+ const proofSurfaces = "required_proof_surfaces" in claim &&
330
+ claim.required_proof_surfaces.length > 0
331
+ ? claim.required_proof_surfaces
332
+ : ["runtime_behavior"];
333
+ for (const confirmation of confirmations)
334
+ for (const applicabilityRef of claim.applicability_refs)
335
+ for (const proofSurface of proofSurfaces)
336
+ addProof(proofs, claim.local_key, {
337
+ check_key: `EXTERNAL.${confirmation.key}`,
338
+ assertion_key: null,
339
+ polarity: claim.required_polarity,
340
+ proof_surface: proofSurface,
341
+ applicability_ref: applicabilityRef,
342
+ });
343
+ }
309
344
  function sameSet(left, right) {
310
345
  return (left.length === right.length &&
311
346
  new Set(left).size === left.length &&
@@ -11,6 +11,7 @@ const BASE_STRUCTURE_BOUNDARY_BY_FIELD = {
11
11
  journey_roles: "contract.check.journey-roles",
12
12
  carrier_paths: "contract.binding.carrier-paths",
13
13
  preserved_assertions: "contract.counterfactual.preserved-assertions",
14
+ allowed_fanout_assertions: "contract.counterfactual.allowed-fanout-assertions",
14
15
  expected_assertion_failures: "contract.counterfactual.expected-assertion-failures",
15
16
  };
16
17
  export function longTaskCompactSharedStructureTargets(root, carrier) {
@@ -1,3 +1,4 @@
1
+ import { allOutcomeResultsExternallyBlocked } from "./long-task-claims.js";
1
2
  export function semanticConformanceRequired(contract, _effectiveRisk) {
2
3
  const productFamilies = new Set(contract.task.execution_targets
3
4
  .filter((target) => contract.task.target_profile.required_target_refs.includes(target.key))
@@ -8,7 +9,8 @@ export function semanticConformanceRequired(contract, _effectiveRisk) {
8
9
  export function validateSemanticConformance(contract, effectiveRisk, compiledChecks, report) {
9
10
  const declared = contract.global.acceptance.checks.filter((check) => check.journey_roles.includes("conformance"));
10
11
  if (semanticConformanceRequired(contract, effectiveRisk) &&
11
- !declared.length) {
12
+ !declared.length &&
13
+ !allOutcomeResultsExternallyBlocked(contract)) {
12
14
  issue(report, "semantic_conformance_check_required", contract.task.id);
13
15
  return;
14
16
  }
@@ -1,10 +1,9 @@
1
1
  import type { CounterfactualControlV2, DeliveryContractV2, DeliveryBindingV2, DeliveryOutcomeV2, GlobalCounterfactualControlV2 } from "./long-task-delivery-types.js";
2
+ import { type ScopedDeliveryBindingV2 } from "./long-task-scoped-binding.js";
2
3
  export declare function validateCounterfactualBindingClaims(outcome: DeliveryOutcomeV2, control: CounterfactualControlV2, binding: DeliveryBindingV2): void;
3
- export declare function resolveGlobalCounterfactualBinding(contract: DeliveryContractV2, control: GlobalCounterfactualControlV2): {
4
+ export declare function resolveGlobalCounterfactualBinding(contract: DeliveryContractV2, control: GlobalCounterfactualControlV2): ScopedDeliveryBindingV2 & {
4
5
  outcome: DeliveryOutcomeV2;
5
- binding: DeliveryBindingV2;
6
6
  };
7
- export declare function validateGlobalCounterfactualBindingClaims(contract: DeliveryContractV2, control: GlobalCounterfactualControlV2): {
7
+ export declare function validateGlobalCounterfactualBindingClaims(contract: DeliveryContractV2, control: GlobalCounterfactualControlV2): ScopedDeliveryBindingV2 & {
8
8
  outcome: DeliveryOutcomeV2;
9
- binding: DeliveryBindingV2;
10
9
  };