@riddledc/riddle-proof 0.8.3 → 0.8.5
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/adapters/openclaw.js +4 -4
- package/dist/advanced/engine-harness.cjs +34 -12
- package/dist/advanced/engine-harness.js +4 -4
- package/dist/advanced/index.cjs +34 -12
- package/dist/advanced/index.d.cts +1 -1
- package/dist/advanced/index.d.ts +1 -1
- package/dist/advanced/index.js +6 -6
- package/dist/advanced/proof-run-engine.cjs +29 -9
- package/dist/advanced/proof-run-engine.d.cts +1 -1
- package/dist/advanced/proof-run-engine.d.ts +1 -1
- package/dist/advanced/proof-run-engine.js +1 -1
- package/dist/advanced/runner.js +4 -4
- package/dist/checkpoint.cjs +3 -1
- package/dist/checkpoint.js +1 -1
- package/dist/{chunk-SKIAZTQ7.js → chunk-4FOHZ7JG.js} +3 -1
- package/dist/{chunk-YB5ACBZE.js → chunk-BDFSMWTI.js} +5 -5
- package/dist/{chunk-U4FUFBSH.js → chunk-FMOYUYH2.js} +1 -1
- package/dist/{chunk-TZ3YMCDM.js → chunk-GMZ57RRY.js} +29 -9
- package/dist/{chunk-OIFHYMHP.js → chunk-OD5UNE57.js} +2 -2
- package/dist/{chunk-SMBZT46I.js → chunk-RBWSCU6V.js} +1 -1
- package/dist/{chunk-ZX45XGDJ.js → chunk-UIJ7X63P.js} +1 -1
- package/dist/{chunk-TNCDVE5O.js → chunk-YZUVEJ5B.js} +1 -1
- package/dist/cli/index.js +5 -5
- package/dist/cli.cjs +34 -12
- package/dist/cli.js +5 -5
- package/dist/engine-harness.cjs +34 -12
- package/dist/engine-harness.js +4 -4
- package/dist/index.cjs +34 -12
- package/dist/index.js +5 -5
- package/dist/openclaw.js +4 -4
- package/dist/{proof-run-engine-Rkd_hXB-.d.cts → proof-run-engine-B7DCPzpK.d.cts} +3 -3
- package/dist/{proof-run-engine-DxWW1VX1.d.ts → proof-run-engine-BomAcXhA.d.ts} +3 -3
- package/dist/proof-run-engine.cjs +29 -9
- package/dist/proof-run-engine.d.cts +1 -1
- package/dist/proof-run-engine.d.ts +1 -1
- package/dist/proof-run-engine.js +1 -1
- package/dist/run-card.js +2 -2
- package/dist/runner.js +4 -4
- package/dist/spec/checkpoint.cjs +3 -1
- package/dist/spec/checkpoint.js +1 -1
- package/dist/spec/index.cjs +3 -1
- package/dist/spec/index.js +3 -3
- package/dist/spec/run-card.js +2 -2
- package/dist/spec/state.js +3 -3
- package/dist/state.js +3 -3
- package/package.json +1 -1
- package/runtime/lib/util.py +57 -0
- package/runtime/lib/verify.py +158 -14
- package/runtime/tests/recon_verify_smoke.py +211 -0
|
@@ -1925,9 +1925,9 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
|
|
|
1925
1925
|
state = readState(config.statePath);
|
|
1926
1926
|
const continuedStage = params.continue_from_checkpoint ? checkpointContinueStage(state) : null;
|
|
1927
1927
|
if (params.continue_from_checkpoint && !params.advance_stage && !continuedStage) {
|
|
1928
|
-
const
|
|
1928
|
+
const recommended2 = recommendedAdvanceStage(state);
|
|
1929
1929
|
return checkpoint(
|
|
1930
|
-
state?.active_checkpoint_stage ||
|
|
1930
|
+
state?.active_checkpoint_stage || recommended2 || "recon",
|
|
1931
1931
|
"continue_unavailable",
|
|
1932
1932
|
"This run call asked to continue from a checkpoint, but the current state has no resumable checkpoint. Inspect status or set advance_stage explicitly.",
|
|
1933
1933
|
{
|
|
@@ -1939,9 +1939,9 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
|
|
|
1939
1939
|
details: {
|
|
1940
1940
|
executed,
|
|
1941
1941
|
activeCheckpoint: state?.active_checkpoint || null,
|
|
1942
|
-
suggestedAdvanceStage:
|
|
1942
|
+
suggestedAdvanceStage: recommended2 || null
|
|
1943
1943
|
},
|
|
1944
|
-
suggestedAdvanceStage:
|
|
1944
|
+
suggestedAdvanceStage: recommended2 || null,
|
|
1945
1945
|
executed
|
|
1946
1946
|
}
|
|
1947
1947
|
);
|
|
@@ -2205,7 +2205,7 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
|
|
|
2205
2205
|
}
|
|
2206
2206
|
const noImplementationMode = !implementationRequired(params, state);
|
|
2207
2207
|
const authorNextStage = stageAfterAuthor(state, params);
|
|
2208
|
-
const explicitAuthorDebug = params.advance_stage === "author";
|
|
2208
|
+
const explicitAuthorDebug = params.advance_stage === "author" && !params.continue_from_checkpoint;
|
|
2209
2209
|
recordAttempt("author", "completed", "Author applied the supervising agent's proof packet to recon observations.", {
|
|
2210
2210
|
autoApproved: authorRes.autoApproved || false,
|
|
2211
2211
|
checkpoint: explicitAuthorDebug ? "author_review" : "author_auto_continue",
|
|
@@ -2254,16 +2254,16 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
|
|
|
2254
2254
|
state = readState(config.statePath);
|
|
2255
2255
|
}
|
|
2256
2256
|
if (!effectiveAdvanceStage) {
|
|
2257
|
-
const
|
|
2257
|
+
const recommended2 = recommendedAdvanceStage(state);
|
|
2258
2258
|
const noImplementationMode = !implementationRequired(params, state);
|
|
2259
2259
|
return checkpoint(
|
|
2260
|
-
|
|
2260
|
+
recommended2 || (noImplementationMode ? "verify" : "implement"),
|
|
2261
2261
|
"awaiting_stage_advance",
|
|
2262
2262
|
"Proof authoring is ready. The wrapper will not guess the next stage from here, explicitly choose whether to revisit recon/author, validate implementation, capture verify evidence, or ship.",
|
|
2263
2263
|
{
|
|
2264
2264
|
nextActions: ["inspect_state", "set_advance_stage", "resume_run"],
|
|
2265
2265
|
advanceOptions: noImplementationMode ? ["recon", "author", "verify", "ship"] : ["recon", "author", "implement", "verify", "ship"],
|
|
2266
|
-
recommendedAdvanceStage:
|
|
2266
|
+
recommendedAdvanceStage: recommended2,
|
|
2267
2267
|
details: { executed },
|
|
2268
2268
|
executed
|
|
2269
2269
|
}
|
|
@@ -2463,7 +2463,7 @@ ${implementRes.stderr || ""}`;
|
|
|
2463
2463
|
state = readState(config.statePath);
|
|
2464
2464
|
}
|
|
2465
2465
|
const checkpointName = "verify_capture_retry";
|
|
2466
|
-
const summary = "Verify ran, but the proof packet still needs internal capture-plan work before it should ship.";
|
|
2466
|
+
const summary = stringValue(proofAssessment.summary) || "Verify ran, but the proof packet still needs internal capture-plan work before it should ship.";
|
|
2467
2467
|
recordAttempt("verify", "checkpoint", summary, {
|
|
2468
2468
|
autoApproved: verifyRes.autoApproved || false,
|
|
2469
2469
|
checkpoint: checkpointName,
|
|
@@ -2819,6 +2819,26 @@ ${implementRes.stderr || ""}`;
|
|
|
2819
2819
|
}
|
|
2820
2820
|
);
|
|
2821
2821
|
}
|
|
2822
|
+
state = readState(config.statePath);
|
|
2823
|
+
const recommended = recommendedAdvanceStage(state);
|
|
2824
|
+
const fallbackStage = recommended || (implementationRequired(params, state) ? "implement" : "verify");
|
|
2825
|
+
return checkpoint(
|
|
2826
|
+
fallbackStage,
|
|
2827
|
+
"awaiting_stage_advance",
|
|
2828
|
+
"The requested run continuation finished its immediate work but did not resolve to verify or ship. Choose the next workflow stage explicitly.",
|
|
2829
|
+
{
|
|
2830
|
+
nextActions: ["inspect_state", "set_advance_stage", "resume_run"],
|
|
2831
|
+
advanceOptions: implementationRequired(params, state) ? ["recon", "author", "implement", "verify", "ship"] : ["recon", "author", "verify"],
|
|
2832
|
+
recommendedAdvanceStage: fallbackStage,
|
|
2833
|
+
continueWithStage: fallbackStage,
|
|
2834
|
+
blocking: false,
|
|
2835
|
+
details: {
|
|
2836
|
+
executed,
|
|
2837
|
+
requestedAdvanceStage: effectiveAdvanceStage
|
|
2838
|
+
},
|
|
2839
|
+
executed
|
|
2840
|
+
}
|
|
2841
|
+
);
|
|
2822
2842
|
}
|
|
2823
2843
|
if (action === "ship") {
|
|
2824
2844
|
const state = readState(config.statePath);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import './proof-run-core-CE0jx7wL.cjs';
|
|
2
|
-
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-
|
|
2
|
+
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-B7DCPzpK.cjs';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import './proof-run-core-CE0jx7wL.js';
|
|
2
|
-
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-
|
|
2
|
+
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from './proof-run-engine-BomAcXhA.js';
|
package/dist/proof-run-engine.js
CHANGED
package/dist/run-card.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RIDDLE_PROOF_RUN_CARD_VERSION,
|
|
3
3
|
createRiddleProofRunCard
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-FMOYUYH2.js";
|
|
5
|
+
import "./chunk-4FOHZ7JG.js";
|
|
6
6
|
import "./chunk-VY4Y5U57.js";
|
|
7
7
|
import "./chunk-MLKGABMK.js";
|
|
8
8
|
export {
|
package/dist/runner.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runRiddleProof
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-UIJ7X63P.js";
|
|
4
|
+
import "./chunk-YZUVEJ5B.js";
|
|
5
|
+
import "./chunk-FMOYUYH2.js";
|
|
6
6
|
import "./chunk-RV6LK7HU.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-4FOHZ7JG.js";
|
|
8
8
|
import "./chunk-VY4Y5U57.js";
|
|
9
9
|
import "./chunk-MLKGABMK.js";
|
|
10
10
|
export {
|
package/dist/spec/checkpoint.cjs
CHANGED
|
@@ -121,7 +121,7 @@ function responseSchemaForAuthorPacket() {
|
|
|
121
121
|
summary: { type: "string" },
|
|
122
122
|
payload: {
|
|
123
123
|
type: "object",
|
|
124
|
-
description: "For decision=author_packet, provide the proof packet itself or {author_packet:{...}} with proof_plan and
|
|
124
|
+
description: "For decision=author_packet, provide the proof packet itself or {author_packet:{...}} with proof_plan, capture_script, and refined_inputs.expected_terminal_path when the proof changes route, query, or hash."
|
|
125
125
|
},
|
|
126
126
|
reasons: { type: "array", items: { type: "string" } },
|
|
127
127
|
continue_with_stage: { type: "string", enum: ["author", "recon"] },
|
|
@@ -428,9 +428,11 @@ function buildAuthorCheckpointPacket(input) {
|
|
|
428
428
|
repo: input.request.repo || fullState.repo,
|
|
429
429
|
branch: input.request.branch || fullState.branch,
|
|
430
430
|
verification_mode: input.request.verification_mode || fullState.verification_mode,
|
|
431
|
+
success_criteria: fullState.success_criteria,
|
|
431
432
|
reference: input.request.reference || fullState.reference,
|
|
432
433
|
server_path: fullState.server_path,
|
|
433
434
|
wait_for_selector: fullState.wait_for_selector,
|
|
435
|
+
route_expectation: jsonCloneRecord(fullState.route_expectation),
|
|
434
436
|
author_summary: fullState.author_summary,
|
|
435
437
|
author_request: jsonCloneRecord(authorRequest),
|
|
436
438
|
recon_baseline_understanding: jsonCloneRecord(fullState.recon_baseline_understanding),
|
package/dist/spec/checkpoint.js
CHANGED
package/dist/spec/index.cjs
CHANGED
|
@@ -344,7 +344,7 @@ function responseSchemaForAuthorPacket() {
|
|
|
344
344
|
summary: { type: "string" },
|
|
345
345
|
payload: {
|
|
346
346
|
type: "object",
|
|
347
|
-
description: "For decision=author_packet, provide the proof packet itself or {author_packet:{...}} with proof_plan and
|
|
347
|
+
description: "For decision=author_packet, provide the proof packet itself or {author_packet:{...}} with proof_plan, capture_script, and refined_inputs.expected_terminal_path when the proof changes route, query, or hash."
|
|
348
348
|
},
|
|
349
349
|
reasons: { type: "array", items: { type: "string" } },
|
|
350
350
|
continue_with_stage: { type: "string", enum: ["author", "recon"] },
|
|
@@ -651,9 +651,11 @@ function buildAuthorCheckpointPacket(input) {
|
|
|
651
651
|
repo: input.request.repo || fullState.repo,
|
|
652
652
|
branch: input.request.branch || fullState.branch,
|
|
653
653
|
verification_mode: input.request.verification_mode || fullState.verification_mode,
|
|
654
|
+
success_criteria: fullState.success_criteria,
|
|
654
655
|
reference: input.request.reference || fullState.reference,
|
|
655
656
|
server_path: fullState.server_path,
|
|
656
657
|
wait_for_selector: fullState.wait_for_selector,
|
|
658
|
+
route_expectation: jsonCloneRecord(fullState.route_expectation),
|
|
657
659
|
author_summary: fullState.author_summary,
|
|
658
660
|
author_request: jsonCloneRecord(authorRequest),
|
|
659
661
|
recon_baseline_understanding: jsonCloneRecord(fullState.recon_baseline_understanding),
|
package/dist/spec/index.js
CHANGED
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
normalizePrLifecycleState,
|
|
11
11
|
normalizeRunParams,
|
|
12
12
|
setRunStatus
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-YZUVEJ5B.js";
|
|
14
14
|
import {
|
|
15
15
|
RIDDLE_PROOF_RUN_CARD_VERSION,
|
|
16
16
|
createRiddleProofRunCard
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-FMOYUYH2.js";
|
|
18
18
|
import {
|
|
19
19
|
RIDDLE_PROOF_CHECKPOINT_PACKET_VERSION,
|
|
20
20
|
RIDDLE_PROOF_CHECKPOINT_RESPONSE_VERSION,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
normalizeCheckpointResponse,
|
|
31
31
|
proofContractFromAuthorCheckpointResponse,
|
|
32
32
|
statePathsForRunState
|
|
33
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-4FOHZ7JG.js";
|
|
34
34
|
import {
|
|
35
35
|
applyTerminalMetadata,
|
|
36
36
|
compactRecord,
|
package/dist/spec/run-card.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RIDDLE_PROOF_RUN_CARD_VERSION,
|
|
3
3
|
createRiddleProofRunCard
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-FMOYUYH2.js";
|
|
5
|
+
import "../chunk-4FOHZ7JG.js";
|
|
6
6
|
import "../chunk-VY4Y5U57.js";
|
|
7
7
|
import "../chunk-MLKGABMK.js";
|
|
8
8
|
export {
|
package/dist/spec/state.js
CHANGED
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
normalizePrLifecycleState,
|
|
10
10
|
normalizeRunParams,
|
|
11
11
|
setRunStatus
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
12
|
+
} from "../chunk-YZUVEJ5B.js";
|
|
13
|
+
import "../chunk-FMOYUYH2.js";
|
|
14
|
+
import "../chunk-4FOHZ7JG.js";
|
|
15
15
|
import "../chunk-VY4Y5U57.js";
|
|
16
16
|
import "../chunk-MLKGABMK.js";
|
|
17
17
|
export {
|
package/dist/state.js
CHANGED
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
normalizePrLifecycleState,
|
|
10
10
|
normalizeRunParams,
|
|
11
11
|
setRunStatus
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-YZUVEJ5B.js";
|
|
13
|
+
import "./chunk-FMOYUYH2.js";
|
|
14
|
+
import "./chunk-4FOHZ7JG.js";
|
|
15
15
|
import "./chunk-VY4Y5U57.js";
|
|
16
16
|
import "./chunk-MLKGABMK.js";
|
|
17
17
|
export {
|
package/package.json
CHANGED
package/runtime/lib/util.py
CHANGED
|
@@ -710,6 +710,60 @@ def invoke(tool, args, timeout=180):
|
|
|
710
710
|
return {'ok': False, 'error': r.stdout[:300], 'stderr': r.stderr[:300]}
|
|
711
711
|
|
|
712
712
|
|
|
713
|
+
def compact_error_text(value, depth=0):
|
|
714
|
+
if depth > 4 or value is None:
|
|
715
|
+
return ''
|
|
716
|
+
if isinstance(value, str):
|
|
717
|
+
return value
|
|
718
|
+
if isinstance(value, (int, float, bool)):
|
|
719
|
+
return str(value)
|
|
720
|
+
if isinstance(value, list):
|
|
721
|
+
return '\n'.join(compact_error_text(item, depth + 1) for item in value[:12])
|
|
722
|
+
if isinstance(value, dict):
|
|
723
|
+
parts = []
|
|
724
|
+
priority_keys = (
|
|
725
|
+
'error', 'message', 'script_error', 'stderr', 'stdout', 'raw',
|
|
726
|
+
'details', 'result', 'capture', 'proof', '_proof_json',
|
|
727
|
+
)
|
|
728
|
+
for key in priority_keys:
|
|
729
|
+
if key in value:
|
|
730
|
+
text = compact_error_text(value.get(key), depth + 1)
|
|
731
|
+
if text:
|
|
732
|
+
parts.append(text)
|
|
733
|
+
if not parts:
|
|
734
|
+
for item in list(value.values())[:12]:
|
|
735
|
+
text = compact_error_text(item, depth + 1)
|
|
736
|
+
if text:
|
|
737
|
+
parts.append(text)
|
|
738
|
+
return '\n'.join(parts)
|
|
739
|
+
return ''
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def non_retryable_riddle_script_error(result):
|
|
743
|
+
text = compact_error_text(result).lower()
|
|
744
|
+
if not text:
|
|
745
|
+
return False
|
|
746
|
+
playwright_timeout = (
|
|
747
|
+
'timeout' in text
|
|
748
|
+
and (
|
|
749
|
+
'locator.' in text
|
|
750
|
+
or 'page.waitforurl' in text
|
|
751
|
+
or 'page.waitforselector' in text
|
|
752
|
+
or 'waiting for selector' in text
|
|
753
|
+
or 'waiting for navigation' in text
|
|
754
|
+
or 'getbyrole' in text
|
|
755
|
+
or 'getbytext' in text
|
|
756
|
+
or 'scrollintoviewifneeded' in text
|
|
757
|
+
)
|
|
758
|
+
)
|
|
759
|
+
deterministic_playwright_error = (
|
|
760
|
+
'strict mode violation' in text
|
|
761
|
+
or 'selector_not_found' in text
|
|
762
|
+
or 'index_out_of_range' in text
|
|
763
|
+
)
|
|
764
|
+
return playwright_timeout or deterministic_playwright_error
|
|
765
|
+
|
|
766
|
+
|
|
713
767
|
def invoke_retry(tool, args, retries=3, timeout=180):
|
|
714
768
|
"""Call an OpenClaw tool with automatic retries on failure."""
|
|
715
769
|
last_result = None
|
|
@@ -720,6 +774,9 @@ def invoke_retry(tool, args, retries=3, timeout=180):
|
|
|
720
774
|
if result.get('ok') or result.get('outputs') or result.get('screenshots'):
|
|
721
775
|
return result
|
|
722
776
|
print(f'invoke_retry({tool}) attempt {attempt}/{retries} failed: {str(result.get("error", "no output"))[:200]}')
|
|
777
|
+
if tool == 'riddle_script' and non_retryable_riddle_script_error(result):
|
|
778
|
+
print('invoke_retry(riddle_script) stopping early for deterministic Playwright script error')
|
|
779
|
+
return result
|
|
723
780
|
if attempt < retries:
|
|
724
781
|
import time
|
|
725
782
|
time.sleep(5)
|
package/runtime/lib/verify.py
CHANGED
|
@@ -1845,16 +1845,17 @@ def normalize_observed_path(value):
|
|
|
1845
1845
|
raw = str(value or '').strip()
|
|
1846
1846
|
if not raw:
|
|
1847
1847
|
return ''
|
|
1848
|
-
parsed = urlparse(raw
|
|
1848
|
+
parsed = urlparse(raw)
|
|
1849
1849
|
path = parsed.path or ''
|
|
1850
1850
|
query = parsed.query or ''
|
|
1851
|
+
fragment = parsed.fragment or ''
|
|
1851
1852
|
if not path.startswith('/'):
|
|
1852
1853
|
path = '/' + path.lstrip('/')
|
|
1853
1854
|
parts = path.split('/')
|
|
1854
1855
|
if len(parts) >= 4 and parts[1] == 's':
|
|
1855
1856
|
path = '/' + '/'.join(parts[3:])
|
|
1856
1857
|
path = path.rstrip('/') or '/'
|
|
1857
|
-
return path + (('?' + query) if query else '')
|
|
1858
|
+
return path + (('?' + query) if query else '') + (('#' + fragment) if fragment else '')
|
|
1858
1859
|
|
|
1859
1860
|
|
|
1860
1861
|
def observed_location_from_page_state(page_state):
|
|
@@ -1862,10 +1863,13 @@ def observed_location_from_page_state(page_state):
|
|
|
1862
1863
|
return ''
|
|
1863
1864
|
pathname = str(page_state.get('pathname') or '').strip()
|
|
1864
1865
|
search = str(page_state.get('search') or '').strip()
|
|
1866
|
+
hash_value = str(page_state.get('hash') or page_state.get('fragment') or '').strip()
|
|
1865
1867
|
if search and not search.startswith('?'):
|
|
1866
1868
|
search = '?' + search
|
|
1869
|
+
if hash_value and not hash_value.startswith('#'):
|
|
1870
|
+
hash_value = '#' + hash_value
|
|
1867
1871
|
if pathname:
|
|
1868
|
-
return pathname + search
|
|
1872
|
+
return pathname + search + hash_value
|
|
1869
1873
|
return str(page_state.get('href') or '').strip()
|
|
1870
1874
|
|
|
1871
1875
|
|
|
@@ -1889,9 +1893,29 @@ def route_matches_expected(expected_path, observed_path):
|
|
|
1889
1893
|
if pair not in remaining:
|
|
1890
1894
|
return False
|
|
1891
1895
|
remaining.remove(pair)
|
|
1896
|
+
if expected_parsed.fragment and observed_parsed.fragment != expected_parsed.fragment:
|
|
1897
|
+
return False
|
|
1892
1898
|
return True
|
|
1893
1899
|
|
|
1894
1900
|
|
|
1901
|
+
def route_parts(value):
|
|
1902
|
+
normalized = normalize_observed_path(value)
|
|
1903
|
+
if not normalized:
|
|
1904
|
+
return {
|
|
1905
|
+
'href': '',
|
|
1906
|
+
'pathname': '',
|
|
1907
|
+
'query': '',
|
|
1908
|
+
'hash': '',
|
|
1909
|
+
}
|
|
1910
|
+
parsed = urlparse(normalized)
|
|
1911
|
+
return {
|
|
1912
|
+
'href': normalized,
|
|
1913
|
+
'pathname': parsed.path.rstrip('/') or '/',
|
|
1914
|
+
'query': parsed.query or '',
|
|
1915
|
+
'hash': ('#' + parsed.fragment) if parsed.fragment else '',
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
|
|
1895
1919
|
EXPLICIT_TERMINAL_PATH_KEYS = (
|
|
1896
1920
|
'expected_terminal_path', 'expectedTerminalPath',
|
|
1897
1921
|
'expected_terminal_route', 'expectedTerminalRoute',
|
|
@@ -1980,6 +2004,16 @@ def terminal_path_from_record(record, depth=0):
|
|
|
1980
2004
|
return ''
|
|
1981
2005
|
|
|
1982
2006
|
|
|
2007
|
+
def terminal_path_from_text(value):
|
|
2008
|
+
if not isinstance(value, str):
|
|
2009
|
+
return ''
|
|
2010
|
+
for match in re.findall(r"""['"`](/[^'"`\s]+[?#][^'"`\s]*)['"`]""", value):
|
|
2011
|
+
candidate = path_candidate(match)
|
|
2012
|
+
if candidate:
|
|
2013
|
+
return candidate
|
|
2014
|
+
return ''
|
|
2015
|
+
|
|
2016
|
+
|
|
1983
2017
|
def interaction_assertions_pass(value):
|
|
1984
2018
|
for record in proof_evidence_records(value):
|
|
1985
2019
|
if any(record.get(key) is False for key in (
|
|
@@ -2039,29 +2073,58 @@ def interaction_terminal_path_from_state(state):
|
|
|
2039
2073
|
candidate = terminal_path_from_record(state.get(key))
|
|
2040
2074
|
if candidate:
|
|
2041
2075
|
return candidate, key
|
|
2076
|
+
for key in (
|
|
2077
|
+
'expected_terminal_path',
|
|
2078
|
+
'expected_after_path',
|
|
2079
|
+
'capture_script',
|
|
2080
|
+
'proof_plan',
|
|
2081
|
+
'success_criteria',
|
|
2082
|
+
'change_request',
|
|
2083
|
+
):
|
|
2084
|
+
candidate = path_candidate(state.get(key)) or terminal_path_from_text(state.get(key))
|
|
2085
|
+
if candidate:
|
|
2086
|
+
return candidate, key
|
|
2042
2087
|
return '', ''
|
|
2043
2088
|
|
|
2044
2089
|
|
|
2045
2090
|
def expected_path_for_verify(state, start_path, proof_evidence):
|
|
2046
2091
|
mode = normalized_verification_mode(state.get('verification_mode'))
|
|
2047
2092
|
normalized_start = normalize_observed_path(start_path) or '/'
|
|
2093
|
+
start_parts = route_parts(normalized_start)
|
|
2048
2094
|
if mode not in INTERACTION_MODES:
|
|
2049
2095
|
return normalized_start, {
|
|
2050
2096
|
'mode': mode,
|
|
2051
2097
|
'source': 'recon_start_path',
|
|
2052
2098
|
'start_path': normalized_start,
|
|
2053
2099
|
'expected_path': normalized_start,
|
|
2100
|
+
'start_pathname': start_parts['pathname'],
|
|
2101
|
+
'start_query': start_parts['query'],
|
|
2102
|
+
'start_hash': start_parts['hash'],
|
|
2103
|
+
'expected_pathname': start_parts['pathname'],
|
|
2104
|
+
'expected_query': start_parts['query'],
|
|
2105
|
+
'expected_hash': start_parts['hash'],
|
|
2054
2106
|
}
|
|
2055
2107
|
candidate, source = interaction_terminal_path_from_state(state)
|
|
2056
2108
|
if not candidate:
|
|
2057
2109
|
candidate, source = interaction_terminal_path_from_evidence(proof_evidence)
|
|
2058
2110
|
expected = candidate or normalized_start
|
|
2111
|
+
expected_parts = route_parts(expected)
|
|
2059
2112
|
return expected, {
|
|
2060
2113
|
'mode': mode,
|
|
2061
2114
|
'source': source or 'recon_start_path',
|
|
2062
2115
|
'start_path': normalized_start,
|
|
2063
2116
|
'expected_path': expected,
|
|
2064
2117
|
'terminal_path': expected if expected != normalized_start else '',
|
|
2118
|
+
'start_pathname': start_parts['pathname'],
|
|
2119
|
+
'start_query': start_parts['query'],
|
|
2120
|
+
'start_hash': start_parts['hash'],
|
|
2121
|
+
'expected_href': expected_parts['href'],
|
|
2122
|
+
'expected_pathname': expected_parts['pathname'],
|
|
2123
|
+
'expected_query': expected_parts['query'],
|
|
2124
|
+
'expected_hash': expected_parts['hash'],
|
|
2125
|
+
'terminal_pathname': expected_parts['pathname'] if expected != normalized_start else '',
|
|
2126
|
+
'terminal_query': expected_parts['query'] if expected != normalized_start else '',
|
|
2127
|
+
'terminal_hash': expected_parts['hash'] if expected != normalized_start else '',
|
|
2065
2128
|
}
|
|
2066
2129
|
|
|
2067
2130
|
|
|
@@ -2379,7 +2442,7 @@ def evaluate_capture_quality(payload, expected_path, verification_mode='proof'):
|
|
|
2379
2442
|
}
|
|
2380
2443
|
|
|
2381
2444
|
|
|
2382
|
-
def build_capture_retry_decision(after_observation, required_baseline_present, proof_evidence_blocker=''):
|
|
2445
|
+
def build_capture_retry_decision(after_observation, required_baseline_present, proof_evidence_blocker='', route_expectation=None):
|
|
2383
2446
|
reasons = []
|
|
2384
2447
|
if not required_baseline_present:
|
|
2385
2448
|
reasons.append('Recon baseline is missing, so verify should return to recon instead of guessing a new reference context.')
|
|
@@ -2391,6 +2454,25 @@ def build_capture_retry_decision(after_observation, required_baseline_present, p
|
|
|
2391
2454
|
'reasons': reasons,
|
|
2392
2455
|
}
|
|
2393
2456
|
|
|
2457
|
+
details = after_observation.get('details') if isinstance(after_observation.get('details'), dict) else {}
|
|
2458
|
+
route_expectation = route_expectation if isinstance(route_expectation, dict) else {}
|
|
2459
|
+
route_mismatch = None
|
|
2460
|
+
reason_text = str(after_observation.get('reason') or '')
|
|
2461
|
+
if 'wrong route' in reason_text:
|
|
2462
|
+
expected = details.get('expected_path') or ''
|
|
2463
|
+
observed = details.get('observed_path_raw') or details.get('observed_path') or ''
|
|
2464
|
+
if expected or observed:
|
|
2465
|
+
route_mismatch = {
|
|
2466
|
+
'field': 'route',
|
|
2467
|
+
'expected_path': expected,
|
|
2468
|
+
'observed_after_path': observed,
|
|
2469
|
+
}
|
|
2470
|
+
error_messages = [
|
|
2471
|
+
str(item).strip()
|
|
2472
|
+
for item in (details.get('capture_error_messages') or [])
|
|
2473
|
+
if str(item).strip()
|
|
2474
|
+
]
|
|
2475
|
+
|
|
2394
2476
|
if proof_evidence_blocker:
|
|
2395
2477
|
reasons.append(proof_evidence_blocker)
|
|
2396
2478
|
decision = 'missing_proof_evidence'
|
|
@@ -2399,24 +2481,46 @@ def build_capture_retry_decision(after_observation, required_baseline_present, p
|
|
|
2399
2481
|
reasons.append('The capture reached usable page context, but the proof evidence explicitly failed its own required audio gate.')
|
|
2400
2482
|
else:
|
|
2401
2483
|
reasons.append('The capture reached usable page context, but the proof script did not emit the structured evidence required for this verification mode.')
|
|
2484
|
+
if route_mismatch:
|
|
2485
|
+
reasons.append(
|
|
2486
|
+
'Route mismatch also present: expected after capture path ' +
|
|
2487
|
+
(route_mismatch.get('expected_path') or '(unknown)') +
|
|
2488
|
+
', observed ' +
|
|
2489
|
+
(route_mismatch.get('observed_after_path') or '(unknown)') +
|
|
2490
|
+
'.'
|
|
2491
|
+
)
|
|
2402
2492
|
reasons.append('Return to author so the capture script can expose passing proof evidence before verify asks for a supervising-agent judgment.')
|
|
2493
|
+
summary = proof_evidence_blocker
|
|
2494
|
+
if route_mismatch:
|
|
2495
|
+
summary += (
|
|
2496
|
+
' Route mismatch: expected ' +
|
|
2497
|
+
(route_mismatch.get('expected_path') or '(unknown)') +
|
|
2498
|
+
', got ' +
|
|
2499
|
+
(route_mismatch.get('observed_after_path') or '(unknown)') +
|
|
2500
|
+
'.'
|
|
2501
|
+
)
|
|
2502
|
+
if error_messages:
|
|
2503
|
+
reasons.append('Capture script error: ' + error_messages[0][:500])
|
|
2504
|
+
summary += ' Capture script error: ' + error_messages[0][:300]
|
|
2403
2505
|
return {
|
|
2404
2506
|
'decision': decision,
|
|
2405
|
-
'summary':
|
|
2507
|
+
'summary': summary,
|
|
2406
2508
|
'recommended_stage': 'author',
|
|
2407
2509
|
'continue_with_stage': 'author',
|
|
2408
2510
|
'reasons': reasons,
|
|
2511
|
+
'mismatch': route_mismatch,
|
|
2409
2512
|
}
|
|
2410
2513
|
|
|
2411
2514
|
reason = after_observation.get('reason') or 'after capture is not usable yet'
|
|
2412
2515
|
reasons.append('The after evidence is not usable yet: ' + reason)
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2516
|
+
route_expectation_source = str(route_expectation.get('source') or '')
|
|
2517
|
+
authored_terminal_route = bool(
|
|
2518
|
+
route_mismatch
|
|
2519
|
+
and route_expectation.get('mode') in INTERACTION_MODES
|
|
2520
|
+
and route_expectation.get('terminal_path')
|
|
2521
|
+
and route_expectation_source != 'recon_start_path'
|
|
2522
|
+
)
|
|
2523
|
+
recommended_stage = 'recon' if 'wrong route' in reason and not authored_terminal_route else 'author'
|
|
2420
2524
|
mismatch = None
|
|
2421
2525
|
if recommended_stage == 'recon':
|
|
2422
2526
|
expected = details.get('expected_path') or ''
|
|
@@ -2433,7 +2537,17 @@ def build_capture_retry_decision(after_observation, required_baseline_present, p
|
|
|
2433
2537
|
summary = 'Verify capture route mismatch needs recon to refresh the reference path.'
|
|
2434
2538
|
reasons.append('The capture appears to be on the wrong route or baseline context, so recon should refresh the reference path.')
|
|
2435
2539
|
else:
|
|
2436
|
-
if
|
|
2540
|
+
if route_mismatch:
|
|
2541
|
+
expected = route_mismatch.get('expected_path') or ''
|
|
2542
|
+
observed = route_mismatch.get('observed_after_path') or ''
|
|
2543
|
+
mismatch = route_mismatch
|
|
2544
|
+
reasons.append('Route mismatch: expected after capture path ' + (expected or '(unknown)') + ', observed ' + (observed or '(unknown)') + '.')
|
|
2545
|
+
reasons.append('The terminal route came from the authored interaction proof packet, so the capture plan should be revised instead of refreshing recon.')
|
|
2546
|
+
summary = 'Verify capture route mismatch after authored interaction: expected ' + (expected or '(unknown)') + ', got ' + (observed or '(unknown)') + '.'
|
|
2547
|
+
if error_messages:
|
|
2548
|
+
reasons.append('Capture script error: ' + error_messages[0][:500])
|
|
2549
|
+
summary += ' Capture script error: ' + error_messages[0][:300]
|
|
2550
|
+
elif error_messages:
|
|
2437
2551
|
reasons.append('Capture script error: ' + error_messages[0][:500])
|
|
2438
2552
|
summary = 'Verify capture script failed: ' + error_messages[0][:300]
|
|
2439
2553
|
else:
|
|
@@ -2528,6 +2642,9 @@ def build_semantic_context(state, results, after_observation, expected_path):
|
|
|
2528
2642
|
after_semantic = semantic_observation('after', after_observation)
|
|
2529
2643
|
expected_start_path = state.get('expected_start_path') or expected_path
|
|
2530
2644
|
route_expectation = state.get('route_expectation') if isinstance(state.get('route_expectation'), dict) else {}
|
|
2645
|
+
expected_parts = route_parts(expected_path)
|
|
2646
|
+
start_parts = route_parts(expected_start_path)
|
|
2647
|
+
after_parts = route_parts(after_semantic.get('observed_path_raw') or after_semantic.get('observed_path') or '')
|
|
2531
2648
|
return {
|
|
2532
2649
|
'expected_path': expected_path,
|
|
2533
2650
|
'expected_start_path': expected_start_path,
|
|
@@ -2540,10 +2657,20 @@ def build_semantic_context(state, results, after_observation, expected_path):
|
|
|
2540
2657
|
'expected_after_path': expected_path,
|
|
2541
2658
|
'expected_start_path': expected_start_path,
|
|
2542
2659
|
'expected_terminal_path': route_expectation.get('terminal_path') or '',
|
|
2660
|
+
'expected_pathname': expected_parts['pathname'],
|
|
2661
|
+
'expected_query': expected_parts['query'],
|
|
2662
|
+
'expected_hash': expected_parts['hash'],
|
|
2663
|
+
'expected_start_pathname': start_parts['pathname'],
|
|
2664
|
+
'expected_start_query': start_parts['query'],
|
|
2665
|
+
'expected_start_hash': start_parts['hash'],
|
|
2666
|
+
'expected_terminal_query': route_expectation.get('terminal_query') or route_expectation.get('expected_query') or '',
|
|
2667
|
+
'expected_terminal_hash': route_expectation.get('terminal_hash') or route_expectation.get('expected_hash') or '',
|
|
2543
2668
|
'expectation_source': route_expectation.get('source') or '',
|
|
2544
2669
|
'before_observed_path': before_semantic.get('observed_path') or before.get('path') or '',
|
|
2545
2670
|
'prod_observed_path': prod_semantic.get('observed_path') or prod.get('path') or '',
|
|
2546
2671
|
'after_observed_path': after_semantic.get('observed_path') or '',
|
|
2672
|
+
'after_observed_query': after_parts['query'],
|
|
2673
|
+
'after_observed_hash': after_parts['hash'],
|
|
2547
2674
|
},
|
|
2548
2675
|
'before': before_semantic,
|
|
2549
2676
|
'prod': prod_semantic,
|
|
@@ -3051,6 +3178,15 @@ if expected_start_path and expected_start_path != expected_path:
|
|
|
3051
3178
|
summary_lines.append('Expected terminal proof path: ' + expected_path)
|
|
3052
3179
|
else:
|
|
3053
3180
|
summary_lines.append('Expected proof path from recon: ' + expected_path)
|
|
3181
|
+
route_expected_query = (s.get('route_expectation') or {}).get('expected_query') or ''
|
|
3182
|
+
route_expected_hash = (s.get('route_expectation') or {}).get('expected_hash') or ''
|
|
3183
|
+
if route_expected_query or route_expected_hash:
|
|
3184
|
+
summary_lines.append(
|
|
3185
|
+
'Expected terminal query/hash: ' +
|
|
3186
|
+
('?' + route_expected_query if route_expected_query else '(none)') +
|
|
3187
|
+
' ' +
|
|
3188
|
+
(route_expected_hash if route_expected_hash else '(none)')
|
|
3189
|
+
)
|
|
3054
3190
|
summary_lines.append('After observation: ' + after_observation['reason'])
|
|
3055
3191
|
supporting = results['after'].get('supporting_artifacts') or {}
|
|
3056
3192
|
if supporting.get('has_structured_payload'):
|
|
@@ -3064,6 +3200,14 @@ if supporting.get('has_structured_payload'):
|
|
|
3064
3200
|
summary_lines.append('Structured after evidence: ' + ('; '.join(basis) if basis else 'present'))
|
|
3065
3201
|
observed_path = (after_observation.get('details') or {}).get('observed_path') or expected_path
|
|
3066
3202
|
summary_lines.append('Observed after path: ' + observed_path)
|
|
3203
|
+
observed_parts = route_parts((after_observation.get('details') or {}).get('observed_path_raw') or observed_path)
|
|
3204
|
+
if observed_parts.get('query') or observed_parts.get('hash') or route_expected_query or route_expected_hash:
|
|
3205
|
+
summary_lines.append(
|
|
3206
|
+
'Observed after query/hash: ' +
|
|
3207
|
+
('?' + observed_parts.get('query') if observed_parts.get('query') else '(none)') +
|
|
3208
|
+
' ' +
|
|
3209
|
+
(observed_parts.get('hash') if observed_parts.get('hash') else '(none)')
|
|
3210
|
+
)
|
|
3067
3211
|
details = after_observation.get('details') or {}
|
|
3068
3212
|
if details.get('headings'):
|
|
3069
3213
|
summary_lines.append('Visible headings: ' + '; '.join(str(item) for item in details.get('headings', [])[:6]))
|
|
@@ -3175,7 +3319,7 @@ if has_good_evidence:
|
|
|
3175
3319
|
summary_lines.append('Proof assessment: awaiting supervising agent judgment')
|
|
3176
3320
|
summary_lines.append('Proof next stage: supervising agent decides after reviewing the evidence packet')
|
|
3177
3321
|
else:
|
|
3178
|
-
capture_retry = visual_delta_recovery or build_capture_retry_decision(after_observation, required_baseline_present, proof_evidence_blocker)
|
|
3322
|
+
capture_retry = visual_delta_recovery or build_capture_retry_decision(after_observation, required_baseline_present, proof_evidence_blocker, s.get('route_expectation') or {})
|
|
3179
3323
|
next_stage_options = ['author', 'verify', 'recon'] if no_implementation_mode else ['author', 'verify', 'implement', 'recon']
|
|
3180
3324
|
s['verify_status'] = 'capture_incomplete'
|
|
3181
3325
|
s['merge_recommendation'] = 'do-not-merge'
|