@riddledc/riddle-proof 0.8.11 → 0.8.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced/engine-harness.cjs +97 -12
- package/dist/advanced/engine-harness.js +2 -2
- package/dist/advanced/index.cjs +98 -12
- package/dist/advanced/index.d.cts +2 -2
- package/dist/advanced/index.d.ts +2 -2
- package/dist/advanced/index.js +4 -4
- package/dist/advanced/proof-run-core.cjs +31 -1
- package/dist/advanced/proof-run-core.d.cts +1 -1
- package/dist/advanced/proof-run-core.d.ts +1 -1
- package/dist/advanced/proof-run-core.js +3 -1
- package/dist/advanced/proof-run-engine.cjs +46 -12
- package/dist/advanced/proof-run-engine.d.cts +2 -2
- package/dist/advanced/proof-run-engine.d.ts +2 -2
- package/dist/advanced/proof-run-engine.js +2 -2
- package/dist/advanced/runner.js +2 -2
- package/dist/{chunk-5N5QFI2S.js → chunk-7GZY5PLT.js} +31 -1
- package/dist/{chunk-46DDSZJR.js → chunk-JBY2SU5U.js} +18 -12
- package/dist/{chunk-5N6MQCLC.js → chunk-NGX4SUQN.js} +1 -1
- package/dist/{chunk-BBUO7HM4.js → chunk-RTLA6CPP.js} +53 -1
- package/dist/{chunk-2PXL3RDB.js → chunk-SZUC4MDN.js} +1 -1
- package/dist/cli/index.js +3 -3
- package/dist/cli.cjs +97 -12
- package/dist/cli.js +3 -3
- package/dist/engine-harness.cjs +97 -12
- package/dist/engine-harness.js +2 -2
- package/dist/index.cjs +97 -12
- package/dist/index.js +3 -3
- package/dist/{proof-run-core-CE0jx7wL.d.cts → proof-run-core-C8FDUhle.d.cts} +5 -2
- package/dist/{proof-run-core-CE0jx7wL.d.ts → proof-run-core-C8FDUhle.d.ts} +5 -2
- package/dist/proof-run-core.cjs +31 -1
- package/dist/proof-run-core.d.cts +1 -1
- package/dist/proof-run-core.d.ts +1 -1
- package/dist/proof-run-core.js +3 -1
- package/dist/{proof-run-engine-BomAcXhA.d.ts → proof-run-engine-By7oLsF-.d.ts} +1 -1
- package/dist/{proof-run-engine-B7DCPzpK.d.cts → proof-run-engine-D80hVFMf.d.cts} +1 -1
- package/dist/proof-run-engine.cjs +46 -12
- package/dist/proof-run-engine.d.cts +2 -2
- package/dist/proof-run-engine.d.ts +2 -2
- package/dist/proof-run-engine.js +2 -2
- package/dist/runner.js +2 -2
- package/package.json +1 -1
- package/runtime/lib/verify.py +266 -22
- package/runtime/tests/recon_verify_smoke.py +291 -4
- package/runtime/tests/trust_boundary_regression.py +18 -0
|
@@ -505,6 +505,25 @@ function visualDeltaShipGateReason(state = {}) {
|
|
|
505
505
|
if (reason) return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof: ${reason}`;
|
|
506
506
|
return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof`;
|
|
507
507
|
}
|
|
508
|
+
function proofAssessmentHardBlockersForState(state = {}) {
|
|
509
|
+
const request = objectValue(state?.proof_assessment_request);
|
|
510
|
+
const blockers = [];
|
|
511
|
+
const add = (value) => {
|
|
512
|
+
if (typeof value !== "string") return;
|
|
513
|
+
const trimmed = value.trim();
|
|
514
|
+
if (trimmed && !blockers.includes(trimmed)) blockers.push(trimmed);
|
|
515
|
+
};
|
|
516
|
+
if (Array.isArray(request.hard_blockers)) {
|
|
517
|
+
for (const blocker of request.hard_blockers) add(blocker);
|
|
518
|
+
}
|
|
519
|
+
add(state?.structured_interaction_capture_failure_summary);
|
|
520
|
+
add(state?.structured_interaction_failure_summary);
|
|
521
|
+
const mergeRecommendation = String(state?.merge_recommendation || "").trim();
|
|
522
|
+
if (mergeRecommendation === "do-not-merge" && blockers.length) {
|
|
523
|
+
add("merge_recommendation=do-not-merge because the proof bundle contains hard blockers.");
|
|
524
|
+
}
|
|
525
|
+
return blockers;
|
|
526
|
+
}
|
|
508
527
|
function visualDeltaEvidenceIssueCode(state = {}, blocker = "") {
|
|
509
528
|
const visualDelta = visualDeltaForState(state || {});
|
|
510
529
|
const status = String(visualDelta.status || "").trim();
|
|
@@ -539,6 +558,7 @@ function validateShipGate(state = {}) {
|
|
|
539
558
|
const visualDelta = visualDeltaForState(state);
|
|
540
559
|
const visualDeltaRequired = visualDeltaRequiredForState(state);
|
|
541
560
|
const visualDeltaBlocker = visualDeltaShipGateReason(state);
|
|
561
|
+
const hardBlockers = proofAssessmentHardBlockersForState(state);
|
|
542
562
|
const reasons = [];
|
|
543
563
|
if (!["before", "prod", "both"].includes(reference)) {
|
|
544
564
|
reasons.push(`reference must be before, prod, or both; got ${reference}`);
|
|
@@ -570,6 +590,9 @@ function validateShipGate(state = {}) {
|
|
|
570
590
|
if (visualDeltaBlocker) {
|
|
571
591
|
reasons.push(visualDeltaBlocker);
|
|
572
592
|
}
|
|
593
|
+
for (const blocker of hardBlockers) {
|
|
594
|
+
reasons.push(`proof hard blocker prevents ready_to_ship: ${blocker}`);
|
|
595
|
+
}
|
|
573
596
|
return {
|
|
574
597
|
ok: reasons.length === 0,
|
|
575
598
|
reasons,
|
|
@@ -586,7 +609,8 @@ function validateShipGate(state = {}) {
|
|
|
586
609
|
proof_assessment_source: proofAssessment.source,
|
|
587
610
|
visual_delta_required: visualDeltaRequired,
|
|
588
611
|
visual_delta_status: typeof visualDelta.status === "string" ? visualDelta.status : null,
|
|
589
|
-
visual_delta_passed: typeof visualDelta.passed === "boolean" ? visualDelta.passed : null
|
|
612
|
+
visual_delta_passed: typeof visualDelta.passed === "boolean" ? visualDelta.passed : null,
|
|
613
|
+
hard_blockers: hardBlockers
|
|
590
614
|
}
|
|
591
615
|
};
|
|
592
616
|
}
|
|
@@ -700,6 +724,10 @@ var CHECKPOINT_CONTRACT_SPECS = {
|
|
|
700
724
|
}],
|
|
701
725
|
required_state: ["verify_decision_request"]
|
|
702
726
|
},
|
|
727
|
+
verify_capture_blocked: {
|
|
728
|
+
purpose: "Verify capture produced conclusive failed browser evidence and should stop instead of retrying proof authoring.",
|
|
729
|
+
required_state: ["verify_decision_request"]
|
|
730
|
+
},
|
|
703
731
|
verify_supervisor_judgment: {
|
|
704
732
|
purpose: "Supervising agent judges whether captured evidence proves the change is ready to ship.",
|
|
705
733
|
accepted_inputs: [{
|
|
@@ -1284,14 +1312,18 @@ function verifyAssessment(state) {
|
|
|
1284
1312
|
};
|
|
1285
1313
|
}
|
|
1286
1314
|
if (state?.verify_status === "capture_incomplete") {
|
|
1315
|
+
const captureQuality = verifyDecision?.capture_quality || {};
|
|
1316
|
+
const terminalBlocker = captureQuality?.terminal_blocker === true || captureQuality?.blocking === true;
|
|
1317
|
+
const recommendedStage = terminalBlocker ? null : verifyDecision?.continue_with_stage || verifyDecision?.recommended_stage || "author";
|
|
1318
|
+
const continueWithStage = terminalBlocker ? null : verifyDecision?.continue_with_stage || verifyDecision?.recommended_stage || "author";
|
|
1287
1319
|
return {
|
|
1288
|
-
decision:
|
|
1320
|
+
decision: captureQuality?.decision || "revise_capture",
|
|
1289
1321
|
summary: verifyDecision?.summary || "Verify needs another internal capture iteration before the evidence can be judged.",
|
|
1290
|
-
recommendedStage
|
|
1291
|
-
continueWithStage
|
|
1322
|
+
recommendedStage,
|
|
1323
|
+
continueWithStage,
|
|
1292
1324
|
escalationTarget: "agent",
|
|
1293
|
-
reasons: Array.isArray(
|
|
1294
|
-
raw:
|
|
1325
|
+
reasons: Array.isArray(captureQuality?.reasons) ? captureQuality.reasons : [],
|
|
1326
|
+
raw: captureQuality || verifyDecision,
|
|
1295
1327
|
source: "workflow_capture"
|
|
1296
1328
|
};
|
|
1297
1329
|
}
|
|
@@ -2511,7 +2543,9 @@ ${implementRes.stderr || ""}`;
|
|
|
2511
2543
|
convergenceSignals
|
|
2512
2544
|
};
|
|
2513
2545
|
if (verifyStatus !== "evidence_captured") {
|
|
2514
|
-
|
|
2546
|
+
const captureQuality = verifyDecisionRequest?.capture_quality || {};
|
|
2547
|
+
const captureTerminalBlocker = captureQuality?.terminal_blocker === true || captureQuality?.blocking === true;
|
|
2548
|
+
if (!captureTerminalBlocker && (verifyContinueWithStage || verifyRecommendedStage || "author") === "author") {
|
|
2515
2549
|
updateState(config.statePath, (currentState) => {
|
|
2516
2550
|
currentState.author_status = "needs_authoring";
|
|
2517
2551
|
currentState.proof_plan_status = "needs_authoring";
|
|
@@ -2519,7 +2553,7 @@ ${implementRes.stderr || ""}`;
|
|
|
2519
2553
|
});
|
|
2520
2554
|
state = readState(config.statePath);
|
|
2521
2555
|
}
|
|
2522
|
-
const checkpointName = "verify_capture_retry";
|
|
2556
|
+
const checkpointName = captureTerminalBlocker ? "verify_capture_blocked" : "verify_capture_retry";
|
|
2523
2557
|
const summary = stringValue(proofAssessment.summary) || "Verify ran, but the proof packet still needs internal capture-plan work before it should ship.";
|
|
2524
2558
|
recordAttempt("verify", "checkpoint", summary, {
|
|
2525
2559
|
autoApproved: verifyRes.autoApproved || false,
|
|
@@ -2532,11 +2566,11 @@ ${implementRes.stderr || ""}`;
|
|
|
2532
2566
|
summary,
|
|
2533
2567
|
{
|
|
2534
2568
|
ok: true,
|
|
2535
|
-
nextActions: ["inspect_after_capture", "continue_internal_loop_with_checkpoint", "return_to_recon_if_baseline_is_wrong"],
|
|
2569
|
+
nextActions: captureTerminalBlocker ? ["inspect_after_capture", "report_specific_browser_evidence_blocker", "start_a_new_run_after_the_product_or_script_is_fixed"] : ["inspect_after_capture", "continue_internal_loop_with_checkpoint", "return_to_recon_if_baseline_is_wrong"],
|
|
2536
2570
|
advanceOptions: verifyLoopAdvanceOptions,
|
|
2537
|
-
recommendedAdvanceStage: verifyRecommendedStage || "author",
|
|
2538
|
-
continueWithStage: verifyContinueWithStage || "author",
|
|
2539
|
-
blocking:
|
|
2571
|
+
recommendedAdvanceStage: captureTerminalBlocker ? null : verifyRecommendedStage || "author",
|
|
2572
|
+
continueWithStage: captureTerminalBlocker ? null : verifyContinueWithStage || "author",
|
|
2573
|
+
blocking: captureTerminalBlocker,
|
|
2540
2574
|
details: verifyDetails,
|
|
2541
2575
|
verifyStatus,
|
|
2542
2576
|
verifySummary,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import './proof-run-core-
|
|
2
|
-
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-
|
|
1
|
+
import './proof-run-core-C8FDUhle.cjs';
|
|
2
|
+
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-D80hVFMf.cjs';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import './proof-run-core-
|
|
2
|
-
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-
|
|
1
|
+
import './proof-run-core-C8FDUhle.js';
|
|
2
|
+
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-By7oLsF-.js';
|
package/dist/proof-run-engine.js
CHANGED
package/dist/runner.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runRiddleProof
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NGX4SUQN.js";
|
|
4
4
|
import "./chunk-YZUVEJ5B.js";
|
|
5
5
|
import "./chunk-FMOYUYH2.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-7GZY5PLT.js";
|
|
7
7
|
import "./chunk-4FOHZ7JG.js";
|
|
8
8
|
import "./chunk-VY4Y5U57.js";
|
|
9
9
|
import "./chunk-MLKGABMK.js";
|
package/package.json
CHANGED
package/runtime/lib/verify.py
CHANGED
|
@@ -2029,7 +2029,7 @@ def interaction_proof_route_match(expected_path, proof_evidence):
|
|
|
2029
2029
|
return None
|
|
2030
2030
|
for record in proof_evidence_records_deep(proof_evidence):
|
|
2031
2031
|
flag = explicit_route_match_flag(record)
|
|
2032
|
-
candidate =
|
|
2032
|
+
candidate = observed_terminal_path_from_record(record)
|
|
2033
2033
|
if candidate and route_matches_expected(expected, candidate):
|
|
2034
2034
|
return {
|
|
2035
2035
|
'matched': True,
|
|
@@ -2042,26 +2042,84 @@ def interaction_proof_route_match(expected_path, proof_evidence):
|
|
|
2042
2042
|
|
|
2043
2043
|
|
|
2044
2044
|
EXPLICIT_TERMINAL_PATH_KEYS = (
|
|
2045
|
+
'expected_url', 'expectedUrl',
|
|
2046
|
+
'expected_href', 'expectedHref',
|
|
2045
2047
|
'expected_terminal_path', 'expectedTerminalPath',
|
|
2046
2048
|
'expected_terminal_url', 'expectedTerminalUrl',
|
|
2049
|
+
'expected_terminal_href', 'expectedTerminalHref',
|
|
2047
2050
|
'expected_terminal_route', 'expectedTerminalRoute',
|
|
2051
|
+
'expected_route', 'expectedRoute',
|
|
2048
2052
|
'terminal_path', 'terminalPath',
|
|
2049
2053
|
'terminal_url', 'terminalUrl',
|
|
2054
|
+
'terminal_href', 'terminalHref',
|
|
2050
2055
|
'terminal_route', 'terminalRoute',
|
|
2051
2056
|
'expected_after_path', 'expectedAfterPath',
|
|
2052
2057
|
'expected_after_url', 'expectedAfterUrl',
|
|
2058
|
+
'expected_after_href', 'expectedAfterHref',
|
|
2053
2059
|
'expected_after_route', 'expectedAfterRoute',
|
|
2054
2060
|
'after_path', 'afterPath',
|
|
2055
2061
|
'after_url', 'afterUrl',
|
|
2062
|
+
'after_href', 'afterHref',
|
|
2056
2063
|
'after_route', 'afterRoute',
|
|
2057
2064
|
'expected_final_path', 'expectedFinalPath',
|
|
2058
2065
|
'expected_final_url', 'expectedFinalUrl',
|
|
2066
|
+
'expected_final_href', 'expectedFinalHref',
|
|
2059
2067
|
'expected_final_route', 'expectedFinalRoute',
|
|
2060
2068
|
'final_path', 'finalPath',
|
|
2061
2069
|
'final_url', 'finalUrl',
|
|
2070
|
+
'final_href', 'finalHref',
|
|
2062
2071
|
'final_route', 'finalRoute',
|
|
2063
2072
|
)
|
|
2064
|
-
|
|
2073
|
+
EXPECTED_TERMINAL_PATH_KEYS = (
|
|
2074
|
+
'expected_url', 'expectedUrl',
|
|
2075
|
+
'expected_href', 'expectedHref',
|
|
2076
|
+
'expected_path', 'expectedPath',
|
|
2077
|
+
'expected_route', 'expectedRoute',
|
|
2078
|
+
'expected_terminal_path', 'expectedTerminalPath',
|
|
2079
|
+
'expected_terminal_url', 'expectedTerminalUrl',
|
|
2080
|
+
'expected_terminal_href', 'expectedTerminalHref',
|
|
2081
|
+
'expected_terminal_route', 'expectedTerminalRoute',
|
|
2082
|
+
'expected_after_path', 'expectedAfterPath',
|
|
2083
|
+
'expected_after_url', 'expectedAfterUrl',
|
|
2084
|
+
'expected_after_href', 'expectedAfterHref',
|
|
2085
|
+
'expected_after_route', 'expectedAfterRoute',
|
|
2086
|
+
'expected_final_path', 'expectedFinalPath',
|
|
2087
|
+
'expected_final_url', 'expectedFinalUrl',
|
|
2088
|
+
'expected_final_href', 'expectedFinalHref',
|
|
2089
|
+
'expected_final_route', 'expectedFinalRoute',
|
|
2090
|
+
)
|
|
2091
|
+
OBSERVED_TERMINAL_PATH_KEYS = (
|
|
2092
|
+
'terminal_path', 'terminalPath',
|
|
2093
|
+
'terminal_url', 'terminalUrl',
|
|
2094
|
+
'terminal_href', 'terminalHref',
|
|
2095
|
+
'terminal_route', 'terminalRoute',
|
|
2096
|
+
'after_path', 'afterPath',
|
|
2097
|
+
'after_url', 'afterUrl',
|
|
2098
|
+
'after_href', 'afterHref',
|
|
2099
|
+
'after_route', 'afterRoute',
|
|
2100
|
+
'final_path', 'finalPath',
|
|
2101
|
+
'final_url', 'finalUrl',
|
|
2102
|
+
'final_href', 'finalHref',
|
|
2103
|
+
'final_route', 'finalRoute',
|
|
2104
|
+
)
|
|
2105
|
+
FULL_LOCATION_PATH_KEYS = (
|
|
2106
|
+
'url', 'href',
|
|
2107
|
+
'hrefNormalized', 'href_normalized',
|
|
2108
|
+
'terminalUrl', 'terminal_url',
|
|
2109
|
+
'afterUrl', 'after_url',
|
|
2110
|
+
'finalUrl', 'final_url',
|
|
2111
|
+
'currentUrl', 'current_url',
|
|
2112
|
+
'pathWithSearchAndHash', 'path_with_search_and_hash',
|
|
2113
|
+
'fullPath', 'full_path',
|
|
2114
|
+
)
|
|
2115
|
+
PARTIAL_LOCATION_PATH_KEYS = (
|
|
2116
|
+
'route',
|
|
2117
|
+
'path',
|
|
2118
|
+
'pathname',
|
|
2119
|
+
'normalizedPath', 'normalized_path',
|
|
2120
|
+
'rawPath', 'raw_path',
|
|
2121
|
+
)
|
|
2122
|
+
LOCATION_PATH_KEYS = FULL_LOCATION_PATH_KEYS + PARTIAL_LOCATION_PATH_KEYS
|
|
2065
2123
|
AFTER_STATE_KEYS = (
|
|
2066
2124
|
'after', 'after_state', 'afterState',
|
|
2067
2125
|
'expected_after', 'expectedAfter',
|
|
@@ -2070,6 +2128,19 @@ AFTER_STATE_KEYS = (
|
|
|
2070
2128
|
'final', 'final_state', 'finalState',
|
|
2071
2129
|
'expected_final', 'expectedFinal',
|
|
2072
2130
|
)
|
|
2131
|
+
EXPECTED_STATE_KEYS = (
|
|
2132
|
+
'expected', 'expectation',
|
|
2133
|
+
'route_expectation', 'routeExpectation',
|
|
2134
|
+
'expected_after', 'expectedAfter',
|
|
2135
|
+
'expected_terminal', 'expectedTerminal',
|
|
2136
|
+
'expected_final', 'expectedFinal',
|
|
2137
|
+
)
|
|
2138
|
+
OBSERVED_STATE_KEYS = (
|
|
2139
|
+
'observed', 'actual',
|
|
2140
|
+
'after', 'after_state', 'afterState',
|
|
2141
|
+
'terminal', 'terminal_state', 'terminalState',
|
|
2142
|
+
'final', 'final_state', 'finalState',
|
|
2143
|
+
)
|
|
2073
2144
|
EVIDENCE_CONTAINER_KEYS = (
|
|
2074
2145
|
'proofEvidence', 'proof_evidence',
|
|
2075
2146
|
'interactionEvidence', 'interaction_evidence',
|
|
@@ -2096,10 +2167,10 @@ def path_candidate(value):
|
|
|
2096
2167
|
return ''
|
|
2097
2168
|
|
|
2098
2169
|
|
|
2099
|
-
def
|
|
2170
|
+
def record_path_candidate_for_keys(record, keys, allow_location_keys=False):
|
|
2100
2171
|
if not isinstance(record, dict):
|
|
2101
2172
|
return ''
|
|
2102
|
-
keys = list(
|
|
2173
|
+
keys = list(keys)
|
|
2103
2174
|
if allow_location_keys:
|
|
2104
2175
|
keys.extend(LOCATION_PATH_KEYS)
|
|
2105
2176
|
for key in keys:
|
|
@@ -2109,6 +2180,10 @@ def record_path_candidate(record, allow_location_keys=False):
|
|
|
2109
2180
|
return ''
|
|
2110
2181
|
|
|
2111
2182
|
|
|
2183
|
+
def record_path_candidate(record, allow_location_keys=False):
|
|
2184
|
+
return record_path_candidate_for_keys(record, EXPLICIT_TERMINAL_PATH_KEYS, allow_location_keys)
|
|
2185
|
+
|
|
2186
|
+
|
|
2112
2187
|
def terminal_path_from_record(record, depth=0):
|
|
2113
2188
|
if not isinstance(record, dict) or depth > 4:
|
|
2114
2189
|
return ''
|
|
@@ -2151,6 +2226,74 @@ def terminal_path_from_record(record, depth=0):
|
|
|
2151
2226
|
return ''
|
|
2152
2227
|
|
|
2153
2228
|
|
|
2229
|
+
def expected_terminal_path_from_record(record, depth=0):
|
|
2230
|
+
if not isinstance(record, dict) or depth > 4:
|
|
2231
|
+
return ''
|
|
2232
|
+
candidate = record_path_candidate_for_keys(record, EXPECTED_TERMINAL_PATH_KEYS)
|
|
2233
|
+
if candidate:
|
|
2234
|
+
return candidate
|
|
2235
|
+
for key in EXPECTED_STATE_KEYS:
|
|
2236
|
+
value = record.get(key)
|
|
2237
|
+
if isinstance(value, dict):
|
|
2238
|
+
candidate = (
|
|
2239
|
+
record_path_candidate_for_keys(value, EXPECTED_TERMINAL_PATH_KEYS, allow_location_keys=True)
|
|
2240
|
+
or expected_terminal_path_from_record(value, depth + 1)
|
|
2241
|
+
)
|
|
2242
|
+
if candidate:
|
|
2243
|
+
return candidate
|
|
2244
|
+
elif isinstance(value, list):
|
|
2245
|
+
for item in value:
|
|
2246
|
+
candidate = expected_terminal_path_from_record(item, depth + 1)
|
|
2247
|
+
if candidate:
|
|
2248
|
+
return candidate
|
|
2249
|
+
for key in EVIDENCE_CONTAINER_KEYS:
|
|
2250
|
+
value = record.get(key)
|
|
2251
|
+
if isinstance(value, dict):
|
|
2252
|
+
candidate = expected_terminal_path_from_record(value, depth + 1)
|
|
2253
|
+
if candidate:
|
|
2254
|
+
return candidate
|
|
2255
|
+
elif isinstance(value, list):
|
|
2256
|
+
for item in value:
|
|
2257
|
+
candidate = expected_terminal_path_from_record(item, depth + 1)
|
|
2258
|
+
if candidate:
|
|
2259
|
+
return candidate
|
|
2260
|
+
return ''
|
|
2261
|
+
|
|
2262
|
+
|
|
2263
|
+
def observed_terminal_path_from_record(record, depth=0):
|
|
2264
|
+
if not isinstance(record, dict) or depth > 4:
|
|
2265
|
+
return ''
|
|
2266
|
+
candidate = record_path_candidate_for_keys(record, OBSERVED_TERMINAL_PATH_KEYS)
|
|
2267
|
+
if candidate:
|
|
2268
|
+
return candidate
|
|
2269
|
+
for key in OBSERVED_STATE_KEYS:
|
|
2270
|
+
value = record.get(key)
|
|
2271
|
+
if isinstance(value, dict):
|
|
2272
|
+
candidate = (
|
|
2273
|
+
record_path_candidate_for_keys(value, OBSERVED_TERMINAL_PATH_KEYS, allow_location_keys=True)
|
|
2274
|
+
or observed_terminal_path_from_record(value, depth + 1)
|
|
2275
|
+
)
|
|
2276
|
+
if candidate:
|
|
2277
|
+
return candidate
|
|
2278
|
+
elif isinstance(value, list):
|
|
2279
|
+
for item in value:
|
|
2280
|
+
candidate = observed_terminal_path_from_record(item, depth + 1)
|
|
2281
|
+
if candidate:
|
|
2282
|
+
return candidate
|
|
2283
|
+
for key in EVIDENCE_CONTAINER_KEYS:
|
|
2284
|
+
value = record.get(key)
|
|
2285
|
+
if isinstance(value, dict):
|
|
2286
|
+
candidate = observed_terminal_path_from_record(value, depth + 1)
|
|
2287
|
+
if candidate:
|
|
2288
|
+
return candidate
|
|
2289
|
+
elif isinstance(value, list):
|
|
2290
|
+
for item in value:
|
|
2291
|
+
candidate = observed_terminal_path_from_record(item, depth + 1)
|
|
2292
|
+
if candidate:
|
|
2293
|
+
return candidate
|
|
2294
|
+
return ''
|
|
2295
|
+
|
|
2296
|
+
|
|
2154
2297
|
def text_path_candidate(value):
|
|
2155
2298
|
if not isinstance(value, str):
|
|
2156
2299
|
return ''
|
|
@@ -2383,19 +2526,41 @@ def failed_interaction_evidence_summary(proof_evidence):
|
|
|
2383
2526
|
return summary
|
|
2384
2527
|
|
|
2385
2528
|
|
|
2529
|
+
def interaction_capture_failure_evidence_summary(proof_evidence):
|
|
2530
|
+
for record in proof_evidence_records_deep(proof_evidence):
|
|
2531
|
+
if not isinstance(record, dict):
|
|
2532
|
+
continue
|
|
2533
|
+
version = str(record.get('version') or '').strip()
|
|
2534
|
+
source = str(record.get('source') or '').strip()
|
|
2535
|
+
if version != 'riddle-proof.interaction.capture-failure.v1' and source != 'verify_capture_failure':
|
|
2536
|
+
continue
|
|
2537
|
+
summary = str(record.get('evidence_summary') or '').strip() or 'Interaction capture failed before usable authored proof evidence was emitted.'
|
|
2538
|
+
failures = collect_interaction_failed_assertions(record)
|
|
2539
|
+
if failures:
|
|
2540
|
+
summary += ' Failed checks: ' + ', '.join(failures[:8]) + '.'
|
|
2541
|
+
error = str(record.get('capture_error') or record.get('error') or '').strip()
|
|
2542
|
+
if error:
|
|
2543
|
+
summary += ' Capture script error: ' + error[:300]
|
|
2544
|
+
return summary
|
|
2545
|
+
return ''
|
|
2546
|
+
|
|
2547
|
+
|
|
2386
2548
|
def interaction_terminal_path_from_evidence(proof_evidence):
|
|
2387
2549
|
for record in proof_evidence_records(proof_evidence):
|
|
2388
|
-
candidate =
|
|
2550
|
+
candidate = expected_terminal_path_from_record(record)
|
|
2389
2551
|
if candidate:
|
|
2390
2552
|
return candidate, 'proof_evidence_contract'
|
|
2391
|
-
|
|
2553
|
+
route_evidence_passed = interaction_assertions_pass(proof_evidence)
|
|
2554
|
+
if not route_evidence_passed:
|
|
2555
|
+
for record in proof_evidence_records_deep(proof_evidence):
|
|
2556
|
+
if interaction_assertions_pass(record) or explicit_route_match_flag(record) is True:
|
|
2557
|
+
route_evidence_passed = True
|
|
2558
|
+
break
|
|
2559
|
+
if route_evidence_passed:
|
|
2392
2560
|
for record in proof_evidence_records(proof_evidence):
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
candidate = record_path_candidate(value, allow_location_keys=True)
|
|
2397
|
-
if candidate:
|
|
2398
|
-
return candidate, 'proof_evidence_after_state'
|
|
2561
|
+
candidate = observed_terminal_path_from_record(record)
|
|
2562
|
+
if candidate:
|
|
2563
|
+
return candidate, 'proof_evidence_contract'
|
|
2399
2564
|
return '', ''
|
|
2400
2565
|
|
|
2401
2566
|
|
|
@@ -2428,6 +2593,33 @@ def interaction_terminal_path_from_state(state):
|
|
|
2428
2593
|
return '', ''
|
|
2429
2594
|
|
|
2430
2595
|
|
|
2596
|
+
def proof_evidence_should_override_state_terminal_path(state_candidate, evidence_candidate, proof_evidence):
|
|
2597
|
+
if not evidence_candidate:
|
|
2598
|
+
return False
|
|
2599
|
+
if not state_candidate:
|
|
2600
|
+
return True
|
|
2601
|
+
state_parts = route_parts(state_candidate)
|
|
2602
|
+
evidence_parts = route_parts(evidence_candidate)
|
|
2603
|
+
if (
|
|
2604
|
+
state_parts.get('pathname') == evidence_parts.get('pathname')
|
|
2605
|
+
and (
|
|
2606
|
+
(evidence_parts.get('query') and not state_parts.get('query'))
|
|
2607
|
+
or (evidence_parts.get('hash') and not state_parts.get('hash'))
|
|
2608
|
+
)
|
|
2609
|
+
):
|
|
2610
|
+
return True
|
|
2611
|
+
if route_matches_expected(state_candidate, evidence_candidate):
|
|
2612
|
+
return False
|
|
2613
|
+
if interaction_assertions_pass(proof_evidence):
|
|
2614
|
+
return True
|
|
2615
|
+
for record in proof_evidence_records_deep(proof_evidence):
|
|
2616
|
+
if interaction_assertions_pass(record):
|
|
2617
|
+
return True
|
|
2618
|
+
if explicit_route_match_flag(record) is True:
|
|
2619
|
+
return True
|
|
2620
|
+
return False
|
|
2621
|
+
|
|
2622
|
+
|
|
2431
2623
|
def expected_path_for_verify(state, start_path, proof_evidence):
|
|
2432
2624
|
mode = normalized_verification_mode(state.get('verification_mode'))
|
|
2433
2625
|
normalized_start = normalize_observed_path(start_path) or '/'
|
|
@@ -2445,9 +2637,14 @@ def expected_path_for_verify(state, start_path, proof_evidence):
|
|
|
2445
2637
|
'expected_query': start_parts['query'],
|
|
2446
2638
|
'expected_hash': start_parts['hash'],
|
|
2447
2639
|
}
|
|
2448
|
-
|
|
2640
|
+
state_candidate, state_source = interaction_terminal_path_from_state(state)
|
|
2641
|
+
evidence_candidate, evidence_source = interaction_terminal_path_from_evidence(proof_evidence)
|
|
2642
|
+
if proof_evidence_should_override_state_terminal_path(state_candidate, evidence_candidate, proof_evidence):
|
|
2643
|
+
candidate, source = evidence_candidate, evidence_source
|
|
2644
|
+
else:
|
|
2645
|
+
candidate, source = state_candidate, state_source
|
|
2449
2646
|
if not candidate:
|
|
2450
|
-
candidate, source =
|
|
2647
|
+
candidate, source = evidence_candidate, evidence_source
|
|
2451
2648
|
expected = candidate or normalized_start
|
|
2452
2649
|
expected_parts = route_parts(expected)
|
|
2453
2650
|
return expected, {
|
|
@@ -2838,10 +3035,17 @@ def build_capture_retry_decision(after_observation, required_baseline_present, p
|
|
|
2838
3035
|
|
|
2839
3036
|
if proof_evidence_blocker:
|
|
2840
3037
|
reasons.append(proof_evidence_blocker)
|
|
2841
|
-
|
|
3038
|
+
interaction_capture_blocker = (
|
|
3039
|
+
proof_evidence_blocker.startswith('Interaction capture ')
|
|
3040
|
+
or 'Interaction capture failed before usable authored proof evidence was emitted' in proof_evidence_blocker
|
|
3041
|
+
or 'Interaction capture reached a different terminal route' in proof_evidence_blocker
|
|
3042
|
+
)
|
|
3043
|
+
decision = 'failed_interaction_capture' if interaction_capture_blocker else 'missing_proof_evidence'
|
|
2842
3044
|
if 'proof_evidence_present=false' in proof_evidence_blocker:
|
|
2843
3045
|
decision = 'failed_proof_evidence'
|
|
2844
3046
|
reasons.append('The capture reached usable page context, but the proof evidence explicitly failed its own required audio gate.')
|
|
3047
|
+
elif interaction_capture_blocker:
|
|
3048
|
+
reasons.append('The capture produced conclusive structured interaction-failure evidence, so this run should block with that specific browser evidence instead of re-authoring in a loop.')
|
|
2845
3049
|
else:
|
|
2846
3050
|
reasons.append('The capture reached usable page context, but the proof script did not emit the structured evidence required for this verification mode.')
|
|
2847
3051
|
if route_mismatch:
|
|
@@ -2852,7 +3056,10 @@ def build_capture_retry_decision(after_observation, required_baseline_present, p
|
|
|
2852
3056
|
(route_mismatch.get('observed_after_path') or '(unknown)') +
|
|
2853
3057
|
'.'
|
|
2854
3058
|
)
|
|
2855
|
-
|
|
3059
|
+
if interaction_capture_blocker:
|
|
3060
|
+
reasons.append('Do not ask the authoring loop to infer a new route; the captured browser evidence is the terminal blocker.')
|
|
3061
|
+
else:
|
|
3062
|
+
reasons.append('Return to author so the capture script can expose passing proof evidence before verify asks for a supervising-agent judgment.')
|
|
2856
3063
|
summary = proof_evidence_blocker
|
|
2857
3064
|
if route_mismatch:
|
|
2858
3065
|
summary += (
|
|
@@ -2868,8 +3075,10 @@ def build_capture_retry_decision(after_observation, required_baseline_present, p
|
|
|
2868
3075
|
return {
|
|
2869
3076
|
'decision': decision,
|
|
2870
3077
|
'summary': summary,
|
|
2871
|
-
'recommended_stage': 'author',
|
|
2872
|
-
'continue_with_stage': 'author',
|
|
3078
|
+
'recommended_stage': None if interaction_capture_blocker else 'author',
|
|
3079
|
+
'continue_with_stage': None if interaction_capture_blocker else 'author',
|
|
3080
|
+
'blocking': bool(interaction_capture_blocker),
|
|
3081
|
+
'terminal_blocker': bool(interaction_capture_blocker),
|
|
2873
3082
|
'reasons': reasons,
|
|
2874
3083
|
'mismatch': route_mismatch,
|
|
2875
3084
|
}
|
|
@@ -2883,6 +3092,25 @@ def build_capture_retry_decision(after_observation, required_baseline_present, p
|
|
|
2883
3092
|
and route_expectation.get('terminal_path')
|
|
2884
3093
|
and route_expectation_source != 'recon_start_path'
|
|
2885
3094
|
)
|
|
3095
|
+
if authored_terminal_route:
|
|
3096
|
+
expected = route_mismatch.get('expected_path') or ''
|
|
3097
|
+
observed = route_mismatch.get('observed_after_path') or ''
|
|
3098
|
+
summary = 'Interaction proof terminal route mismatch: expected ' + (expected or '(unknown)') + ', got ' + (observed or '(unknown)') + '.'
|
|
3099
|
+
if error_messages:
|
|
3100
|
+
reasons.append('Capture script error: ' + error_messages[0][:500])
|
|
3101
|
+
summary += ' Capture script error: ' + error_messages[0][:300]
|
|
3102
|
+
reasons.append('Route mismatch: expected after capture path ' + (expected or '(unknown)') + ', observed ' + (observed or '(unknown)') + '.')
|
|
3103
|
+
reasons.append('The terminal route came from authored interaction proof evidence, so the captured browser evidence is the terminal blocker instead of an author retry loop.')
|
|
3104
|
+
return {
|
|
3105
|
+
'decision': 'failed_interaction_capture',
|
|
3106
|
+
'summary': summary,
|
|
3107
|
+
'recommended_stage': None,
|
|
3108
|
+
'continue_with_stage': None,
|
|
3109
|
+
'blocking': True,
|
|
3110
|
+
'terminal_blocker': True,
|
|
3111
|
+
'reasons': reasons,
|
|
3112
|
+
'mismatch': route_mismatch,
|
|
3113
|
+
}
|
|
2886
3114
|
recommended_stage = 'recon' if 'wrong route' in reason and not authored_terminal_route else 'author'
|
|
2887
3115
|
mismatch = None
|
|
2888
3116
|
if recommended_stage == 'recon':
|
|
@@ -3645,12 +3873,17 @@ if proof_evidence_required_for_mode(s.get('verification_mode')):
|
|
|
3645
3873
|
summary_lines.append('Structured proof evidence gate: ' + proof_evidence_blocker)
|
|
3646
3874
|
|
|
3647
3875
|
structured_interaction_failure_summary = ''
|
|
3876
|
+
structured_interaction_capture_failure_summary = ''
|
|
3648
3877
|
proof_evidence = evidence_bundle.get('proof_evidence')
|
|
3649
3878
|
if verification_mode in INTERACTION_MODES and proof_evidence is not None:
|
|
3650
3879
|
structured_interaction_failure_summary = failed_interaction_evidence_summary(proof_evidence)
|
|
3880
|
+
structured_interaction_capture_failure_summary = interaction_capture_failure_evidence_summary(proof_evidence)
|
|
3651
3881
|
if structured_interaction_failure_summary:
|
|
3652
3882
|
summary_lines.append('Structured interaction evidence gate: ' + structured_interaction_failure_summary)
|
|
3883
|
+
if structured_interaction_capture_failure_summary:
|
|
3884
|
+
summary_lines.append('Structured interaction capture blocker: ' + structured_interaction_capture_failure_summary)
|
|
3653
3885
|
s['structured_interaction_failure_summary'] = structured_interaction_failure_summary
|
|
3886
|
+
s['structured_interaction_capture_failure_summary'] = structured_interaction_capture_failure_summary
|
|
3654
3887
|
|
|
3655
3888
|
visual_delta_recovery = build_visual_delta_recovery_decision(
|
|
3656
3889
|
s.get('verification_mode'),
|
|
@@ -3662,6 +3895,7 @@ if visual_delta_recovery:
|
|
|
3662
3895
|
|
|
3663
3896
|
has_judgable_failed_interaction_evidence = (
|
|
3664
3897
|
bool(structured_interaction_failure_summary)
|
|
3898
|
+
and not structured_interaction_capture_failure_summary
|
|
3665
3899
|
and required_baseline_present
|
|
3666
3900
|
and not proof_evidence_blocker
|
|
3667
3901
|
and not visual_delta_recovery
|
|
@@ -3670,6 +3904,7 @@ has_good_evidence = (
|
|
|
3670
3904
|
required_baseline_present
|
|
3671
3905
|
and (after_observation.get('valid') or has_judgable_failed_interaction_evidence)
|
|
3672
3906
|
and not proof_evidence_blocker
|
|
3907
|
+
and not structured_interaction_capture_failure_summary
|
|
3673
3908
|
and not visual_delta_recovery
|
|
3674
3909
|
)
|
|
3675
3910
|
|
|
@@ -3726,7 +3961,12 @@ if has_good_evidence:
|
|
|
3726
3961
|
summary_lines.append('Proof assessment: awaiting supervising agent judgment')
|
|
3727
3962
|
summary_lines.append('Proof next stage: supervising agent decides after reviewing the evidence packet')
|
|
3728
3963
|
else:
|
|
3729
|
-
capture_retry = build_capture_retry_decision(
|
|
3964
|
+
capture_retry = build_capture_retry_decision(
|
|
3965
|
+
after_observation,
|
|
3966
|
+
required_baseline_present,
|
|
3967
|
+
proof_evidence_blocker or structured_interaction_capture_failure_summary,
|
|
3968
|
+
s.get('route_expectation') or {},
|
|
3969
|
+
)
|
|
3730
3970
|
if visual_delta_recovery:
|
|
3731
3971
|
observation_reason = str(after_observation.get('reason') or '')
|
|
3732
3972
|
observation_details = after_observation.get('details') if isinstance(after_observation.get('details'), dict) else {}
|
|
@@ -3735,6 +3975,7 @@ else:
|
|
|
3735
3975
|
or 'console/runtime errors' in observation_reason
|
|
3736
3976
|
or (observation_details.get('capture_error_messages') or [])
|
|
3737
3977
|
or proof_evidence_blocker
|
|
3978
|
+
or structured_interaction_capture_failure_summary
|
|
3738
3979
|
)
|
|
3739
3980
|
if has_primary_capture_failure:
|
|
3740
3981
|
capture_retry['visual_delta_recovery'] = visual_delta_recovery
|
|
@@ -3742,6 +3983,9 @@ else:
|
|
|
3742
3983
|
else:
|
|
3743
3984
|
capture_retry = visual_delta_recovery
|
|
3744
3985
|
next_stage_options = ['author', 'verify', 'recon'] if no_implementation_mode else ['author', 'verify', 'implement', 'recon']
|
|
3986
|
+
capture_terminal_blocker = bool(capture_retry.get('terminal_blocker') or capture_retry.get('blocking'))
|
|
3987
|
+
recommended_stage = None if capture_terminal_blocker else (capture_retry.get('recommended_stage') or 'author')
|
|
3988
|
+
continue_with_stage = None if capture_terminal_blocker else (capture_retry.get('continue_with_stage') or 'author')
|
|
3745
3989
|
s['verify_status'] = 'capture_incomplete'
|
|
3746
3990
|
s['merge_recommendation'] = 'do-not-merge'
|
|
3747
3991
|
s['proof_assessment'] = {}
|
|
@@ -3756,8 +4000,8 @@ else:
|
|
|
3756
4000
|
'latest_observation': after_observation,
|
|
3757
4001
|
'capture_quality': capture_retry,
|
|
3758
4002
|
'next_stage_options': next_stage_options,
|
|
3759
|
-
'recommended_stage':
|
|
3760
|
-
'continue_with_stage':
|
|
4003
|
+
'recommended_stage': recommended_stage,
|
|
4004
|
+
'continue_with_stage': continue_with_stage,
|
|
3761
4005
|
'fields_agent_may_update': ['capture_script', 'server_path', 'wait_for_selector', 'proof_plan'],
|
|
3762
4006
|
'instructions': [
|
|
3763
4007
|
'The after-proof evidence packet is incomplete, so use the recommended stage before proof review.',
|
|
@@ -3767,7 +4011,7 @@ else:
|
|
|
3767
4011
|
],
|
|
3768
4012
|
}
|
|
3769
4013
|
summary_lines.append('Proof assessment: not yet possible because the after capture is still incomplete')
|
|
3770
|
-
summary_lines.append('Proof next stage: ' + str(
|
|
4014
|
+
summary_lines.append('Proof next stage: blocked' if capture_terminal_blocker else 'Proof next stage: ' + str(recommended_stage or 'author'))
|
|
3771
4015
|
|
|
3772
4016
|
s['verify_summary'] = '\n'.join(summary_lines)
|
|
3773
4017
|
s['proof_summary'] = s['verify_summary']
|