@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
@@ -0,0 +1,373 @@
1
+ import crypto from "node:crypto";
2
+ import path from "node:path";
3
+ import { isDeepStrictEqual, stripVTControlCharacters } from "node:util";
4
+ import { fileURLToPath } from "node:url";
5
+ export const RUNNER_PROOF_CONTRACT = "dd-flow/check-runner-proof@1";
6
+ function object(value) { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }
7
+ function boundedString(value, bytes) { return typeof value === "string" && Buffer.byteLength(value) <= bytes; }
8
+ const validatedBrowserProofs = new WeakSet();
9
+ const unsupportedObservationContracts = new WeakSet();
10
+ const validatedNativeProofs = new WeakSet();
11
+ const validatedLibraryInputs = new WeakMap();
12
+ const validatedChildren = new WeakMap();
13
+ const validatedReports = new WeakMap();
14
+ const hash = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
15
+ const absoluteFile = (value) => typeof value === "string" && value.length > 0 && value.length <= 4096 && !value.includes("\0") && path.isAbsolute(value);
16
+ 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)); }
17
+ export function candidateLibraryInputsValid(value) { return Array.isArray(value) && value.length <= 64 && new Set(value.map(item => object(item) ? item.file : null)).size === value.length && value.every(item => object(item) && absoluteFile(item.file) && hash(item.sha256)); }
18
+ 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 !== ".."); }
19
+ function proofArtifact(value) { return object(value) && relativeProofPath(value.relative_path) && typeof value.sha256 === "string" && /^[a-f0-9]{64}$/.test(value.sha256); }
20
+ function browserProofShape(proof) {
21
+ const attempt = proof.attempt, step = proof.step;
22
+ 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)
23
+ && proof.native_code === "PLAYWRIGHT_ACTION_INACTIVITY" && object(attempt) && boundedString(attempt.invocation_id, 4096) && boundedString(attempt.test_id, 4096) && Boolean(attempt.test_id)
24
+ && [attempt.retry, attempt.repeat_each_index, attempt.worker_index].every(value => Number.isSafeInteger(value) && value >= 0)
25
+ && 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
26
+ && proofArtifact(proof.worker_snapshot) && proofArtifact(proof.native_result);
27
+ }
28
+ export function runnerFailureValid(value) {
29
+ if (!object(value) || typeof value.category !== "string" || !["assertion", "tooling", "unclassified"].includes(value.category))
30
+ return false;
31
+ if (value.diagnostic_artifact !== undefined && !proofArtifact(value.diagnostic_artifact))
32
+ return false;
33
+ for (const key of ["code", "message", "file", "test_id", "native_code", "classification_reason"])
34
+ if (value[key] !== undefined && !boundedString(value[key], 4096))
35
+ return false;
36
+ 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)))
37
+ return false;
38
+ if (value.repair_evidence !== undefined) {
39
+ const proof = value.repair_evidence;
40
+ if (object(proof) && proof.rule === "candidate_library_query_error")
41
+ return proof.exception_type === "TestingLibraryElementError" && proof.phase === "test"
42
+ && boundedString(proof.test_id, 4096) && Boolean(proof.test_id) && absoluteFile(proof.file) && hash(proof.file_sha256)
43
+ && [proof.line, proof.column].every(n => Number.isSafeInteger(n) && Number(n) > 0) && object(proof.library)
44
+ && proof.library.name === "@testing-library/dom" && proof.library.version === "10.4.1" && hash(proof.library.package_json_sha256) && hash(proof.library.lockfile_sha256)
45
+ && Array.isArray(proof.helper_frames) && proof.helper_frames.length >= 2 && proof.helper_frames.length <= 8
46
+ && proof.helper_frames.every(frame => object(frame) && absoluteFile(frame.file) && hash(frame.file_sha256) && [frame.line, frame.column].every(n => Number.isSafeInteger(n) && Number(n) > 0)) && proofArtifact(proof.native_artifact);
47
+ if (object(proof) && proof.rule === "candidate_test_runtime_error")
48
+ return ["TypeError", "ReferenceError", "Error"].includes(String(proof.exception_type)) && (proof.exception_type !== "Error" || proof.phase === "test")
49
+ && absoluteFile(proof.file) && hash(proof.file_sha256) && ["test", "setup", "collection"].includes(String(proof.phase))
50
+ && [proof.line, proof.column].every(value => Number.isSafeInteger(value) && Number(value) > 0) && proofArtifact(proof.native_artifact);
51
+ if (object(proof) && proof.rule === "candidate_migration_sql")
52
+ return proof.native_code === "42601" && absoluteFile(proof.file) && hash(proof.file_sha256)
53
+ && boundedString(proof.operation_id, 4096) && Boolean(proof.operation_id) && proofArtifact(proof.native_artifact) && object(proof.world)
54
+ && boundedString(proof.world.invocation_id, 4096) && typeof proof.world.world_id === "string" && /^[a-f0-9]{32}$/.test(proof.world.world_id)
55
+ && boundedString(proof.world.role, 128) && Boolean(proof.world.role) && hash(proof.world.migrations_sha256) && proof.world.phase === "migrating" && proof.world.setup_completed === false;
56
+ if (object(proof) && proof.rule === "candidate_tool_diagnostic")
57
+ return absoluteFile(proof.file) && proofArtifact(proof.native_artifact)
58
+ && object(proof.tool) && { biome: "2.5.6", typescript: "5.9.3", docs: "1", "value-absence": "1" }[String(proof.tool.name)] === proof.tool.version
59
+ && 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)
60
+ && 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")
61
+ && (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));
62
+ if (!object(proof) || !boundedString(proof.rule, 128) || !boundedString(proof.native_code, 128) || !boundedString(proof.file, 4096) || !object(proof.world))
63
+ return false;
64
+ const world = proof.world;
65
+ 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)
66
+ return false;
67
+ if (proof.rule === "candidate_browser_action_inactivity" && !browserProofShape(proof))
68
+ return false;
69
+ if (!["candidate_sql_integrity", "candidate_browser_action_inactivity"].includes(String(proof.rule)))
70
+ return false;
71
+ }
72
+ return true;
73
+ }
74
+ function readBytes(context, relative, limit) {
75
+ if (!relativeProofPath(relative))
76
+ throw new Error("Unsafe proof path");
77
+ const bytes = context.readArtifact(relative);
78
+ if (!Buffer.isBuffer(bytes) || bytes.length > limit)
79
+ throw new Error("Evidence is not bounded bytes");
80
+ context.consumed += bytes.length;
81
+ if (context.consumed > 128 * 1024 * 1024)
82
+ throw Object.assign(new Error("Proof byte budget exceeded"), { code: "proof_budget_exceeded" });
83
+ return bytes;
84
+ }
85
+ function readProofArtifact(root, ref, context, limit = 2 * 1024 * 1024) {
86
+ const bytes = readBytes(context, path.posix.join(root, ref.relative_path), limit);
87
+ if (crypto.createHash("sha256").update(bytes).digest("hex") !== ref.sha256)
88
+ throw new Error("Proof hash differs");
89
+ const value = JSON.parse(bytes.toString("utf8"));
90
+ if (!object(value))
91
+ throw new Error("Invalid proof object");
92
+ return value;
93
+ }
94
+ // Exact known helper bytes keep this a library-specific proof, not permission
95
+ // to skip arbitrary dependency frames. New versions need explicit evidence.
96
+ const queryInputHashes = {
97
+ "dist/config.js": "3627f6265cccd26604777602d7249788c2347a273bdd1cae41719c790f2c5a7a",
98
+ "dist/query-helpers.js": "901f513f2ac901992d7c5002e644aef69286edbcd7d050d3ee7dd6aaaebfc9cc",
99
+ "package.json": "4e62e2aa8b974b6a5d2d532396f7610d1b4e806d9d2aaef3c84221f0d2fab06d",
100
+ };
101
+ function validateLibraryQuery(root, proof, failure, evidence, context) {
102
+ const native = readProofArtifact(root, proof.native_artifact, context, 256 * 1024);
103
+ const facts = Object.fromEntries(Object.entries(proof).filter(([key]) => key !== "native_artifact"));
104
+ if (evidence.schema_id !== "dd-flow/check-runner-evidence@2" || !isDeepStrictEqual(native.binding, evidence.binding)
105
+ || Object.entries(facts).some(([key, value]) => !isDeepStrictEqual(native[key], value)) || native.executing_file !== proof.file || native.exception_name !== proof.exception_type
106
+ || failure.category !== "unclassified" || failure.origin !== "candidate" || failure.native_code !== proof.exception_type || failure.phase !== "test" || failure.test_id !== proof.test_id || failure.file !== proof.file
107
+ || native.stack_complete !== true || native.frames_complete !== true || !Array.isArray(native.frame_lines) || native.frame_lines.length !== proof.helper_frames.length + 1
108
+ || native.frame_lines.some(line => !boundedString(line, 4096)) || Buffer.byteLength(JSON.stringify(native.frame_lines)) > 128 * 1024)
109
+ return false;
110
+ const expectedFrames = [...proof.helper_frames, { file: proof.file, line: proof.line, column: proof.column }];
111
+ for (const [index, line] of native.frame_lines.entries()) {
112
+ const match = String(line).match(/^\s*at (?:.*?\()?((?:file:\/\/)?\/[^\n]+):(\d+):(\d+)\)?$/), expected = expectedFrames[index];
113
+ if (!match || (match[1].startsWith("file:") ? fileURLToPath(match[1]) : match[1]) !== expected.file || Number(match[2]) !== expected.line || Number(match[3]) !== expected.column)
114
+ return false;
115
+ }
116
+ const packageRoot = path.dirname(path.dirname(proof.helper_frames[0].file));
117
+ if (proof.helper_frames[0].file !== path.join(packageRoot, "dist/config.js") || !String(native.frame_lines[0]).includes("getElementError")
118
+ || proof.helper_frames.slice(1).some(frame => frame.file !== path.join(packageRoot, "dist/query-helpers.js")))
119
+ return false;
120
+ if (!Array.isArray(native.library_inputs) || native.library_inputs.length !== 4)
121
+ return false;
122
+ const inputs = [];
123
+ let packageJson;
124
+ for (const [index, value] of native.library_inputs.entries()) {
125
+ if (!object(value) || !absoluteFile(value.file) || !hash(value.sha256) || !proofArtifact(value.artifact))
126
+ return false;
127
+ const expectedName = Object.keys(queryInputHashes)[index];
128
+ if (expectedName ? value.file !== path.join(packageRoot, expectedName) || value.sha256 !== queryInputHashes[expectedName]
129
+ : path.basename(value.file) !== "pnpm-lock.yaml" || value.sha256 !== proof.library.lockfile_sha256)
130
+ return false;
131
+ const bytes = readBytes(context, path.posix.join(root, value.artifact.relative_path), 2 * 1024 * 1024);
132
+ if (crypto.createHash("sha256").update(bytes).digest("hex") !== value.sha256 || value.artifact.sha256 !== value.sha256)
133
+ return false;
134
+ if (index === 2)
135
+ packageJson = JSON.parse(bytes.toString("utf8"));
136
+ if (index === 3 && !bytes.toString("utf8").includes("@testing-library/dom@10.4.1"))
137
+ return false;
138
+ inputs.push({ file: value.file, sha256: value.sha256, artifact: value.artifact });
139
+ }
140
+ if (packageJson?.name !== proof.library.name || packageJson.version !== proof.library.version || inputs[2].sha256 !== proof.library.package_json_sha256
141
+ || proof.helper_frames.some(frame => inputs.find(input => input.file === frame.file)?.sha256 !== frame.file_sha256))
142
+ return false;
143
+ const candidateRoot = path.dirname(inputs[3].file);
144
+ if ([proof.file, ...inputs.map(input => input.file)].some(file => { const relative = path.relative(candidateRoot, file); return !relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative); }))
145
+ return false;
146
+ validatedLibraryInputs.set(proof, inputs);
147
+ validatedNativeProofs.add(proof);
148
+ return true;
149
+ }
150
+ function matches(actual, expected) { return object(actual) && Object.entries(expected).every(([key, value]) => actual[key] === value); }
151
+ function validateBrowserArtifacts(root, proof, binding, context) {
152
+ const snapshot = readProofArtifact(root, proof.worker_snapshot, context), native = readProofArtifact(root, proof.native_result, context);
153
+ 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
154
+ || !isDeepStrictEqual(snapshot.target, proof.target) || !isDeepStrictEqual(native.target, proof.target) || snapshot.window_epoch !== proof.window_epoch || native.window_epoch !== proof.window_epoch))
155
+ return false;
156
+ 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)))))
157
+ return false;
158
+ if (!matches(snapshot.binding, binding) || !matches(native.binding, binding) || !matches(snapshot.attempt, proof.attempt) || !matches(native.attempt, proof.attempt)
159
+ || !matches(snapshot.step, proof.step) || !matches(native.step, proof.step) || !matches(snapshot.world, proof.world) || proof.world.invocation_id !== binding.invocation_id
160
+ || 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 })
161
+ || !matches(snapshot.world_health, { api: true, web: true, db: true }))
162
+ return false;
163
+ 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));
164
+ 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;
165
+ }
166
+ export function runnerProofArtifacts(evidence) {
167
+ return [...new Set([...(evidence?.failures ?? []).flatMap(failure => {
168
+ const child = validatedChildren.get(failure);
169
+ if (child && failure.child_evidence)
170
+ 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))];
171
+ const proof = failure.repair_evidence;
172
+ 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, ...(proof.rule === "candidate_library_query_error" ? (validatedLibraryInputs.get(proof) ?? []).map(input => input.artifact.relative_path) : [])] : [])];
173
+ }), ...(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))])])];
174
+ }
175
+ function diagnostic(context, value) {
176
+ // Diagnostics are locators, never another copy of untrusted report content.
177
+ if (context.diagnostics.length >= 64 || context.bytes + Buffer.byteLength(JSON.stringify(value)) > 32 * 1024 - 64) {
178
+ context.truncated = true;
179
+ return;
180
+ }
181
+ context.diagnostics.push(value);
182
+ context.bytes += Buffer.byteLength(JSON.stringify(value));
183
+ }
184
+ export function validateRunnerProof(contract, binding, readArtifact) {
185
+ const context = { diagnostics: [], bytes: 0, consumed: 0, truncated: false, readArtifact };
186
+ const evidence = readRunnerEvidenceInternal("", contract, binding, 0, { reports: 0 }, context, "");
187
+ return { contract: RUNNER_PROOF_CONTRACT, evidence, required_artifacts: evidence ? runnerProofArtifacts(evidence) : [], diagnostics: context.diagnostics, ...(context.truncated ? { diagnostics_truncated: true } : {}) };
188
+ }
189
+ function readRunnerEvidenceInternal(root, contract, binding, depth, budget, context, prefix) {
190
+ const report = relativeProofPath(contract.path) ? path.posix.join(prefix, contract.path) : "<invalid-report-path>";
191
+ const count = context.diagnostics.length;
192
+ let accepted = false;
193
+ let failureReason = "invalid_shape";
194
+ const invalid = () => { if (context.diagnostics.length === count)
195
+ diagnostic(context, { reason: failureReason, report }); return null; };
196
+ try {
197
+ if (!relativeProofPath(contract.path))
198
+ return invalid();
199
+ if (depth > 4 || ++budget.reports > 64) {
200
+ failureReason = "proof_budget_exceeded";
201
+ return invalid();
202
+ }
203
+ const bytes = readBytes(context, path.posix.join(root, contract.path), 1024 * 1024);
204
+ failureReason = "invalid_json";
205
+ const evidence = JSON.parse(bytes.toString("utf8"));
206
+ failureReason = "invalid_shape";
207
+ if (!object(evidence))
208
+ return invalid();
209
+ if (!["dd-flow/check-runner-evidence@1", "dd-flow/check-runner-evidence@2"].includes(contract.schema_id))
210
+ return null;
211
+ const partial = evidence.failure_projection;
212
+ 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)
213
+ || ![partial.represented, partial.omitted].every(value => Number.isSafeInteger(value) && value >= 0)))
214
+ return null;
215
+ if (evidence.schema_id !== contract.schema_id) {
216
+ 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" });
217
+ return null;
218
+ }
219
+ if (!evidence.binding || Object.entries(binding).some(([key, value]) => evidence.binding[key] !== value)) {
220
+ diagnostic(context, { reason: "binding_mismatch", report });
221
+ return null;
222
+ }
223
+ if (typeof evidence.runner?.name !== "string" || !evidence.runner.name || typeof evidence.runner.version !== "string" || !evidence.runner.version || typeof evidence.completed !== "boolean" || !evidence.counts
224
+ || ![evidence.counts.passed, evidence.counts.failed, evidence.counts.skipped].every(n => Number.isSafeInteger(n) && n >= 0)
225
+ || !Array.isArray(evidence.failures) || evidence.failures.length > 64 || evidence.failures.some(f => !runnerFailureValid(f))
226
+ || !partial && evidence.counts.failed > evidence.failures.length || evidence.observation_unknown !== undefined && typeof evidence.observation_unknown !== "boolean")
227
+ return null;
228
+ const children = new Map();
229
+ const consumed = new Map();
230
+ if (evidence.steps !== undefined) {
231
+ if (!Array.isArray(evidence.steps) || evidence.steps.length > 64)
232
+ return null;
233
+ const ids = new Set(), invocations = new Set();
234
+ let stopped = false;
235
+ for (const [ordinal, step] of evidence.steps.entries()) {
236
+ 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))
237
+ return null;
238
+ ids.add(step.id);
239
+ if (step.state === "unreached") {
240
+ if (!stopped || step.binding !== undefined || step.report !== undefined || step.exit_code !== undefined)
241
+ return null;
242
+ continue;
243
+ }
244
+ if (stopped)
245
+ return null;
246
+ if (step.state === "failed")
247
+ stopped = true;
248
+ 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)
249
+ return null;
250
+ 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"))
251
+ return null;
252
+ if (crypto.createHash("sha256").update(step.binding.command).digest("hex") !== step.command_sha256)
253
+ return null;
254
+ invocations.add(step.binding.invocation_id);
255
+ failureReason = "artifact_mismatch";
256
+ readProofArtifact(root, step.report, context, 1024 * 1024);
257
+ failureReason = "invalid_shape";
258
+ const child = readRunnerEvidenceInternal(path.posix.join(root, path.posix.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)));
259
+ if (!child || step.state === "passed" && (child.counts.failed !== 0 || child.failures.length !== 0) || step.state === "failed" && !child.failures.length && child.completed && !child.observation_unknown)
260
+ return null;
261
+ if (!child.completed || child.observation_unknown)
262
+ stopped = true;
263
+ children.set(step.id, child);
264
+ }
265
+ for (const key of ["passed", "failed", "skipped"])
266
+ if (evidence.counts[key] !== [...children.values()].reduce((total, child) => total + child.counts[key], 0))
267
+ return null;
268
+ }
269
+ for (const failure of evidence.failures) {
270
+ failureReason = "artifact_mismatch";
271
+ if (failure.child_evidence) {
272
+ const ref = failure.child_evidence, step = evidence.steps?.find(item => item.id === ref.step_id), child = children.get(ref.step_id);
273
+ if (!step || !child || !isDeepStrictEqual(ref.binding, step.binding) || !isDeepStrictEqual(ref.report, step.report))
274
+ return null;
275
+ const original = { ...failure };
276
+ delete original.child_evidence;
277
+ const used = consumed.get(child) ?? new Set();
278
+ consumed.set(child, used);
279
+ const index = child.failures.findIndex((item, index) => { const native = { ...item }; delete native.child_evidence; return !used.has(index) && isDeepStrictEqual(native, original); });
280
+ if (index < 0)
281
+ return null;
282
+ used.add(index);
283
+ const matched = child.failures[index];
284
+ 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 }))
285
+ return null;
286
+ validatedChildren.set(failure, { failure: matched, evidence: child });
287
+ continue;
288
+ }
289
+ if (failure.diagnostic_artifact && !matches(readProofArtifact(root, failure.diagnostic_artifact, context, 2 * 1024 * 1024).binding, binding))
290
+ return null;
291
+ const proof = failure.repair_evidence;
292
+ if (proof?.rule === "candidate_library_query_error" && !validateLibraryQuery(root, proof, failure, evidence, context))
293
+ return null;
294
+ if (proof?.rule === "candidate_migration_sql" || proof?.rule === "candidate_tool_diagnostic" || proof?.rule === "candidate_test_runtime_error") {
295
+ const native = readProofArtifact(root, proof.native_artifact, context, 2 * 1024 * 1024), facts = Object.fromEntries(Object.entries(proof).filter(([key]) => key !== "native_artifact"));
296
+ if (!isDeepStrictEqual(native.binding, binding) || Object.entries(facts).some(([key, value]) => !isDeepStrictEqual(native[key], value)))
297
+ return null;
298
+ if (proof.rule === "candidate_test_runtime_error") {
299
+ failureReason = "attribution_unproven";
300
+ if (evidence.schema_id !== "dd-flow/check-runner-evidence@2" || native.executing_file !== proof.file || native.exception_name !== proof.exception_type
301
+ || !isDeepStrictEqual(native.top_frame, { file: proof.file, line: proof.line, column: proof.column }) || !boundedString(native.stack, 16384) || !String(native.stack).startsWith(`${proof.exception_type}:`))
302
+ return null;
303
+ const firstFrame = String(native.stack).split("\n").find(line => /^\s*at\s/.test(line));
304
+ const frame = firstFrame?.match(/^\s*at\s+(?:.*?\()?((?:file:\/\/)?\/[^\n]+):(\d+):(\d+)\)?$/);
305
+ if (!frame || (frame[1].startsWith("file://") ? fileURLToPath(frame[1]) : frame[1]) !== proof.file || Number(frame[2]) !== proof.line || Number(frame[3]) !== proof.column)
306
+ return null;
307
+ }
308
+ validatedNativeProofs.add(proof);
309
+ }
310
+ if (proof?.rule === "candidate_browser_action_inactivity") {
311
+ if (proof.observation_contract === undefined && evidence.schema_id === "dd-flow/check-runner-evidence@1") {
312
+ // Legacy diagnostics remain readable; they do not prove the new window/target guarantee.
313
+ if (!validateBrowserArtifacts(root, proof, binding, context))
314
+ return null;
315
+ evidence.completed = false;
316
+ unsupportedObservationContracts.add(evidence);
317
+ continue;
318
+ }
319
+ if (proof.observation_contract !== "dd-tasks/browser-observation@2" || !validateBrowserArtifacts(root, proof, binding, context))
320
+ return null;
321
+ validatedBrowserProofs.add(proof);
322
+ }
323
+ }
324
+ failureReason = "invalid_shape";
325
+ const represented = [...consumed.values()].reduce((total, used) => total + used.size, 0), childFailures = [...children.values()].reduce((total, child) => total + child.failures.length, 0);
326
+ if (partial) {
327
+ if (children.size ? partial.represented !== represented || partial.omitted !== childFailures - represented : partial.represented !== evidence.failures.filter(failure => failure.code !== "report_truncated").length)
328
+ return null;
329
+ evidence.completed = false;
330
+ }
331
+ else if (represented !== childFailures)
332
+ return null;
333
+ // Valid but incomplete native observation is diagnostic evidence, not a
334
+ // malformed schema. Preserve the primary failure and references through
335
+ // every composite, including a parent that incorrectly claims completion.
336
+ if ([...children.values()].some(child => !child.completed || child.observation_unknown)) {
337
+ evidence.completed = false;
338
+ if ([...children.values()].some(child => unsupportedObservationContracts.has(child)))
339
+ unsupportedObservationContracts.add(evidence);
340
+ 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))
341
+ evidence.observation_unknown = true;
342
+ }
343
+ validatedReports.set(evidence, (evidence.steps ?? []).flatMap(step => step.report && children.has(step.id) ? [{ report: step.report, evidence: children.get(step.id) }] : []));
344
+ if (unsupportedObservationContracts.has(evidence))
345
+ diagnostic(context, { reason: "unsupported_observation_contract", report, expected: "dd-tasks/browser-observation@2", observed: "legacy" });
346
+ else if (!evidence.completed || evidence.observation_unknown)
347
+ diagnostic(context, { reason: "observation_incomplete", report });
348
+ for (const failure of evidence.failures)
349
+ if (failure.origin === "unknown" || failure.category === "unclassified" && !failure.repair_evidence) {
350
+ diagnostic(context, { reason: "attribution_unproven", report });
351
+ break;
352
+ }
353
+ accepted = true;
354
+ return evidence;
355
+ }
356
+ catch (error) {
357
+ if (error.code === "ENOENT")
358
+ failureReason = "missing";
359
+ else if (error.code === "proof_budget_exceeded")
360
+ failureReason = "proof_budget_exceeded";
361
+ return invalid();
362
+ }
363
+ finally {
364
+ if (!accepted && context.diagnostics.length === count)
365
+ diagnostic(context, { reason: failureReason, report });
366
+ }
367
+ }
368
+ export function runnerEvidencePass(evidence) { return Boolean(evidence?.completed && !evidence.failure_projection && !evidence.observation_unknown && evidence.counts.failed === 0 && evidence.failures.length === 0); }
369
+ export const validatedRunnerChild = (failure) => validatedChildren.get(failure);
370
+ export const hasValidatedNativeProof = (proof) => validatedNativeProofs.has(proof);
371
+ export const runnerLibraryInputs = (proof) => validatedLibraryInputs.get(proof)?.map(({ file, sha256 }) => ({ file, sha256 }));
372
+ export const hasValidatedBrowserProof = (proof) => validatedBrowserProofs.has(proof);
373
+ export const hasUnsupportedObservation = (evidence) => unsupportedObservationContracts.has(evidence);
@@ -1,5 +1,5 @@
1
1
  import crypto from "node:crypto";
