@deksden-com/dd-flow-cli 0.9.0-beta.150 → 0.9.0-beta.152

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 (40) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +17 -22
  3. package/dist/build-info.json +6 -6
  4. package/dist/harness-runtime/lib/agy-error-boundary.mjs +26 -5
  5. package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +67 -0
  6. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +10 -14
  7. package/dist/harness-runtime/lib/dd-agy.mjs +3 -3
  8. package/dist/harness-runtime/lib/dd-droid.mjs +2 -2
  9. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +62 -10
  10. package/dist/harness-runtime/lib/dd-opencode.mjs +32 -14
  11. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +1 -1
  12. package/dist/harness-runtime/lib/dd-zcode.mjs +37 -12
  13. package/dist/harness-runtime/lib/delegation-instructions.d.mts +1 -1
  14. package/dist/harness-runtime/lib/delegation-instructions.mjs +13 -4
  15. package/dist/harness-runtime/lib/model-observations.mjs +19 -7
  16. package/dist/harness-runtime/lib/native-children.d.mts +12 -1
  17. package/dist/harness-runtime/lib/native-children.mjs +47 -1
  18. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +9 -1
  19. package/dist/harness-runtime/lib/provider-overload-policy.mjs +97 -6
  20. package/dist/services/check-runner-evidence.js +54 -309
  21. package/dist/services/check-runner-proof.js +373 -0
  22. package/dist/services/code-checks.js +31 -12
  23. package/dist/services/controller-child-capacity.js +58 -0
  24. package/dist/services/controller-child-observer.js +9 -3
  25. package/dist/services/memory-init-native-capacity.js +1 -1
  26. package/dist/services/provider-capacity.js +15 -7
  27. package/dist/services/repair-intents.js +4 -1
  28. package/dist/services/run-controller-adapter.js +1 -1
  29. package/dist/services/run-controller.js +87 -18
  30. package/dist/services/run-recovery-guard.js +1 -1
  31. package/dist/services/run-recovery-runtime.js +1 -1
  32. package/dist/services/stage-context.js +56 -8
  33. package/dist/services/vnext-code-review.js +1 -1
  34. package/dist/services/vnext-code.js +3 -2
  35. package/dist/services/vnext-fanout.js +1 -1
  36. package/dist/services/vnext-merge.js +1 -1
  37. package/dist/services/work-registry.js +1 -1
  38. package/dist/verification-components.json +299 -0
  39. package/package.json +7 -6
  40. package/tools/verification-jobs.mjs +317 -0
@@ -1,71 +1,13 @@
1
1
  import fs from "node:fs";
2
2
  import crypto from "node:crypto";
3
- import { stripVTControlCharacters } from "node:util";
4
3
  import path from "node:path";
5
4
  import { parse } from "shell-quote";
6
5
  import { AppError } from "../shared/errors.js";
7
6
  import { validateObservationDuration } from "../harness-runtime/lib/observation-clock.mjs";
8
7
  import { isDeepStrictEqual } from "node:util";
9
- import { fileURLToPath } from "node:url";
10
- function object(value) { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }
11
- function boundedString(value, bytes) { return typeof value === "string" && Buffer.byteLength(value) <= bytes; }
12
- const validatedBrowserProofs = new WeakSet();
13
- const unsupportedObservationContracts = new WeakSet();
14
- const validatedNativeProofs = new WeakSet();
15
- const validatedChildren = new WeakMap();
16
- const validatedReports = new WeakMap();
17
- const hash = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
8
+ import { runnerFailureValid, validateRunnerProof, validatedRunnerChild, hasValidatedNativeProof, hasValidatedBrowserProof, hasUnsupportedObservation, runnerLibraryInputs } from "./check-runner-proof.js";
9
+ export { candidateAbsencesValid, candidateLibraryInputsValid, runnerFailureValid, runnerEvidencePass, runnerProofArtifacts } from "./check-runner-proof.js";
18
10
  const absoluteFile = (value) => typeof value === "string" && value.length > 0 && value.length <= 4096 && !value.includes("\0") && path.isAbsolute(value);
