@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.
- 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 +62 -10
- package/dist/harness-runtime/lib/dd-opencode.mjs +32 -14
- 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 +54 -309
- package/dist/services/check-runner-proof.js +373 -0
- package/dist/services/code-checks.js +31 -12
- 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/repair-intents.js +4 -1
- 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-code-review.js +1 -1
- package/dist/services/vnext-code.js +3 -2
- package/dist/services/vnext-fanout.js +1 -1
- package/dist/services/vnext-merge.js +1 -1
- package/dist/services/work-registry.js +1 -1
- package/dist/verification-components.json +299 -0
- package/package.json +7 -6
- 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 {
|
|
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, 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 =
|
|
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 =
|
|
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 || !
|
|
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 =
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
367
|
-
for (const part of
|
|
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
|
-
|
|
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
|
-
|
|
499
|
-
|
|
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
|
|
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 &&
|
|
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
|
|
560
|
-
|
|
561
|
-
|
|
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
|
}
|