2
- import { candidateAbsencesValid, candidateFailureAbsences, candidateFailureFiles, checkRepairDisposition, readRunnerEvidence, readRunnerEvidenceDetailed, readRunnerProgress, runnerContractsValid, runnerEvidencePass, runnerObservationDurations, runnerProofArtifacts } from "./check-runner-evidence.js";
2
+ import { candidateAbsencesValid, candidateFailureAbsences, candidateFailureFiles, candidateLibraryInputs, candidateLibraryInputsValid, currentCandidateLibraryInputsValid, checkRepairDisposition, readRunnerEvidence, readRunnerEvidenceDetailed, readRunnerProgress, runnerContractsValid, runnerEvidencePass, runnerObservationDurations, runnerProofArtifacts } from "./check-runner-evidence.js";
3
3
  export { assertSourceRepairEligible, checkRepairDisposition } from "./check-runner-evidence.js";
4
4
  import { isGitlessWorkspace, ownedWorkspaceHookPath, projectServicePathFilter, workspaceGitFiles } from "./workspace-files.js";
5
5
  import { spawn, spawnSync } from "node:child_process";
@@ -17,6 +17,7 @@ import { admitRuntimeCheck, runtimeBudgetForRun } from "./runtime-budget.js";
17
17
  import { getResourceDatabase } from "../storage/database.js";