19
- export function candidateAbsencesValid(value) { return Array.isArray(value) && value.length <= 64 && value.every(item => object(item) && absoluteFile(item.file) && item.path_state === "missing" && object(item.declaration) && absoluteFile(item.declaration.file) && hash(item.declaration.sha256)); }
20
- function relativeProofPath(value) { return typeof value === "string" && value.length > 0 && Buffer.byteLength(value) <= 4096 && !path.isAbsolute(value) && !value.includes("\\") && value.split("/").every(part => part !== "" && part !== "." && part !== ".."); }
21
- function proofArtifact(value) { return object(value) && relativeProofPath(value.relative_path) && typeof value.sha256 === "string" && /^[a-f0-9]{64}$/.test(value.sha256); }
22
- function browserProofShape(proof) {
23
- const attempt = proof.attempt, step = proof.step;
24
- return (proof.observation_contract === undefined || proof.observation_contract === "dd-tasks/browser-observation@2" && object(proof.target) && [proof.target.page_id, proof.target.context_id].every(value => boundedString(value, 256) && Boolean(value)) && Number.isSafeInteger(proof.window_epoch) && Number(proof.window_epoch) > 0)
25
- && proof.native_code === "PLAYWRIGHT_ACTION_INACTIVITY" && object(attempt) && boundedString(attempt.invocation_id, 4096) && boundedString(attempt.test_id, 4096) && Boolean(attempt.test_id)
26
- && [attempt.retry, attempt.repeat_each_index, attempt.worker_index].every(value => Number.isSafeInteger(value) && value >= 0)
27
- && object(step) && boundedString(step.id, 4096) && Boolean(step.id) && ["pw:api", "expect"].includes(String(step.category)) && step.file === proof.file && Number.isSafeInteger(step.line) && step.line > 0
28
- && proofArtifact(proof.worker_snapshot) && proofArtifact(proof.native_result);
29
- }
30
- export function runnerFailureValid(value) {
31
- if (!object(value) || typeof value.category !== "string" || !["assertion", "tooling", "unclassified"].includes(value.category))
32
- return false;
33
- if (value.diagnostic_artifact !== undefined && !proofArtifact(value.diagnostic_artifact))
34
- return false;
35
- for (const key of ["code", "message", "file", "test_id", "native_code", "classification_reason"])
36
- if (value[key] !== undefined && !boundedString(value[key], 4096))
37
- return false;
38
- if (value.stack !== undefined && !boundedString(value.stack, 16384) || value.origin !== undefined && (typeof value.origin !== "string" || !["candidate", "tooling", "unknown"].includes(value.origin)) || value.phase !== undefined && (typeof value.phase !== "string" || !["test", "setup", "teardown", "collection", "runner"].includes(value.phase)))
39
- return false;
40
- if (value.repair_evidence !== undefined) {
41
- const proof = value.repair_evidence;
42
- if (object(proof) && proof.rule === "candidate_test_runtime_error")
43
- return ["TypeError", "ReferenceError", "Error"].includes(String(proof.exception_type)) && (proof.exception_type !== "Error" || proof.phase === "test")
44
- && absoluteFile(proof.file) && hash(proof.file_sha256) && ["test", "setup", "collection"].includes(String(proof.phase))
45
- && [proof.line, proof.column].every(value => Number.isSafeInteger(value) && Number(value) > 0) && proofArtifact(proof.native_artifact);
46
- if (object(proof) && proof.rule === "candidate_migration_sql")
47
- return proof.native_code === "42601" && absoluteFile(proof.file) && hash(proof.file_sha256)
48
- && boundedString(proof.operation_id, 4096) && Boolean(proof.operation_id) && proofArtifact(proof.native_artifact) && object(proof.world)
49
- && boundedString(proof.world.invocation_id, 4096) && typeof proof.world.world_id === "string" && /^[a-f0-9]{32}$/.test(proof.world.world_id)
50
- && boundedString(proof.world.role, 128) && Boolean(proof.world.role) && hash(proof.world.migrations_sha256) && proof.world.phase === "migrating" && proof.world.setup_completed === false;
51
- if (object(proof) && proof.rule === "candidate_tool_diagnostic")
52
- return absoluteFile(proof.file) && proofArtifact(proof.native_artifact)
53
- && object(proof.tool) && { biome: "2.5.6", typescript: "5.9.3", docs: "1", "value-absence": "1" }[String(proof.tool.name)] === proof.tool.version
54
- && object(proof.step) && boundedString(proof.step.id, 256) && Boolean(proof.step.id) && Number.isSafeInteger(proof.step.ordinal) && proof.step.ordinal >= 0 && hash(proof.step.command_sha256)
55
- && boundedString(proof.diagnostic_code, 256) && (proof.tool.name === "biome" ? ["format", "assist/source/organizeImports"].includes(String(proof.diagnostic_code)) || /^lint\/[\w/-]+$/.test(String(proof.diagnostic_code)) : proof.tool.name === "typescript" ? /^TS\d+$/.test(String(proof.diagnostic_code)) : proof.tool.name === "docs" ? ["required_path_missing", "docs_contract"].includes(String(proof.diagnostic_code)) : proof.diagnostic_code === "value_absence")
56
- && (proof.path_state === "present" && hash(proof.file_sha256) || proof.path_state === "missing" && proof.tool.name === "docs" && proof.diagnostic_code === "required_path_missing" && proof.file_sha256 === null && object(proof.declaration) && absoluteFile(proof.declaration.file) && hash(proof.declaration.sha256));
57
- if (!object(proof) || !boundedString(proof.rule, 128) || !boundedString(proof.native_code, 128) || !boundedString(proof.file, 4096) || !object(proof.world))
58
- return false;
59
- const world = proof.world;
60
- if (world.schema_id !== "dd-tasks/test-world-ready@1" || !boundedString(world.invocation_id, 4096) || typeof world.world_id !== "string" || !/^[a-f0-9]{32}$/.test(world.world_id) || typeof world.migrations_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(world.migrations_sha256) || world.ready !== true || world.setup_completed !== true || !boundedString(world.role, 128) || !world.role)
61
- return false;
62
- if (proof.rule === "candidate_browser_action_inactivity" && !browserProofShape(proof))
63
- return false;
64
- if (!["candidate_sql_integrity", "candidate_browser_action_inactivity"].includes(String(proof.rule)))
65
- return false;
66
- }
67
- return true;
68
- }
69
11
  function candidateFile(root, file) {
70
12
  if (!root || !path.isAbsolute(file))
71
13
  return false;
@@ -90,7 +32,7 @@ function candidateFile(root, file) {
90
32
  }
91
33
  export function candidateFailureFiles(evidence, root) {
92
34
  return [...new Set((evidence?.failures ?? []).flatMap(failure => {
93
- const child = validatedChildren.get(failure);
35
+ const child = validatedRunnerChild(failure);
94
36
  if (child)
95
37
  return candidateFailureFiles({ ...child.evidence, failures: [child.failure] }, root);
96
38
  const proof = failure.repair_evidence;
@@ -103,6 +45,28 @@ export function candidateFailureFiles(evidence, root) {
103
45
  }
104
46
  }))];
105
47
  }
48
+ export function candidateLibraryInputs(evidence, root) {
49
+ const inputs = new Map();
50
+ for (const failure of evidence?.failures ?? []) {
51
+ const child = validatedRunnerChild(failure);
52
+ const candidates = child ? candidateLibraryInputs({ ...child.evidence, failures: [child.failure] }, root)
53
+ : failure.repair_evidence?.rule === "candidate_library_query_error" ? runnerLibraryInputs(failure.repair_evidence) ?? [] : [];
54
+ try {
55
+ if (candidates.some(input => !candidateFile(root, input.file) || crypto.createHash("sha256").update(readBoundedProofFile(input.file, 2 * 1024 * 1024)).digest("hex") !== input.sha256))
56
+ continue;
57
+ }
58
+ catch {
59
+ continue;
60
+ }
61
+ for (const input of candidates)
62
+ inputs.set(input.file, input.sha256);
63
+ }
64
+ return [...inputs].map(([file, sha256]) => ({ file, sha256 }));
65
+ }
66
+ /** Current execution/repair boundary only; historical readers retain captured proof. */
67
+ export function currentCandidateLibraryInputsValid(receipt, workspaceRoot) {
68
+ return isDeepStrictEqual(receipt.candidate_library_inputs ?? [], candidateLibraryInputs(receipt.runner_evidence, workspaceRoot));
69
+ }
106
70
  function absentCandidate(root, file) {
107
71
  if (!root || !absoluteFile(file))
108
72
  return false;
@@ -147,12 +111,12 @@ function absentCandidate(root, file) {
147
111
  }
148
112
  export function candidateFailureAbsences(evidence, root) {
149
113
  return (evidence?.failures ?? []).flatMap(failure => {
150
- const child = validatedChildren.get(failure);
114
+ const child = validatedRunnerChild(failure);
151
115
  if (child)
152
116
  return candidateFailureAbsences({ ...child.evidence, failures: [child.failure] }, root);
153
117
  try {
154
118
  const proof = failure.repair_evidence;
155
- if (proof?.rule !== "candidate_tool_diagnostic" || proof.path_state !== "missing" || !proof.declaration || !validatedNativeProofs.has(proof)
119
+ if (proof?.rule !== "candidate_tool_diagnostic" || proof.path_state !== "missing" || !proof.declaration || !hasValidatedNativeProof(proof)
156
120
  || !absentCandidate(root, proof.file) || !candidateFile(root, proof.declaration.file) || crypto.createHash("sha256").update(fs.readFileSync(proof.declaration.file)).digest("hex") !== proof.declaration.sha256)
157
121
  return [];
158
122
  return [{ file: proof.file, declaration: proof.declaration, path_state: "missing" }];
@@ -162,10 +126,10 @@ export function candidateFailureAbsences(evidence, root) {
162
126
  }
163
127
  });
164
128
  }
165
- function candidateFailure(failure, evidence, root, capturedFiles, capturedAbsences) {
166
- const child = validatedChildren.get(failure);
129
+ function candidateFailure(failure, evidence, root, capturedFiles, capturedAbsences, capturedLibraryInputs) {
130
+ const child = validatedRunnerChild(failure);
167
131
  if (child)
168
- return candidateFailure(child.failure, child.evidence, root, capturedFiles, capturedAbsences);
132
+ return candidateFailure(child.failure, child.evidence, root, capturedFiles, capturedAbsences, capturedLibraryInputs);
169
133
  const owned = (file) => {
170
134
  if (capturedFiles !== undefined)
171
135
  return capturedFiles.includes(file);
@@ -184,11 +148,16 @@ function candidateFailure(failure, evidence, root, capturedFiles, capturedAbsenc
184
148
  if (failure.category === "assertion")
185
149
  return failure.origin === undefined || failure.origin === "candidate" && failure.file !== undefined && owned(failure.file);
186
150
  const proof = failure.repair_evidence;
151
+ if (proof?.rule === "candidate_library_query_error") {
152
+ const required = runnerLibraryInputs(proof), captured = capturedFiles !== undefined ? capturedLibraryInputs : root ? candidateLibraryInputs({ ...evidence, failures: [failure] }, root) : undefined;
153
+ return hasValidatedNativeProof(proof) && failure.category === "unclassified" && failure.origin === "candidate" && failure.phase === "test" && failure.native_code === proof.exception_type
154
+ && failure.test_id === proof.test_id && failure.file === proof.file && owned(proof.file) && Boolean(required?.length && captured && required.every(input => captured.some(item => item.file === input.file && item.sha256 === input.sha256)));
155
+ }
187
156
  if (proof?.rule === "candidate_test_runtime_error")
188
- return validatedNativeProofs.has(proof) && failure.category === "unclassified" && failure.origin === "candidate"
157
+ return hasValidatedNativeProof(proof) && failure.category === "unclassified" && failure.origin === "candidate"
189
158
  && failure.phase === proof.phase && failure.native_code === proof.exception_type && failure.file === proof.file && owned(proof.file);
190
159
  if (proof?.rule === "candidate_migration_sql" || proof?.rule === "candidate_tool_diagnostic") {
191
- if (!validatedNativeProofs.has(proof) || failure.category !== "unclassified" || failure.origin !== "candidate" || failure.file !== proof.file || !["setup", "collection", "test"].includes(failure.phase ?? ""))
160
+ if (!hasValidatedNativeProof(proof) || failure.category !== "unclassified" || failure.origin !== "candidate" || failure.file !== proof.file || !["setup", "collection", "test"].includes(failure.phase ?? ""))
192
161
  return false;
193
162
  if (proof.rule === "candidate_migration_sql")
194
163
  return failure.phase === "setup" && failure.native_code === "42601" && proof.world.invocation_id === evidence.binding.invocation_id && owned(proof.file);
@@ -197,7 +166,7 @@ function candidateFailure(failure, evidence, root, capturedFiles, capturedAbsenc
197
166
  return proof.path_state === "missing" ? Boolean(proof.declaration && (capturedAbsences ?? (root ? candidateFailureAbsences(evidence, root) : [])).some(item => item.file === proof.file && isDeepStrictEqual(item.declaration, proof.declaration))) : owned(proof.file);
198
167
  }
199
168
  if (proof?.rule === "candidate_browser_action_inactivity")
200
- return validatedBrowserProofs.has(proof) && failure.category === "unclassified" && failure.phase === "test" && failure.origin === "candidate" && failure.native_code === proof.native_code && failure.file === proof.file && failure.test_id === proof.attempt.test_id && proof.attempt.invocation_id === evidence.binding.invocation_id && owned(proof.file);
169
+ return hasValidatedBrowserProof(proof) && failure.category === "unclassified" && failure.phase === "test" && failure.origin === "candidate" && failure.native_code === proof.native_code && failure.file === proof.file && failure.test_id === proof.attempt.test_id && proof.attempt.invocation_id === evidence.binding.invocation_id && owned(proof.file);
201
170
  return failure.category === "unclassified" && failure.origin === "candidate" && ["test", "setup"].includes(failure.phase ?? "") && proof?.rule === "candidate_sql_integrity" && ["23502", "23503", "23505", "23514"].includes(proof.native_code) && failure.native_code === proof.native_code && failure.file === proof.file && proof.world.invocation_id === evidence.binding.invocation_id && owned(proof.file);
202
171
  }
203
172
  export function runnerObservationDurations(environment) {
@@ -278,48 +247,6 @@ function readBoundedProofFile(file, limit) {
278
247
  fs.closeSync(fd);
279
248
  }
280
249
  }
281
- function readProofArtifact(root, ref, limit = 256 * 1024) {
282
- let current = root;
283
- if (fs.lstatSync(current).isSymbolicLink())
284
- throw new Error("Symlink evidence root");
285
- for (const part of ref.relative_path.split("/")) {
286
- current = path.join(current, part);
287
- if (fs.lstatSync(current).isSymbolicLink())
288
- throw new Error("Symlink proof artifact");
289
- }
290
- const bytes = readBoundedProofFile(current, limit);
291
- if (crypto.createHash("sha256").update(bytes).digest("hex") !== ref.sha256)
292
- throw new Error("Browser proof hash differs");
293
- const value = JSON.parse(bytes.toString("utf8"));
294
- if (!object(value))
295
- throw new Error("Invalid proof object");
296
- return value;
297
- }
298
- function matches(actual, expected) { return object(actual) && Object.entries(expected).every(([key, value]) => actual[key] === value); }
299
- function validateBrowserArtifacts(root, proof, binding) {
300
- const snapshot = readProofArtifact(root, proof.worker_snapshot), native = readProofArtifact(root, proof.native_result);
301
- if (proof.observation_contract !== undefined && (proof.observation_contract !== "dd-tasks/browser-observation@2" || snapshot.observation_contract !== proof.observation_contract || native.observation_contract !== proof.observation_contract
302
- || !isDeepStrictEqual(snapshot.target, proof.target) || !isDeepStrictEqual(native.target, proof.target) || snapshot.window_epoch !== proof.window_epoch || native.window_epoch !== proof.window_epoch))
303
- return false;
304
- if (!Array.isArray(native.errors) || native.errors.length < 1 || native.errors.length > 64 || native.errors.some(error => !boundedString(error, 4096) || !/^(?:Error: )?(?:[A-Za-z][\w.]*: )?Test timeout of [1-9]\d*ms exceeded\.(?:\nCall log:[\s\S]*)?$/.test(stripVTControlCharacters(String(error)))))
305
- return false;
306
- if (!matches(snapshot.binding, binding) || !matches(native.binding, binding) || !matches(snapshot.attempt, proof.attempt) || !matches(native.attempt, proof.attempt)
307
- || !matches(snapshot.step, proof.step) || !matches(native.step, proof.step) || !matches(snapshot.world, proof.world) || proof.world.invocation_id !== binding.invocation_id
308
- || native.status !== "timedOut" || native.phase !== "test" || native.file !== proof.file || snapshot.phase !== "body" || snapshot.observation_gap !== false || !matches(snapshot.browser, { connected: true, page_closed: false, crashed: false })
309
- || !matches(snapshot.world_health, { api: true, web: true, db: true }))
310
- return false;
311
- const quiet = snapshot.quiet_ms, last = Date.parse(String(snapshot.last_progress_at)), deadline = Date.parse(String(snapshot.deadline_at)), observed = Date.parse(String(snapshot.observed_at)), health = Date.parse(String(snapshot.health_observed_at)), ended = Date.parse(String(native.ended_at));
312
- return Number.isSafeInteger(quiet) && quiet > 0 && [last, deadline, observed, health, ended].every(Number.isFinite) && Math.abs(deadline - last - quiet) <= 2 && observed >= last && observed <= deadline && health <= observed && health >= last && deadline - health <= Math.min(1000, quiet / 4) && ended >= deadline && ended <= Date.now() && ended - deadline <= 5000;
313
- }
314
- export function runnerProofArtifacts(evidence) {
315
- return [...new Set([...(evidence?.failures ?? []).flatMap(failure => {
316
- const child = validatedChildren.get(failure);
317
- if (child && failure.child_evidence)
318
- return [failure.child_evidence.report.relative_path, ...runnerProofArtifacts(child.evidence).map(file => path.posix.join(path.posix.dirname(failure.child_evidence.report.relative_path), file))];
319
- const proof = failure.repair_evidence;
320
- return [...(failure.diagnostic_artifact ? [failure.diagnostic_artifact.relative_path] : []), ...(proof?.rule === "candidate_browser_action_inactivity" ? [proof.worker_snapshot.relative_path, proof.native_result.relative_path] : proof && "native_artifact" in proof ? [proof.native_artifact.relative_path] : [])];
321
- }), ...(evidence ? validatedReports.get(evidence) ?? [] : []).flatMap(child => [child.report.relative_path, ...runnerProofArtifacts(child.evidence).map(file => path.posix.join(path.posix.dirname(child.report.relative_path), file))])])];
322
- }
323
250
  export function runnerContractsValid(value) {
324
251
  if (value === undefined)
325
252
  return true;
@@ -332,211 +259,26 @@ export function runnerContractsValid(value) {
332
259
  return alias.startsWith("@check/") && ["dd-flow/check-runner-evidence@1", "dd-flow/check-runner-evidence@2"].includes(String(contract.schema_id)) && typeof contract.path === "string" && contract.path.length > 0 && contract.path.split("/")[0] !== "engine-outcome.json" && !contract.path.includes("\\") && !path.isAbsolute(contract.path) && !contract.path.split("/").some(part => part === ".." || part === "" || part === ".");
333
260
  });
334
261
  }
335
- function diagnostic(context, value) {
336
- // Diagnostics are locators, never another copy of untrusted report content.
337
- if (context.diagnostics.length >= 64 || context.bytes + Buffer.byteLength(JSON.stringify(value)) > 32 * 1024 - 64) {
338
- context.truncated = true;
339
- return;
340
- }
341
- context.diagnostics.push(value);
342
- context.bytes += Buffer.byteLength(JSON.stringify(value));
343
- }
344
262
  export function readRunnerEvidenceDetailed(root, contract, binding) {
345
- const context = { diagnostics: [], bytes: 0, truncated: false };
346
- const evidence = readRunnerEvidenceInternal(root, contract, binding, 0, { reports: 0 }, context, "");
347
- return { evidence, diagnostics: context.diagnostics, ...(context.truncated ? { diagnostics_truncated: true } : {}) };
348
- }
349
- export function readRunnerEvidence(root, contract, binding, depth = 0, budget = { reports: 0 }) {
350
- return readRunnerEvidenceInternal(root, contract, binding, depth, budget, { diagnostics: [], bytes: 0, truncated: false }, "");
351
- }
352
- function readRunnerEvidenceInternal(root, contract, binding, depth, budget, context, prefix) {
353
- const report = relativeProofPath(contract.path) ? path.posix.join(prefix, contract.path) : "<invalid-report-path>";
354
- const count = context.diagnostics.length;
355
- let accepted = false;
356
- let failureReason = "invalid_shape";
357
- const invalid = () => { if (context.diagnostics.length === count)
358
- diagnostic(context, { reason: failureReason, report }); return null; };
359
- try {
360
- if (!relativeProofPath(contract.path))
361
- return invalid();
362
- if (depth > 4 || ++budget.reports > 64)
363
- return null;
263
+ return validateRunnerProof(contract, binding, relative => {
364
264
  let current = root;
365
265
  if (!fs.lstatSync(current).isDirectory() || fs.lstatSync(current).isSymbolicLink())
366
- return null;
367
- for (const part of contract.path.split(/[\\/]/)) {
266
+ throw new Error("Unsafe proof root");
267
+ for (const part of relative.split("/")) {
368
268
  current = path.join(current, part);
369
269
  if (fs.lstatSync(current).isSymbolicLink())
370
- return null;
371
- }
372
- const bytes = readBoundedProofFile(current, 1024 * 1024);
373
- failureReason = "invalid_json";
374
- const evidence = JSON.parse(bytes.toString("utf8"));
375
- failureReason = "invalid_shape";
376
- if (!object(evidence))
377
- return invalid();
378
- if (!["dd-flow/check-runner-evidence@1", "dd-flow/check-runner-evidence@2"].includes(contract.schema_id))
379
- return null;
380
- const partial = evidence.failure_projection;
381
- if (partial !== undefined && (evidence.schema_id !== "dd-flow/check-runner-evidence@2" || !object(partial) || partial.state !== "partial" || !["count_bound", "byte_bound", "interrupt"].includes(partial.reason)
382
- || ![partial.represented, partial.omitted].every(value => Number.isSafeInteger(value) && value >= 0)))
383
- return null;
384
- if (evidence.schema_id !== contract.schema_id) {
385
- diagnostic(context, { reason: "invalid_shape", report, expected: contract.schema_id, observed: ["dd-flow/check-runner-evidence@1", "dd-flow/check-runner-evidence@2"].includes(evidence.schema_id) ? evidence.schema_id : "unsupported" });
386
- return null;
387
- }
388
- if (!evidence.binding || Object.entries(binding).some(([key, value]) => evidence.binding[key] !== value)) {
389
- diagnostic(context, { reason: "binding_mismatch", report });
390
- return null;
391
- }
392
- if (typeof evidence.runner?.name !== "string" || !evidence.runner.name || typeof evidence.runner.version !== "string" || !evidence.runner.version || typeof evidence.completed !== "boolean" || !evidence.counts
393
- || ![evidence.counts.passed, evidence.counts.failed, evidence.counts.skipped].every(n => Number.isSafeInteger(n) && n >= 0)
394
- || !Array.isArray(evidence.failures) || evidence.failures.length > 64 || evidence.failures.some(f => !runnerFailureValid(f))
395
- || !partial && evidence.counts.failed > evidence.failures.length || evidence.observation_unknown !== undefined && typeof evidence.observation_unknown !== "boolean")
396
- return null;
397
- const children = new Map();
398
- const consumed = new Map();
399
- if (evidence.steps !== undefined) {
400
- if (!Array.isArray(evidence.steps) || evidence.steps.length > 64)
401
- return null;
402
- const ids = new Set(), invocations = new Set();
403
- let stopped = false;
404
- for (const [ordinal, step] of evidence.steps.entries()) {
405
- if (!step || !boundedString(step.id, 256) || !step.id || ids.has(step.id) || step.ordinal !== ordinal || !hash(step.command_sha256) || !["passed", "failed", "unreached"].includes(step.state))
406
- return null;
407
- ids.add(step.id);
408
- if (step.state === "unreached") {
409
- if (!stopped || step.binding !== undefined || step.report !== undefined || step.exit_code !== undefined)
410
- return null;
411
- continue;
412
- }
413
- if (stopped)
414
- return null;
415
- if (step.state === "failed")
416
- stopped = true;
417
- if (!step.binding || !proofArtifact(step.report) || !Number.isSafeInteger(step.exit_code) || step.state === "passed" && step.exit_code !== 0 || step.state === "failed" && step.exit_code === 0 || invocations.has(step.binding.invocation_id) || step.binding.invocation_id === binding.invocation_id)
418
- return null;
419
- if (step.binding.invocation_id !== `${binding.invocation_id}:step:${step.id}` || step.binding.check_id !== `${binding.check_id}/${step.id}` || step.binding.verification_epoch !== binding.verification_epoch || step.binding.input_hash !== crypto.createHash("sha256").update(JSON.stringify([binding.input_hash, step.command_sha256])).digest("hex"))
420
- return null;
421
- if (crypto.createHash("sha256").update(step.binding.command).digest("hex") !== step.command_sha256)
422
- return null;
423
- invocations.add(step.binding.invocation_id);
424
- failureReason = "artifact_mismatch";
425
- readProofArtifact(root, step.report, 1024 * 1024);
426
- failureReason = "invalid_shape";
427
- const child = readRunnerEvidenceInternal(path.join(root, path.dirname(step.report.relative_path)), { schema_id: contract.schema_id, path: path.basename(step.report.relative_path) }, step.binding, depth + 1, budget, context, path.posix.join(prefix, path.posix.dirname(step.report.relative_path)));
428
- if (!child || step.state === "passed" && (child.counts.failed !== 0 || child.failures.length !== 0) || step.state === "failed" && !child.failures.length && child.completed && !child.observation_unknown)
429
- return null;
430
- if (!child.completed || child.observation_unknown)
431
- stopped = true;
432
- children.set(step.id, child);
433
- }
434
- for (const key of ["passed", "failed", "skipped"])
435
- if (evidence.counts[key] !== [...children.values()].reduce((total, child) => total + child.counts[key], 0))
436
- return null;
437
- }
438
- for (const failure of evidence.failures) {
439
- failureReason = "artifact_mismatch";
440
- if (failure.child_evidence) {
441
- const ref = failure.child_evidence, step = evidence.steps?.find(item => item.id === ref.step_id), child = children.get(ref.step_id);
442
- if (!step || !child || !isDeepStrictEqual(ref.binding, step.binding) || !isDeepStrictEqual(ref.report, step.report))
443
- return null;
444
- const original = { ...failure };
445
- delete original.child_evidence;
446
- const used = consumed.get(child) ?? new Set();
447
- consumed.set(child, used);
448
- const index = child.failures.findIndex((item, index) => { const native = { ...item }; delete native.child_evidence; return !used.has(index) && isDeepStrictEqual(native, original); });
449
- if (index < 0)
450
- return null;
451
- used.add(index);
452
- const matched = child.failures[index];
453
- if (!matched.child_evidence && matched.repair_evidence?.rule === "candidate_tool_diagnostic" && !isDeepStrictEqual(matched.repair_evidence.step, { id: step.id, ordinal: step.ordinal, command_sha256: step.command_sha256 }))
454
- return null;
455
- validatedChildren.set(failure, { failure: matched, evidence: child });
456
- continue;
457
- }
458
- if (failure.diagnostic_artifact && !matches(readProofArtifact(root, failure.diagnostic_artifact, 2 * 1024 * 1024).binding, binding))
459
- return null;
460
- const proof = failure.repair_evidence;
461
- if (proof?.rule === "candidate_migration_sql" || proof?.rule === "candidate_tool_diagnostic" || proof?.rule === "candidate_test_runtime_error") {
462
- const native = readProofArtifact(root, proof.native_artifact, 2 * 1024 * 1024), facts = Object.fromEntries(Object.entries(proof).filter(([key]) => key !== "native_artifact"));
463
- if (!isDeepStrictEqual(native.binding, binding) || Object.entries(facts).some(([key, value]) => !isDeepStrictEqual(native[key], value)))
464
- return null;
465
- if (proof.rule === "candidate_test_runtime_error") {
466
- failureReason = "attribution_unproven";
467
- if (evidence.schema_id !== "dd-flow/check-runner-evidence@2" || native.executing_file !== proof.file || native.exception_name !== proof.exception_type
468
- || !isDeepStrictEqual(native.top_frame, { file: proof.file, line: proof.line, column: proof.column }) || !boundedString(native.stack, 16384) || !String(native.stack).startsWith(`${proof.exception_type}:`))
469
- return null;
470
- const firstFrame = String(native.stack).split("\n").find(line => /^\s*at\s/.test(line));
471
- const frame = firstFrame?.match(/^\s*at\s+(?:.*?\()?((?:file:\/\/)?\/[^\n]+):(\d+):(\d+)\)?$/);
472
- if (!frame || (frame[1].startsWith("file://") ? fileURLToPath(frame[1]) : frame[1]) !== proof.file || Number(frame[2]) !== proof.line || Number(frame[3]) !== proof.column)
473
- return null;
474
- }
475
- validatedNativeProofs.add(proof);
476
- }
477
- if (proof?.rule === "candidate_browser_action_inactivity") {
478
- if (proof.observation_contract === undefined && evidence.schema_id === "dd-flow/check-runner-evidence@1") {
479
- // Legacy diagnostics remain readable; they do not prove the new window/target guarantee.
480
- if (!validateBrowserArtifacts(root, proof, binding))
481
- return null;
482
- evidence.completed = false;
483
- unsupportedObservationContracts.add(evidence);
484
- continue;
485
- }
486
- if (proof.observation_contract !== "dd-tasks/browser-observation@2" || !validateBrowserArtifacts(root, proof, binding))
487
- return null;
488
- validatedBrowserProofs.add(proof);
489
- }
490
- }
491
- failureReason = "invalid_shape";
492
- const represented = [...consumed.values()].reduce((total, used) => total + used.size, 0), childFailures = [...children.values()].reduce((total, child) => total + child.failures.length, 0);
493
- if (partial) {
494
- if (children.size ? partial.represented !== represented || partial.omitted !== childFailures - represented : partial.represented !== evidence.failures.filter(failure => failure.code !== "report_truncated").length)
495
- return null;
496
- evidence.completed = false;
270
+ throw new Error("Symlink proof artifact");
497
271
  }
498
- else if (represented !== childFailures)
499
- return null;
500
- // Valid but incomplete native observation is diagnostic evidence, not a
501
- // malformed schema. Preserve the primary failure and references through
502
- // every composite, including a parent that incorrectly claims completion.
503
- if ([...children.values()].some(child => !child.completed || child.observation_unknown)) {
504
- evidence.completed = false;
505
- if ([...children.values()].some(child => unsupportedObservationContracts.has(child)))
506
- unsupportedObservationContracts.add(evidence);
507
- if (evidence.schema_id === "dd-flow/check-runner-evidence@1" && [...children.values()].some(child => !child.completed && !unsupportedObservationContracts.has(child)) || [...children.values()].some(child => child.observation_unknown))
508
- evidence.observation_unknown = true;
509
- }
510
- validatedReports.set(evidence, (evidence.steps ?? []).flatMap(step => step.report && children.has(step.id) ? [{ report: step.report, evidence: children.get(step.id) }] : []));
511
- if (unsupportedObservationContracts.has(evidence))
512
- diagnostic(context, { reason: "unsupported_observation_contract", report, expected: "dd-tasks/browser-observation@2", observed: "legacy" });
513
- else if (!evidence.completed || evidence.observation_unknown)
514
- diagnostic(context, { reason: "observation_incomplete", report });
515
- for (const failure of evidence.failures)
516
- if (failure.origin === "unknown" || failure.category === "unclassified" && !failure.repair_evidence) {
517
- diagnostic(context, { reason: "attribution_unproven", report });
518
- break;
519
- }
520
- accepted = true;
521
- return evidence;
522
- }
523
- catch (error) {
524
- if (error.code === "ENOENT")
525
- failureReason = "missing";
526
- return invalid();
527
- }
528
- finally {
529
- if (!accepted && context.diagnostics.length === count)
530
- diagnostic(context, { reason: failureReason, report });
531
- }
272
+ return readBoundedProofFile(current, 2 * 1024 * 1024);
273
+ });
532
274
  }
533
- export function runnerEvidencePass(evidence) { return Boolean(evidence?.completed && !evidence.failure_projection && !evidence.observation_unknown && evidence.counts.failed === 0 && evidence.failures.length === 0); }
275
+ export function readRunnerEvidence(root, contract, binding) { return readRunnerEvidenceDetailed(root, contract, binding).evidence; }
534
276
  export function checkRepairDisposition(receipt) {
535
277
  if (receipt.fingerprint_scope != null && receipt.fingerprint_scope !== "check_inputs")
536
278
  return "tooling_blocker";
537
279
  if (receipt.status === "running" || receipt.status === "aborted" || receipt.runner_evidence?.observation_unknown)
538
280
  return "settlement_required";
539
- if (receipt.runner_evidence && unsupportedObservationContracts.has(receipt.runner_evidence) || receipt.runner_evidence?.failure_projection || receipt.runner_evidence?.schema_id === "dd-flow/check-runner-evidence@2" && !receipt.runner_evidence.completed)
281
+ if (receipt.runner_evidence && hasUnsupportedObservation(receipt.runner_evidence) || receipt.runner_evidence?.failure_projection || receipt.runner_evidence?.schema_id === "dd-flow/check-runner-evidence@2" && !receipt.runner_evidence.completed)
540
282
  return "tooling_blocker";
541
283
  if (receipt.status === "passed")
542
284
  return "passed";
@@ -551,12 +293,15 @@ export function checkRepairDisposition(receipt) {
551
293
  return "tooling_blocker";
552
294
  if (!evidence.completed)
553
295
  return "unclassified";
554
- if (evidence.failures.some(f => !runnerFailureValid(f) || !candidateFailure(f, evidence, receipt.candidate_root, receipt.candidate_files, receipt.candidate_absences)))
296
+ if (evidence.failures.some(f => !runnerFailureValid(f) || !candidateFailure(f, evidence, receipt.candidate_root, receipt.candidate_files, receipt.candidate_absences, receipt.candidate_library_inputs)))
555
297
  return "unclassified";
556
298
  return "source_repair";
557
299
  }
558
- export function assertSourceRepairEligible(receipts) {
559
- const blockers = receipts.filter(receipt => checkRepairDisposition(receipt) !== "source_repair");
560
- if (!receipts.length || blockers.length)
561
- throw new AppError("check_gate_blocked", "Checks require tooling diagnosis or settlement before product repair; retained nonzero is not a candidate-source verdict", 2, { failures: receipts, dispositions: receipts.map(receipt => ({ check_receipt_id: receipt.id, disposition: checkRepairDisposition(receipt) })), outcome: "blocked", recoverable: false, instruction: "Inspect retained runner evidence. Do not change product code to repair tooling, unknown observation or unclassified failure. Retry only after establishing and correcting the cause." });
300
+ export function assertSourceRepairEligible(receipts, workspaceRoot) {
301
+ const dispositions = receipts.map(receipt => {
302
+ const drift = workspaceRoot !== undefined && !currentCandidateLibraryInputsValid(receipt, workspaceRoot);
303
+ return { check_receipt_id: receipt.id, disposition: drift ? "tooling_blocker" : checkRepairDisposition(receipt), ...(drift ? { reason: "candidate_library_inputs_changed" } : {}) };
304
+ });
305
+ if (!receipts.length || dispositions.some(item => item.disposition !== "source_repair"))
306
+ throw new AppError("check_gate_blocked", "Checks require tooling diagnosis or settlement before product repair; retained nonzero is not a candidate-source verdict", 2, { failures: receipts, dispositions, outcome: "blocked", recoverable: false, instruction: "Inspect retained runner evidence. Do not change product code to repair tooling, unknown observation or unclassified failure. Retry only after establishing and correcting the cause." });
562
307
  }