@deksden-com/dd-flow-cli 0.9.0-beta.150 → 0.9.0-beta.151
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.
- package/CHANGELOG.md +12 -0
- package/README.md +17 -22
- package/dist/build-info.json +6 -6
- package/dist/harness-runtime/lib/agy-error-boundary.mjs +26 -5
- package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +67 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +10 -14
- package/dist/harness-runtime/lib/dd-agy.mjs +3 -3
- package/dist/harness-runtime/lib/dd-droid.mjs +2 -2
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +8 -3
- package/dist/harness-runtime/lib/dd-opencode.mjs +2 -2
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +1 -1
- package/dist/harness-runtime/lib/dd-zcode.mjs +37 -12
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +1 -1
- package/dist/harness-runtime/lib/delegation-instructions.mjs +13 -4
- package/dist/harness-runtime/lib/model-observations.mjs +19 -7
- package/dist/harness-runtime/lib/native-children.d.mts +12 -1
- package/dist/harness-runtime/lib/native-children.mjs +47 -1
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +9 -1
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +97 -6
- package/dist/services/check-runner-evidence.js +17 -302
- package/dist/services/check-runner-proof.js +305 -0
- package/dist/services/code-checks.js +4 -1
- package/dist/services/controller-child-capacity.js +58 -0
- package/dist/services/controller-child-observer.js +9 -3
- package/dist/services/memory-init-native-capacity.js +1 -1
- package/dist/services/provider-capacity.js +15 -7
- package/dist/services/run-controller-adapter.js +1 -1
- package/dist/services/run-controller.js +87 -18
- package/dist/services/run-recovery-guard.js +1 -1
- package/dist/services/run-recovery-runtime.js +1 -1
- package/dist/services/stage-context.js +56 -8
- package/dist/services/vnext-fanout.js +1 -1
- package/dist/verification-components.json +299 -0
- package/package.json +7 -6
|
@@ -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 {
|
|
10
|
-
|
|
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 } from "./check-runner-proof.js";
|
|
9
|
+
export { candidateAbsencesValid, 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 =
|
|
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;
|
|
@@ -147,12 +89,12 @@ function absentCandidate(root, file) {
|
|
|
147
89
|
}
|
|
148
90
|
export function candidateFailureAbsences(evidence, root) {
|
|
149
91
|
return (evidence?.failures ?? []).flatMap(failure => {
|
|
150
|
-
const child =
|
|
92
|
+
const child = validatedRunnerChild(failure);
|
|
151
93
|
if (child)
|
|
152
94
|
return candidateFailureAbsences({ ...child.evidence, failures: [child.failure] }, root);
|
|
153
95
|
try {
|
|
154
96
|
const proof = failure.repair_evidence;
|
|
155
|
-
if (proof?.rule !== "candidate_tool_diagnostic" || proof.path_state !== "missing" || !proof.declaration || !
|
|
97
|
+
if (proof?.rule !== "candidate_tool_diagnostic" || proof.path_state !== "missing" || !proof.declaration || !hasValidatedNativeProof(proof)
|
|
156
98
|
|| !absentCandidate(root, proof.file) || !candidateFile(root, proof.declaration.file) || crypto.createHash("sha256").update(fs.readFileSync(proof.declaration.file)).digest("hex") !== proof.declaration.sha256)
|
|
157
99
|
return [];
|
|
158
100
|
return [{ file: proof.file, declaration: proof.declaration, path_state: "missing" }];
|
|
@@ -163,7 +105,7 @@ export function candidateFailureAbsences(evidence, root) {
|
|
|
163
105
|
});
|
|
164
106
|
}
|
|
165
107
|
function candidateFailure(failure, evidence, root, capturedFiles, capturedAbsences) {
|
|
166
|
-
const child =
|
|
108
|
+
const child = validatedRunnerChild(failure);
|
|
167
109
|
if (child)
|
|
168
110
|
return candidateFailure(child.failure, child.evidence, root, capturedFiles, capturedAbsences);
|
|
169
111
|
const owned = (file) => {
|
|
@@ -185,10 +127,10 @@ function candidateFailure(failure, evidence, root, capturedFiles, capturedAbsenc
|
|
|
185
127
|
return failure.origin === undefined || failure.origin === "candidate" && failure.file !== undefined && owned(failure.file);
|
|
186
128
|
const proof = failure.repair_evidence;
|
|
187
129
|
if (proof?.rule === "candidate_test_runtime_error")
|
|
188
|
-
return
|
|
130
|
+
return hasValidatedNativeProof(proof) && failure.category === "unclassified" && failure.origin === "candidate"
|
|
189
131
|
&& failure.phase === proof.phase && failure.native_code === proof.exception_type && failure.file === proof.file && owned(proof.file);
|
|
190
132
|
if (proof?.rule === "candidate_migration_sql" || proof?.rule === "candidate_tool_diagnostic") {
|
|
191
|
-
if (!
|
|
133
|
+
if (!hasValidatedNativeProof(proof) || failure.category !== "unclassified" || failure.origin !== "candidate" || failure.file !== proof.file || !["setup", "collection", "test"].includes(failure.phase ?? ""))
|
|
192
134
|
return false;
|
|
193
135
|
if (proof.rule === "candidate_migration_sql")
|
|
194
136
|
return failure.phase === "setup" && failure.native_code === "42601" && proof.world.invocation_id === evidence.binding.invocation_id && owned(proof.file);
|
|
@@ -197,7 +139,7 @@ function candidateFailure(failure, evidence, root, capturedFiles, capturedAbsenc
|
|
|
197
139
|
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
140
|
}
|
|
199
141
|
if (proof?.rule === "candidate_browser_action_inactivity")
|
|
200
|
-
return
|
|
142
|
+
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
143
|
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
144
|
}
|
|
203
145
|
export function runnerObservationDurations(environment) {
|
|
@@ -278,48 +220,6 @@ function readBoundedProofFile(file, limit) {
|
|
|
278
220
|
fs.closeSync(fd);
|
|
279
221
|
}
|
|
280
222
|
}
|
|
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
223
|
export function runnerContractsValid(value) {
|
|
324
224
|
if (value === undefined)
|
|
325
225
|
return true;
|
|
@@ -332,211 +232,26 @@ export function runnerContractsValid(value) {
|
|
|
332
232
|
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
233
|
});
|
|
334
234
|
}
|
|
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
235
|
export function readRunnerEvidenceDetailed(root, contract, binding) {
|
|
345
|
-
|
|
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;
|
|
236
|
+
return validateRunnerProof(contract, binding, relative => {
|
|
364
237
|
let current = root;
|
|
365
238
|
if (!fs.lstatSync(current).isDirectory() || fs.lstatSync(current).isSymbolicLink())
|
|
366
|
-
|
|
367
|
-
for (const part of
|
|
239
|
+
throw new Error("Unsafe proof root");
|
|
240
|
+
for (const part of relative.split("/")) {
|
|
368
241
|
current = path.join(current, part);
|
|
369
242
|
if (fs.lstatSync(current).isSymbolicLink())
|
|
370
|
-
|
|
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;
|
|
243
|
+
throw new Error("Symlink proof artifact");
|
|
437
244
|
}
|
|
438
|
-
|
|
439
|
-
|
|
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;
|
|
497
|
-
}
|
|
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
|
-
}
|
|
245
|
+
return readBoundedProofFile(current, 2 * 1024 * 1024);
|
|
246
|
+
});
|
|
532
247
|
}
|
|
533
|
-
export function
|
|
248
|
+
export function readRunnerEvidence(root, contract, binding) { return readRunnerEvidenceDetailed(root, contract, binding).evidence; }
|
|
534
249
|
export function checkRepairDisposition(receipt) {
|
|
535
250
|
if (receipt.fingerprint_scope != null && receipt.fingerprint_scope !== "check_inputs")
|
|
536
251
|
return "tooling_blocker";
|
|
537
252
|
if (receipt.status === "running" || receipt.status === "aborted" || receipt.runner_evidence?.observation_unknown)
|
|
538
253
|
return "settlement_required";
|
|
539
|
-
if (receipt.runner_evidence &&
|
|
254
|
+
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
255
|
return "tooling_blocker";
|
|
541
256
|
if (receipt.status === "passed")
|
|
542
257
|
return "passed";
|