18
18
  import { readBoundedReleaseFile } from "./release-source.js";
19
19
  import { ownedCheckEnvironment, validateCheckEnvironment } from "./check-environment.js";
20
+ import { browserCheckCapabilityEnvironment } from "./stage-context.js";
20
21
  const profileRelativePath = path.join(".memory-bank", "spec", "engineering", "code-check-profile.json");
21
22
  // Receipt reservation precedes child spawn so an interrupted CLI leaves an
22
23
  // auditable attempt. A second caller must not mistake that small handoff window
@@ -68,6 +69,8 @@ export function codeExecutionEnvironment(workspaceRoot, checkEnvironment) {
68
69
  // provider can otherwise run x64 checks against arm64-installed modules.
69
70
  const environment = { ...process.env, PATH: `${path.dirname(process.execPath)}${path.delimiter}${process.env.PATH ?? ""}`, DD_FLOW_LOCAL_DATABASE_SUFFIX: suffix };
70
71
  delete environment.NODE_PATH;
72
+ delete environment.DD_FLOW_BROWSER_CAPABILITY_PATH;
73
+ delete environment.DD_FLOW_BROWSER_CAPABILITY_SHA256;
71
74
  for (const key of ["DD_FLOW_CHECK_RUNNER_EVIDENCE_PATH", "DD_FLOW_CHECK_RUNNER_PROGRESS_PATH", "DD_FLOW_CHECK_INVOCATION_ID", "DD_FLOW_CHECK_ID", "DD_FLOW_CHECK_INPUT_HASH", "DD_FLOW_CHECK_VERIFICATION_EPOCH", "DD_FLOW_CHECK_COMMAND"])
72
75
  delete environment[key];
73
76
  Object.assign(environment, selected);
@@ -349,7 +352,7 @@ export class PreparedCheckEvidence {
349
352
  candidates: receipts.filter(item => item.scope === input.scope && item.work_id === (input.workId ?? null))
350
353
  .slice(-16).map(item => ({ id: item.id, input_hash: item.input_hash, profile_hash: item.profile_hash }))
351
354
  });
352
- if (!checkReceiptEvidenceValidForMode(bindReceipt(receipt, group), input.mode))
355
+ if (!checkReceiptEvidenceValidForMode(bindReceipt(receipt, group), input.mode) || !currentCandidateLibraryInputsValid(receipt, input.workspaceRoot))
353
356
  fail();
354
357
  passed = passed && receipt.status === "passed";
355
358
  for (const artifact of receipt.artifacts)
@@ -621,7 +624,7 @@ async function runCodeChecksOwned(context, input, workspaceGateId, generation, s
621
624
  const runnerEnvironment = group.runnerEvidence ? { DD_FLOW_CHECK_RUNNER_EVIDENCE_PATH: path.join(evidenceDir, group.runnerEvidence.path), DD_FLOW_CHECK_RUNNER_PROGRESS_PATH: path.join(evidenceDir, "runner-progress.jsonl"), DD_FLOW_CHECK_INVOCATION_ID: id, DD_FLOW_CHECK_ID: id, DD_FLOW_CHECK_INPUT_HASH: inputHash, DD_FLOW_CHECK_VERIFICATION_EPOCH: epoch } : {};
622
625
  if (group.runnerEvidence)
623
626
  fs.mkdirSync(path.dirname(path.join(evidenceDir, group.runnerEvidence.path)), { recursive: true });
624
- result = await runCheck(group.command, input.workspaceRoot, { ...codeExecutionEnvironment(input.workspaceRoot, input.checkEnvironment), DD_FLOW_EVIDENCE_DIR: evidenceDir, DD_FLOW_CHECK_COMPLETION_FILE: completionPath, ...runnerEnvironment, ...portEnvironment(resources) }, stdout, stderr, (elapsed) => input.progress?.(`check ${index + 1}/${groups.length} still running (${elapsed}s): ${group.command}`), (pid) => {
627
+ result = await runCheck(group.command, input.workspaceRoot, { ...codeExecutionEnvironment(input.workspaceRoot, input.checkEnvironment), ...browserCheckCapabilityEnvironment(context, input.runId, input.runHome), DD_FLOW_EVIDENCE_DIR: evidenceDir, DD_FLOW_CHECK_COMPLETION_FILE: completionPath, ...runnerEnvironment, ...portEnvironment(resources) }, stdout, stderr, (elapsed) => input.progress?.(`check ${index + 1}/${groups.length} still running (${elapsed}s): ${group.command}`), (pid) => {
625
628
  confirmedExpiry = confirmManagedProcess(context, { id: managed.id, leaseToken: managed.lease_token, pid, processGroupId: process.platform === "win32" ? null : pid }).lease_expires_at;
626
629
  assertRunMutationAllowed(context, input.projectId, input.runId, generation);
627
630
  if (budget)
@@ -663,8 +666,9 @@ async function runCodeChecksOwned(context, input, workspaceGateId, generation, s
663
666
  physicalFailures.push(result.failure);
664
667
  const candidateFiles = candidateFailureFiles(runnerEvidence, input.workspaceRoot);
665
668
  const candidateAbsences = candidateFailureAbsences(runnerEvidence, input.workspaceRoot);
669
+ const libraryInputs = candidateLibraryInputs(runnerEvidence, input.workspaceRoot);
666
670
  if (group.runnerEvidence)
667
- retainExecutionOutcome(evidenceDir, { id, command: group.command, input_hash: inputHash, verification_epoch: epoch, fingerprint_scope: "check_inputs", input_manifest: reserved.inputManifest ?? null, execution_contract_hash: executionContractHash }, input.workspaceRoot, physicalFailures, candidateFiles, candidateAbsences);
671
+ retainExecutionOutcome(evidenceDir, { id, command: group.command, input_hash: inputHash, verification_epoch: epoch, fingerprint_scope: "check_inputs", input_manifest: reserved.inputManifest ?? null, execution_contract_hash: executionContractHash }, input.workspaceRoot, physicalFailures, candidateFiles, candidateAbsences, libraryInputs);
668
672
  const requiredArtifacts = [...new Set([...group.requiredArtifacts, ...runnerProofArtifacts(runnerEvidence), ...(group.runnerEvidence ? ["engine-outcome.json"] : [])])];
669
673
  artifacts = collectCheckArtifacts(evidenceDir, requiredArtifacts);
670
674
  const status = result.aborted ? "aborted" : result.exitCode === 0 && !result.error && artifacts.complete && mutationPaths.length === 0 && (!group.runnerEvidence || runnerEvidencePass(runnerEvidence)) ? "passed" : "failed";
@@ -673,6 +677,7 @@ async function runCodeChecksOwned(context, input, workspaceGateId, generation, s
673
677
  receipt.candidate_root = input.workspaceRoot;
674
678
  receipt.candidate_files = candidateFiles;
675
679
  receipt.candidate_absences = candidateAbsences;
680
+ receipt.candidate_library_inputs = libraryInputs;
676
681
  receipt.execution_failures = physicalFailures;
677
682
  context.db.run("UPDATE check_receipts SET execution_failures_json = ? WHERE project_id = ? AND id = ? AND status = 'running'", [JSON.stringify(physicalFailures), input.projectId, id]);
678
683
  const completion = readCompletion(completionPath);
@@ -765,6 +770,7 @@ export function reconcileUnfinishedChecks(context, input) {
765
770
  physicalFailures.push({ code: "completion_snapshot_unknown", message: "Detached check has no bound scoped completion snapshot; current inputs cannot prove inputs at command completion" });
766
771
  let candidateFiles = candidateFailureFiles(runnerEvidence, input.workspaceRoot);
767
772
  let candidateAbsences = candidateFailureAbsences(runnerEvidence, input.workspaceRoot);
773
+ let libraryInputs = candidateLibraryInputs(runnerEvidence, input.workspaceRoot);
768
774
  if (receipt?.runner_evidence_required) {
769
775
  const evidenceDir = path.join(path.dirname(pendingReceipt.receipt_path), "artifacts");
770
776
  if (fs.existsSync(path.join(evidenceDir, "engine-outcome.json"))) {
@@ -775,13 +781,14 @@ export function reconcileUnfinishedChecks(context, input) {
775
781
  physicalFailures.push(...(proven.execution_failures ?? []));
776
782
  candidateFiles = proven.candidate_files ?? candidateFiles;
777
783
  candidateAbsences = proven.candidate_absences ?? [];
784
+ libraryInputs = proven.candidate_library_inputs ?? [];
778
785
  }
779
786
  else {
780
787
  physicalFailures.push({ code: "engine_outcome_unbound", message: "Existing execution outcome is not bound to retained inventory; prior launcher/cleanup outcome is unknown" });
781
788
  fs.renameSync(path.join(evidenceDir, "engine-outcome.json"), path.join(evidenceDir, `engine-outcome-unconfirmed-${crypto.randomUUID()}.json`));
782
789
  }
783
790
  }
784
- retainExecutionOutcome(evidenceDir, receipt, input.workspaceRoot, physicalFailures, candidateFiles, candidateAbsences);
791
+ retainExecutionOutcome(evidenceDir, receipt, input.workspaceRoot, physicalFailures, candidateFiles, candidateAbsences, libraryInputs);
785
792
  receipt.required_artifacts = [...new Set([...(receipt.required_artifacts ?? []), ...runnerProofArtifacts(runnerEvidence), "engine-outcome.json"])];
786
793
  artifacts = collectCheckArtifacts(evidenceDir, receipt.required_artifacts);
787
794
  }
@@ -795,6 +802,7 @@ export function reconcileUnfinishedChecks(context, input) {
795
802
  repaired.candidate_root = input.workspaceRoot;
796
803
  repaired.candidate_files = candidateFiles;
797
804
  repaired.candidate_absences = candidateAbsences;
805
+ repaired.candidate_library_inputs = libraryInputs;
798
806
  repaired.execution_failures = physicalFailures;
799
807
  writeReceipt(pendingReceipt.receipt_path, repaired);
800
808
  if (status === "failed")
@@ -821,6 +829,11 @@ function reserveOrReuseReceipt(context, input) {
821
829
  const sourceCorrection = Boolean(retryPredecessor && (sourceRepairContractMatches(retryPredecessor, input.group, input.profileHash)
822
830
  || input.retryReason === "check_inputs_changed" && staleRetryContractMatches(retryPredecessor, input.group, input.profileHash, input)));
823
831
  const latestContract = receipts.filter(receipt => receipt.command === input.group.command && receipt.profile_hash === input.profileHash && receipt.gate === input.group.gate && receipt.scope === input.scope && receipt.work_id === (input.workId ?? null)).at(-1);
832
+ // Hash files before SQLite's writer reservation. Captured identities remain
833
+ // valid historical evidence, but cannot authorize reuse in a changed install.
834
+ const priorLibrariesCurrent = !prior || currentCandidateLibraryInputsValid(prior, input.workspaceRoot);
835
+ const latestLibrariesCurrent = !latestContract || currentCandidateLibraryInputsValid(latestContract, input.workspaceRoot);
836
+ const libraryDrift = (receipt) => { throw new AppError("check_evidence_stale", "Captured library inputs differ from the current candidate installation; request an explicit restored-condition retry", 2, { reason: "candidate_library_inputs_changed", check_receipt_id: receipt.id, effect: "no_effect" }); };
824
837
  return context.db.writeTransaction(() => {
825
838
  assertRunMutationAllowed(context, input.projectId, input.runId, input.generation);
826
839
  if (JSON.stringify(context.db.all(snapshotSql, [input.projectId, input.runId])) !== snapshot)
@@ -856,10 +869,16 @@ function reserveOrReuseReceipt(context, input) {
856
869
  // An unchanged failed contract needs a concrete restored-condition retry,
857
870
  // not repeated coordinator finish until it happens to become green.
858
871
  if (!retryId && latestContract?.fingerprint_scope === "check_inputs" && latestContract.status === "failed"
859
- && latestContract.execution_failures?.some(failure => failure.code === "check_inputs_changed") && latestContract.execution_contract_hash === input.executionContractHash)
872
+ && latestContract.execution_failures?.some(failure => failure.code === "check_inputs_changed") && latestContract.execution_contract_hash === input.executionContractHash) {
873
+ if (!latestLibrariesCurrent)
874
+ libraryDrift(latestContract);
860
875
  return { receipt: latestContract };
861
- if (prior && ["failed", "aborted"].includes(prior.status) && !retryId)
876
+ }
877
+ if (prior && ["failed", "aborted"].includes(prior.status) && !retryId) {
878
+ if (!priorLibrariesCurrent)
879
+ libraryDrift(prior);
862
880
  return { receipt: prior };
881
+ }
863
882
  if (prior?.status === "passed" && !retryIds.has(prior.id) && input.group.reusable && reusablePrior)
864
883
  return { receipt: prior };
865
884
  let ordinal = (context.db.get("SELECT COALESCE(MAX(reservation_sequence), 0) + 1 AS sequence FROM check_receipts WHERE project_id = ? AND run_id = ?", [input.projectId, input.runId])?.sequence ?? 1);
@@ -1001,12 +1020,12 @@ function resolvedReceiptArtifacts(receiptPath, artifacts, binding) {
1001
1020
  if (retainedOutcome.complete && retainedOutcome.items.every(item => artifacts.some(artifact => relocate(artifact.path) === item.path && artifact.sha256 === item.sha256))) {
1002
1021
  try {
1003
1022
  const value = JSON.parse(fs.readFileSync(outcomePath, "utf8"));
1004
- if (value.schema_id === "dd-flow/check-execution-outcome@1" && value.id === binding.id && value.command === binding.command && value.input_hash === binding.input_hash && value.verification_epoch === binding.verification_epoch && (value.fingerprint_scope ?? null) === (binding.fingerprint_scope ?? null) && (value.execution_contract_hash ?? null) === (binding.execution_contract_hash ?? null) && JSON.stringify(value.input_manifest ?? null) === JSON.stringify(binding.input_manifest ?? null) && typeof value.candidate_root === "string" && path.isAbsolute(value.candidate_root) && (value.candidate_files === undefined || Array.isArray(value.candidate_files) && value.candidate_files.length <= 64 && value.candidate_files.every((file) => typeof file === "string" && path.isAbsolute(file) && Buffer.byteLength(file) <= 4096)) && (value.candidate_absences === undefined || candidateAbsencesValid(value.candidate_absences)) && Array.isArray(value.execution_failures) && value.execution_failures.every((failure) => failure && typeof failure === "object" && typeof failure.code === "string" && typeof failure.message === "string"))
1023
+ if (value.schema_id === "dd-flow/check-execution-outcome@1" && value.id === binding.id && value.command === binding.command && value.input_hash === binding.input_hash && value.verification_epoch === binding.verification_epoch && (value.fingerprint_scope ?? null) === (binding.fingerprint_scope ?? null) && (value.execution_contract_hash ?? null) === (binding.execution_contract_hash ?? null) && JSON.stringify(value.input_manifest ?? null) === JSON.stringify(binding.input_manifest ?? null) && typeof value.candidate_root === "string" && path.isAbsolute(value.candidate_root) && (value.candidate_files === undefined || Array.isArray(value.candidate_files) && value.candidate_files.length <= 64 && value.candidate_files.every((file) => typeof file === "string" && path.isAbsolute(file) && Buffer.byteLength(file) <= 4096)) && (value.candidate_absences === undefined || candidateAbsencesValid(value.candidate_absences)) && (value.candidate_library_inputs === undefined || candidateLibraryInputsValid(value.candidate_library_inputs)) && Array.isArray(value.execution_failures) && value.execution_failures.every((failure) => failure && typeof failure === "object" && typeof failure.code === "string" && typeof failure.message === "string"))
1005
1024
  outcome = value;
1006
1025
  }
1007
1026
  catch { /* Missing physical proof cannot grant attribution. */ }
1008
1027
  }
1009
- return { completion_path: path.join(currentRoot, "completion.json"), required_artifacts: original.required_artifacts ?? [], inputs: original.inputs ?? [], artifacts: artifacts.map((artifact) => ({ ...artifact, path: relocate(artifact.path) })), ...(original.runner_evidence_required ? { runner_evidence_required: original.runner_evidence_required, runner_evidence: evidence, runner_evidence_diagnostics: diagnostics, ...(diagnosticsTruncated ? { runner_evidence_diagnostics_truncated: true } : {}) } : {}), execution_failures: outcome.execution_failures ?? [], ...(outcome.candidate_root ? { candidate_root: outcome.candidate_root } : {}), ...(outcome.candidate_files ? { candidate_files: outcome.candidate_files } : {}), ...(outcome.candidate_absences ? { candidate_absences: outcome.candidate_absences } : {}) };
1028
+ return { completion_path: path.join(currentRoot, "completion.json"), required_artifacts: original.required_artifacts ?? [], inputs: original.inputs ?? [], artifacts: artifacts.map((artifact) => ({ ...artifact, path: relocate(artifact.path) })), ...(original.runner_evidence_required ? { runner_evidence_required: original.runner_evidence_required, runner_evidence: evidence, runner_evidence_diagnostics: diagnostics, ...(diagnosticsTruncated ? { runner_evidence_diagnostics_truncated: true } : {}) } : {}), execution_failures: outcome.execution_failures ?? [], ...(outcome.candidate_root ? { candidate_root: outcome.candidate_root } : {}), ...(outcome.candidate_files ? { candidate_files: outcome.candidate_files } : {}), ...(outcome.candidate_absences ? { candidate_absences: outcome.candidate_absences } : {}), ...(outcome.candidate_library_inputs ? { candidate_library_inputs: outcome.candidate_library_inputs } : {}) };
1010
1029
  }
1011
1030
  function executionFailures(input) {
1012
1031
  const failures = [];
@@ -1024,11 +1043,11 @@ function executionFailures(input) {
1024
1043
  failures.push({ code: "invalid_runner_evidence", message: "Required runner evidence is unavailable or invalid" });
1025
1044
  return failures;
1026
1045
  }
1027
- function retainExecutionOutcome(evidenceDir, binding, candidateRoot, failures, candidateFiles, candidateAbsences = []) {
1046
+ function retainExecutionOutcome(evidenceDir, binding, candidateRoot, failures, candidateFiles, candidateAbsences = [], libraryInputs = []) {
1028
1047
  const temporary = path.join(evidenceDir, `.engine-outcome-${crypto.randomUUID()}.tmp`);
1029
1048
  if (!fs.lstatSync(evidenceDir).isDirectory() || fs.lstatSync(evidenceDir).isSymbolicLink())
1030
1049
  throw new AppError("check_evidence_stale", "Check evidence directory was replaced", 1);
1031
- fs.writeFileSync(temporary, JSON.stringify({ schema_id: "dd-flow/check-execution-outcome@1", id: binding.id, command: binding.command, input_hash: binding.input_hash, verification_epoch: binding.verification_epoch, fingerprint_scope: binding.fingerprint_scope ?? null, input_manifest: binding.input_manifest ?? null, execution_contract_hash: binding.execution_contract_hash ?? null, candidate_root: candidateRoot, candidate_files: candidateFiles, candidate_absences: candidateAbsences, execution_failures: failures }), { flag: "wx", mode: 0o600 });
1050
+ fs.writeFileSync(temporary, JSON.stringify({ schema_id: "dd-flow/check-execution-outcome@1", id: binding.id, command: binding.command, input_hash: binding.input_hash, verification_epoch: binding.verification_epoch, fingerprint_scope: binding.fingerprint_scope ?? null, input_manifest: binding.input_manifest ?? null, execution_contract_hash: binding.execution_contract_hash ?? null, candidate_root: candidateRoot, candidate_files: candidateFiles, candidate_absences: candidateAbsences, candidate_library_inputs: libraryInputs, execution_failures: failures }), { flag: "wx", mode: 0o600 });
1032
1051
  fs.renameSync(temporary, path.join(evidenceDir, "engine-outcome.json"));
1033
1052
  }
1034
1053
  export function workspaceFingerprint(root) { return workspaceState(root).fingerprint; }
@@ -1358,7 +1377,7 @@ function checkReceiptEvidenceValidForMode(receipt, mode) {
1358
1377
  // Receipt annotations are not execution authority. Compare the SQL-owned
1359
1378
  // failures with the independently hash/binding-checked engine sidecar.
1360
1379
  const hasEngineOutcome = receipt.required_artifacts.includes("engine-outcome.json") || receipt.artifacts.some(artifact => artifact.path === "engine-outcome.json" || artifact.path === path.join(path.dirname(receipt.receipt_path), "artifacts", "engine-outcome.json"));
1361
- if (hasEngineOutcome && (!isDeepStrictEqual(retainedProof.execution_failures ?? [], receipt.execution_failures ?? []) || !isDeepStrictEqual(retainedProof.candidate_absences ?? [], receipt.candidate_absences ?? [])))
1380
+ if (hasEngineOutcome && (!isDeepStrictEqual(retainedProof.execution_failures ?? [], receipt.execution_failures ?? []) || !isDeepStrictEqual(retainedProof.candidate_absences ?? [], receipt.candidate_absences ?? []) || !isDeepStrictEqual(retainedProof.candidate_library_inputs ?? [], receipt.candidate_library_inputs ?? [])))
1362
1381
  return false;
1363
1382
  const root = path.join(path.dirname(receipt.receipt_path), "artifacts");
1364
1383
  if (stored.runner_evidence_required && mode !== "diagnostic" && !runnerEvidencePass(readRunnerEvidence(root, stored.runner_evidence_required, { invocation_id: receipt.id, check_id: receipt.id, command: receipt.command, input_hash: receipt.input_hash, verification_epoch: receipt.verification_epoch })))