@riddledc/riddle-proof 0.7.80 → 0.7.82
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/README.md +14 -0
- package/dist/checkpoint.js +2 -2
- package/dist/{chunk-EXF7V6TJ.js → chunk-7F5LNUGR.js} +1 -1
- package/dist/{chunk-33XO42CY.js → chunk-ABQQLRTS.js} +1 -1
- package/dist/{chunk-JRFKVTKY.js → chunk-ENWYM4L7.js} +2 -2
- package/dist/{chunk-F7BNWDF6.js → chunk-MPPLZ35C.js} +4 -4
- package/dist/{chunk-4DM3OTWH.js → chunk-UYB7ABWU.js} +3 -2
- package/dist/{chunk-DUFDZJOF.js → chunk-VY4Y5U57.js} +1 -0
- package/dist/{chunk-3UHWI3FO.js → chunk-ZHEFYLII.js} +14 -2
- package/dist/cli.cjs +14 -0
- package/dist/cli.js +6 -6
- package/dist/codex-exec-agent.js +2 -2
- package/dist/engine-harness.cjs +14 -0
- package/dist/engine-harness.js +5 -5
- package/dist/index.cjs +14 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/local-agent.js +2 -2
- package/dist/openclaw.js +4 -4
- package/dist/result.cjs +1 -0
- package/dist/result.js +1 -1
- package/dist/run-card.cjs +12 -0
- package/dist/run-card.js +3 -3
- package/dist/runner.cjs +1 -0
- package/dist/runner.js +5 -5
- package/dist/state.cjs +13 -0
- package/dist/state.js +4 -4
- package/dist/types.d.cts +21 -1
- package/dist/types.d.ts +21 -1
- package/package.json +1 -1
- package/runtime/lib/preflight.py +7 -0
- package/runtime/lib/recon.py +40 -6
- package/runtime/lib/util.py +151 -3
- package/runtime/lib/verify.py +400 -32
- package/runtime/tests/recon_verify_smoke.py +128 -0
package/dist/types.d.cts
CHANGED
|
@@ -9,6 +9,20 @@ type RiddleProofVerificationMode = "proof" | "visual" | "interaction" | "playabl
|
|
|
9
9
|
type RiddleProofDecision = "ready_to_ship" | "needs_richer_proof" | "revise_capture" | "needs_recon" | "needs_implementation" | (string & {});
|
|
10
10
|
type RiddleProofStage = "preflight" | "setup" | "recon" | "author" | "implement" | "prove" | "verify" | "ship" | "notify" | (string & {});
|
|
11
11
|
type RiddleProofArtifactRole = "baseline" | "after_proof" | "incidental" | "diagnostic" | (string & {});
|
|
12
|
+
interface RiddleProofViewportCapture {
|
|
13
|
+
name?: string;
|
|
14
|
+
slug?: string;
|
|
15
|
+
width?: number;
|
|
16
|
+
height?: number;
|
|
17
|
+
screenshot_label?: string;
|
|
18
|
+
screenshot_url?: string;
|
|
19
|
+
}
|
|
20
|
+
interface RiddleProofViewportMatrixStatus {
|
|
21
|
+
status?: "not_requested" | "not_run" | "complete" | "incomplete" | (string & {});
|
|
22
|
+
requested?: RiddleProofViewportCapture[];
|
|
23
|
+
executed?: RiddleProofViewportCapture[];
|
|
24
|
+
missing?: RiddleProofViewportCapture[];
|
|
25
|
+
}
|
|
12
26
|
interface RiddleProofRunParams {
|
|
13
27
|
repo?: string;
|
|
14
28
|
branch?: string;
|
|
@@ -201,6 +215,7 @@ interface RiddleProofRunCard {
|
|
|
201
215
|
visual_delta?: Record<string, unknown> | null;
|
|
202
216
|
evidence_issue_code?: string | null;
|
|
203
217
|
proof_evidence_present?: boolean;
|
|
218
|
+
viewport_matrix?: RiddleProofViewportMatrixStatus;
|
|
204
219
|
artifacts?: RiddleProofCheckpointArtifact[];
|
|
205
220
|
};
|
|
206
221
|
observability?: {
|
|
@@ -285,6 +300,7 @@ interface RiddleProofRunState {
|
|
|
285
300
|
implementation_detection_summary?: string | null;
|
|
286
301
|
implementation_detection?: Record<string, unknown> | null;
|
|
287
302
|
proof_session?: RiddleProofVisualSession;
|
|
303
|
+
viewport_matrix_status?: RiddleProofViewportMatrixStatus;
|
|
288
304
|
finalized?: boolean;
|
|
289
305
|
blocker?: RiddleProofBlocker;
|
|
290
306
|
checkpoint_packet?: RiddleProofCheckpointPacket;
|
|
@@ -338,6 +354,7 @@ interface RiddleProofRunResult {
|
|
|
338
354
|
proof_contract?: RiddleProofProofContract;
|
|
339
355
|
run_card?: RiddleProofRunCard;
|
|
340
356
|
proof_session?: RiddleProofVisualSession;
|
|
357
|
+
viewport_matrix_status?: RiddleProofViewportMatrixStatus;
|
|
341
358
|
evidence_bundle?: RiddleProofEvidenceBundle;
|
|
342
359
|
raw?: Record<string, unknown>;
|
|
343
360
|
}
|
|
@@ -373,6 +390,7 @@ interface RiddleProofRunStatusSnapshot {
|
|
|
373
390
|
state_paths?: RiddleProofStatePaths;
|
|
374
391
|
proof_contract?: RiddleProofProofContract;
|
|
375
392
|
run_card?: RiddleProofRunCard;
|
|
393
|
+
viewport_matrix_status?: RiddleProofViewportMatrixStatus;
|
|
376
394
|
latest_event?: RiddleProofEvent;
|
|
377
395
|
}
|
|
378
396
|
interface RiddleProofEvidenceBundle {
|
|
@@ -381,6 +399,7 @@ interface RiddleProofEvidenceBundle {
|
|
|
381
399
|
after?: EvidenceReference;
|
|
382
400
|
artifacts?: EvidenceArtifact[];
|
|
383
401
|
proof_session?: RiddleProofVisualSession;
|
|
402
|
+
viewport_matrix?: RiddleProofViewportMatrixStatus;
|
|
384
403
|
proof_evidence?: unknown;
|
|
385
404
|
playability_evidence?: unknown;
|
|
386
405
|
proof_evidence_sample?: unknown;
|
|
@@ -572,6 +591,7 @@ interface RiddleProofVisualSession {
|
|
|
572
591
|
};
|
|
573
592
|
evidence?: {
|
|
574
593
|
visual_delta?: JsonValue;
|
|
594
|
+
viewport_matrix?: RiddleProofViewportMatrixStatus;
|
|
575
595
|
semantic_context?: JsonValue;
|
|
576
596
|
artifact_contract?: JsonValue;
|
|
577
597
|
artifact_usage?: JsonValue;
|
|
@@ -593,4 +613,4 @@ interface RiddleProofVisualSessionFingerprintBasis {
|
|
|
593
613
|
capture_script_hash?: string;
|
|
594
614
|
}
|
|
595
615
|
|
|
596
|
-
export type { EvidenceArtifact, EvidenceReference, ImplementationAdapter, ImplementationAdapterInput, ImplementationAdapterResult, IntegrationContext, JsonObject, JsonPrimitive, JsonValue, JudgeAdapter, NotificationAdapter, PreflightAdapter, PreflightAdapterInput, PreflightAdapterResult, ProofAdapter, ProofAdapterInput, ProofAdapterResult, RiddleProofArtifactRole, RiddleProofAssessment, RiddleProofBlocker, RiddleProofCheckpointArtifact, RiddleProofCheckpointPacket, RiddleProofCheckpointResponse, RiddleProofCheckpointRole, RiddleProofCheckpointRoutingHint, RiddleProofCheckpointSummary, RiddleProofCheckpointVisibility, RiddleProofDecision, RiddleProofEvent, RiddleProofEvidenceBundle, RiddleProofPrLifecycleState, RiddleProofPrLifecycleStatus, RiddleProofProofContract, RiddleProofRunCard, RiddleProofRunParams, RiddleProofRunResult, RiddleProofRunState, RiddleProofRunStatusSnapshot, RiddleProofStage, RiddleProofStatePaths, RiddleProofStatus, RiddleProofTerminalMetadata, RiddleProofVerificationMode, RiddleProofVisualSession, RiddleProofVisualSessionFingerprintBasis, SetupAdapter, SetupAdapterInput, SetupAdapterResult, ShipAdapter };
|
|
616
|
+
export type { EvidenceArtifact, EvidenceReference, ImplementationAdapter, ImplementationAdapterInput, ImplementationAdapterResult, IntegrationContext, JsonObject, JsonPrimitive, JsonValue, JudgeAdapter, NotificationAdapter, PreflightAdapter, PreflightAdapterInput, PreflightAdapterResult, ProofAdapter, ProofAdapterInput, ProofAdapterResult, RiddleProofArtifactRole, RiddleProofAssessment, RiddleProofBlocker, RiddleProofCheckpointArtifact, RiddleProofCheckpointPacket, RiddleProofCheckpointResponse, RiddleProofCheckpointRole, RiddleProofCheckpointRoutingHint, RiddleProofCheckpointSummary, RiddleProofCheckpointVisibility, RiddleProofDecision, RiddleProofEvent, RiddleProofEvidenceBundle, RiddleProofPrLifecycleState, RiddleProofPrLifecycleStatus, RiddleProofProofContract, RiddleProofRunCard, RiddleProofRunParams, RiddleProofRunResult, RiddleProofRunState, RiddleProofRunStatusSnapshot, RiddleProofStage, RiddleProofStatePaths, RiddleProofStatus, RiddleProofTerminalMetadata, RiddleProofVerificationMode, RiddleProofViewportCapture, RiddleProofViewportMatrixStatus, RiddleProofVisualSession, RiddleProofVisualSessionFingerprintBasis, SetupAdapter, SetupAdapterInput, SetupAdapterResult, ShipAdapter };
|
package/dist/types.d.ts
CHANGED
|
@@ -9,6 +9,20 @@ type RiddleProofVerificationMode = "proof" | "visual" | "interaction" | "playabl
|
|
|
9
9
|
type RiddleProofDecision = "ready_to_ship" | "needs_richer_proof" | "revise_capture" | "needs_recon" | "needs_implementation" | (string & {});
|
|
10
10
|
type RiddleProofStage = "preflight" | "setup" | "recon" | "author" | "implement" | "prove" | "verify" | "ship" | "notify" | (string & {});
|
|
11
11
|
type RiddleProofArtifactRole = "baseline" | "after_proof" | "incidental" | "diagnostic" | (string & {});
|
|
12
|
+
interface RiddleProofViewportCapture {
|
|
13
|
+
name?: string;
|
|
14
|
+
slug?: string;
|
|
15
|
+
width?: number;
|
|
16
|
+
height?: number;
|
|
17
|
+
screenshot_label?: string;
|
|
18
|
+
screenshot_url?: string;
|
|
19
|
+
}
|
|
20
|
+
interface RiddleProofViewportMatrixStatus {
|
|
21
|
+
status?: "not_requested" | "not_run" | "complete" | "incomplete" | (string & {});
|
|
22
|
+
requested?: RiddleProofViewportCapture[];
|
|
23
|
+
executed?: RiddleProofViewportCapture[];
|
|
24
|
+
missing?: RiddleProofViewportCapture[];
|
|
25
|
+
}
|
|
12
26
|
interface RiddleProofRunParams {
|
|
13
27
|
repo?: string;
|
|
14
28
|
branch?: string;
|
|
@@ -201,6 +215,7 @@ interface RiddleProofRunCard {
|
|
|
201
215
|
visual_delta?: Record<string, unknown> | null;
|
|
202
216
|
evidence_issue_code?: string | null;
|
|
203
217
|
proof_evidence_present?: boolean;
|
|
218
|
+
viewport_matrix?: RiddleProofViewportMatrixStatus;
|
|
204
219
|
artifacts?: RiddleProofCheckpointArtifact[];
|
|
205
220
|
};
|
|
206
221
|
observability?: {
|
|
@@ -285,6 +300,7 @@ interface RiddleProofRunState {
|
|
|
285
300
|
implementation_detection_summary?: string | null;
|
|
286
301
|
implementation_detection?: Record<string, unknown> | null;
|
|
287
302
|
proof_session?: RiddleProofVisualSession;
|
|
303
|
+
viewport_matrix_status?: RiddleProofViewportMatrixStatus;
|
|
288
304
|
finalized?: boolean;
|
|
289
305
|
blocker?: RiddleProofBlocker;
|
|
290
306
|
checkpoint_packet?: RiddleProofCheckpointPacket;
|
|
@@ -338,6 +354,7 @@ interface RiddleProofRunResult {
|
|
|
338
354
|
proof_contract?: RiddleProofProofContract;
|
|
339
355
|
run_card?: RiddleProofRunCard;
|
|
340
356
|
proof_session?: RiddleProofVisualSession;
|
|
357
|
+
viewport_matrix_status?: RiddleProofViewportMatrixStatus;
|
|
341
358
|
evidence_bundle?: RiddleProofEvidenceBundle;
|
|
342
359
|
raw?: Record<string, unknown>;
|
|
343
360
|
}
|
|
@@ -373,6 +390,7 @@ interface RiddleProofRunStatusSnapshot {
|
|
|
373
390
|
state_paths?: RiddleProofStatePaths;
|
|
374
391
|
proof_contract?: RiddleProofProofContract;
|
|
375
392
|
run_card?: RiddleProofRunCard;
|
|
393
|
+
viewport_matrix_status?: RiddleProofViewportMatrixStatus;
|
|
376
394
|
latest_event?: RiddleProofEvent;
|
|
377
395
|
}
|
|
378
396
|
interface RiddleProofEvidenceBundle {
|
|
@@ -381,6 +399,7 @@ interface RiddleProofEvidenceBundle {
|
|
|
381
399
|
after?: EvidenceReference;
|
|
382
400
|
artifacts?: EvidenceArtifact[];
|
|
383
401
|
proof_session?: RiddleProofVisualSession;
|
|
402
|
+
viewport_matrix?: RiddleProofViewportMatrixStatus;
|
|
384
403
|
proof_evidence?: unknown;
|
|
385
404
|
playability_evidence?: unknown;
|
|
386
405
|
proof_evidence_sample?: unknown;
|
|
@@ -572,6 +591,7 @@ interface RiddleProofVisualSession {
|
|
|
572
591
|
};
|
|
573
592
|
evidence?: {
|
|
574
593
|
visual_delta?: JsonValue;
|
|
594
|
+
viewport_matrix?: RiddleProofViewportMatrixStatus;
|
|
575
595
|
semantic_context?: JsonValue;
|
|
576
596
|
artifact_contract?: JsonValue;
|
|
577
597
|
artifact_usage?: JsonValue;
|
|
@@ -593,4 +613,4 @@ interface RiddleProofVisualSessionFingerprintBasis {
|
|
|
593
613
|
capture_script_hash?: string;
|
|
594
614
|
}
|
|
595
615
|
|
|
596
|
-
export type { EvidenceArtifact, EvidenceReference, ImplementationAdapter, ImplementationAdapterInput, ImplementationAdapterResult, IntegrationContext, JsonObject, JsonPrimitive, JsonValue, JudgeAdapter, NotificationAdapter, PreflightAdapter, PreflightAdapterInput, PreflightAdapterResult, ProofAdapter, ProofAdapterInput, ProofAdapterResult, RiddleProofArtifactRole, RiddleProofAssessment, RiddleProofBlocker, RiddleProofCheckpointArtifact, RiddleProofCheckpointPacket, RiddleProofCheckpointResponse, RiddleProofCheckpointRole, RiddleProofCheckpointRoutingHint, RiddleProofCheckpointSummary, RiddleProofCheckpointVisibility, RiddleProofDecision, RiddleProofEvent, RiddleProofEvidenceBundle, RiddleProofPrLifecycleState, RiddleProofPrLifecycleStatus, RiddleProofProofContract, RiddleProofRunCard, RiddleProofRunParams, RiddleProofRunResult, RiddleProofRunState, RiddleProofRunStatusSnapshot, RiddleProofStage, RiddleProofStatePaths, RiddleProofStatus, RiddleProofTerminalMetadata, RiddleProofVerificationMode, RiddleProofVisualSession, RiddleProofVisualSessionFingerprintBasis, SetupAdapter, SetupAdapterInput, SetupAdapterResult, ShipAdapter };
|
|
616
|
+
export type { EvidenceArtifact, EvidenceReference, ImplementationAdapter, ImplementationAdapterInput, ImplementationAdapterResult, IntegrationContext, JsonObject, JsonPrimitive, JsonValue, JudgeAdapter, NotificationAdapter, PreflightAdapter, PreflightAdapterInput, PreflightAdapterResult, ProofAdapter, ProofAdapterInput, ProofAdapterResult, RiddleProofArtifactRole, RiddleProofAssessment, RiddleProofBlocker, RiddleProofCheckpointArtifact, RiddleProofCheckpointPacket, RiddleProofCheckpointResponse, RiddleProofCheckpointRole, RiddleProofCheckpointRoutingHint, RiddleProofCheckpointSummary, RiddleProofCheckpointVisibility, RiddleProofDecision, RiddleProofEvent, RiddleProofEvidenceBundle, RiddleProofPrLifecycleState, RiddleProofPrLifecycleStatus, RiddleProofProofContract, RiddleProofRunCard, RiddleProofRunParams, RiddleProofRunResult, RiddleProofRunState, RiddleProofRunStatusSnapshot, RiddleProofStage, RiddleProofStatePaths, RiddleProofStatus, RiddleProofTerminalMetadata, RiddleProofVerificationMode, RiddleProofViewportCapture, RiddleProofViewportMatrixStatus, RiddleProofVisualSession, RiddleProofVisualSessionFingerprintBasis, SetupAdapter, SetupAdapterInput, SetupAdapterResult, ShipAdapter };
|
package/package.json
CHANGED
package/runtime/lib/preflight.py
CHANGED
|
@@ -15,6 +15,7 @@ from util import (
|
|
|
15
15
|
invoke,
|
|
16
16
|
load_package_json,
|
|
17
17
|
load_proof_session_source,
|
|
18
|
+
normalize_viewport_matrix,
|
|
18
19
|
save_state,
|
|
19
20
|
validate_proof_session_resume,
|
|
20
21
|
)
|
|
@@ -130,6 +131,12 @@ if raw_assertions:
|
|
|
130
131
|
raise SystemExit('assertions_json is not valid JSON: ' + str(e))
|
|
131
132
|
s['parsed_assertions'] = parsed_assertions
|
|
132
133
|
s['viewport_matrix'] = parse_json_arg('viewport_matrix_json', (dict, list), None)
|
|
134
|
+
s['viewport_matrix_status'] = {
|
|
135
|
+
'status': 'not_run' if normalize_viewport_matrix(s.get('viewport_matrix')) else 'not_requested',
|
|
136
|
+
'requested': normalize_viewport_matrix(s.get('viewport_matrix')),
|
|
137
|
+
'executed': [],
|
|
138
|
+
'missing': [],
|
|
139
|
+
}
|
|
133
140
|
s['deterministic_setup'] = parse_json_arg('deterministic_setup_json', (dict, list), None)
|
|
134
141
|
|
|
135
142
|
# Generate branch if not provided. The riddle-proof/* namespace is reserved for
|
package/runtime/lib/recon.py
CHANGED
|
@@ -19,6 +19,7 @@ from util import ( # noqa: E402
|
|
|
19
19
|
apply_auth_context,
|
|
20
20
|
build_capture_script,
|
|
21
21
|
capture_static_preview,
|
|
22
|
+
capture_viewport_matrix_status,
|
|
22
23
|
enrich_capture_payload,
|
|
23
24
|
has_auth_context,
|
|
24
25
|
invoke_retry,
|
|
@@ -27,6 +28,9 @@ from util import ( # noqa: E402
|
|
|
27
28
|
save_state,
|
|
28
29
|
should_use_static_preview,
|
|
29
30
|
summarize_capture_artifacts,
|
|
31
|
+
viewport_matrix_return_js,
|
|
32
|
+
viewport_matrix_screenshot_js,
|
|
33
|
+
viewport_matrix_setup_js,
|
|
30
34
|
)
|
|
31
35
|
import subprocess as sp
|
|
32
36
|
|
|
@@ -514,9 +518,10 @@ def route_matches_expected(expected_path, observed_path):
|
|
|
514
518
|
return True
|
|
515
519
|
|
|
516
520
|
|
|
517
|
-
def build_probe_capture_script(base_script='', screenshot_label=''):
|
|
521
|
+
def build_probe_capture_script(base_script='', screenshot_label='', viewport_matrix=None):
|
|
518
522
|
pieces = []
|
|
519
523
|
script = (base_script or '').strip()
|
|
524
|
+
pieces.extend(viewport_matrix_setup_js(viewport_matrix))
|
|
520
525
|
if script:
|
|
521
526
|
pieces.append(script.rstrip(';') + ';')
|
|
522
527
|
pieces.extend([
|
|
@@ -556,9 +561,12 @@ def build_probe_capture_script(base_script='', screenshot_label=''):
|
|
|
556
561
|
'console.log(' + json.dumps(PAGE_STATE_PREFIX) + ' + JSON.stringify(pageState));',
|
|
557
562
|
])
|
|
558
563
|
label = (screenshot_label or '').strip()
|
|
559
|
-
|
|
564
|
+
viewport_screenshot_lines = viewport_matrix_screenshot_js(label, viewport_matrix)
|
|
565
|
+
if label and viewport_screenshot_lines:
|
|
566
|
+
pieces.extend(viewport_screenshot_lines)
|
|
567
|
+
elif label and 'saveScreenshot' not in script:
|
|
560
568
|
pieces.append('await saveScreenshot(' + json.dumps(label) + ');')
|
|
561
|
-
pieces.append('return { pageState };')
|
|
569
|
+
pieces.append('return { pageState, viewportMatrix: ' + viewport_matrix_return_js() + ' };')
|
|
562
570
|
return ' '.join(pieces)
|
|
563
571
|
|
|
564
572
|
|
|
@@ -727,7 +735,7 @@ def capture_workspace_baseline(project_dir, label, plan, capture_script=''):
|
|
|
727
735
|
'exclude': server_exclude,
|
|
728
736
|
'path': target_path,
|
|
729
737
|
'readiness_path': '/' if has_auth_context(s) else target_path,
|
|
730
|
-
'script': build_probe_capture_script(capture_script, label),
|
|
738
|
+
'script': build_probe_capture_script(capture_script, label, s.get('viewport_matrix')),
|
|
731
739
|
}
|
|
732
740
|
if s.get('color_scheme'):
|
|
733
741
|
server_args['color_scheme'] = s['color_scheme']
|
|
@@ -757,7 +765,7 @@ def capture_workspace_baseline(project_dir, label, plan, capture_script=''):
|
|
|
757
765
|
print('Recon capture (' + label + ') using static preview fallback: ' + static_reason)
|
|
758
766
|
capture_phase = label + '_capture'
|
|
759
767
|
record_recon_phase(capture_phase, 'running', 'Capturing ' + label + ' recon baseline evidence.')
|
|
760
|
-
capture = capture_static_preview(state_for_capture, project_dir, label, build_probe_capture_script(capture_script, label), timeout=300, target_path=target_path)
|
|
768
|
+
capture = capture_static_preview(state_for_capture, project_dir, label, build_probe_capture_script(capture_script, label, s.get('viewport_matrix')), timeout=300, target_path=target_path)
|
|
761
769
|
raw = (capture.get('raw') or {}).get('capture') or {}
|
|
762
770
|
append_capture_diagnostic(
|
|
763
771
|
s,
|
|
@@ -787,7 +795,7 @@ def capture_prod_baseline(prod_url, plan, capture_script=''):
|
|
|
787
795
|
if not target_url:
|
|
788
796
|
raise SystemExit('Requested prod baseline in recon, but prod_url is missing.')
|
|
789
797
|
wait_for_selector = (plan.get('wait_for_selector') or '').strip()
|
|
790
|
-
script = build_capture_script(target_url, build_probe_capture_script(capture_script, 'prod'), 'prod', wait_for_selector)
|
|
798
|
+
script = build_capture_script(target_url, build_probe_capture_script(capture_script, 'prod', s.get('viewport_matrix')), 'prod', wait_for_selector, s.get('viewport_matrix'))
|
|
791
799
|
args = {'script': script, 'timeout_sec': 60}
|
|
792
800
|
apply_auth_context(s, args)
|
|
793
801
|
print('Recon capture (prod) at ' + target_url)
|
|
@@ -815,6 +823,7 @@ def baseline_record(capture, observation):
|
|
|
815
823
|
'url': capture.get('url'),
|
|
816
824
|
'static_fallback_reason': capture.get('static_fallback_reason', ''),
|
|
817
825
|
'artifact_summary': summarize_capture_artifacts(capture.get('raw') or {}),
|
|
826
|
+
'viewport_matrix': capture_viewport_matrix_status(s, capture.get('raw') or {}, str(observation.get('label') or '')),
|
|
818
827
|
'observation': observation,
|
|
819
828
|
}
|
|
820
829
|
|
|
@@ -833,6 +842,24 @@ def build_observation_packet(label, expected_path, capture=None, error=''):
|
|
|
833
842
|
|
|
834
843
|
payload = (capture or {}).get('raw') or {}
|
|
835
844
|
quality = evaluate_capture_quality(payload, expected_path)
|
|
845
|
+
viewport_matrix = capture_viewport_matrix_status(s, payload, label)
|
|
846
|
+
if viewport_matrix.get('status') == 'incomplete':
|
|
847
|
+
missing_names = [
|
|
848
|
+
str(item.get('name') or item.get('slug') or '').strip()
|
|
849
|
+
for item in viewport_matrix.get('missing') or []
|
|
850
|
+
if str(item.get('name') or item.get('slug') or '').strip()
|
|
851
|
+
]
|
|
852
|
+
missing_text = ', '.join(missing_names[:8]) or 'requested viewport screenshots'
|
|
853
|
+
quality['valid'] = False
|
|
854
|
+
quality['telemetry_ready'] = False
|
|
855
|
+
quality['reason'] = (
|
|
856
|
+
quality.get('reason') + '; missing requested viewport evidence: ' + missing_text
|
|
857
|
+
if quality.get('reason') and quality.get('reason') != 'ok'
|
|
858
|
+
else 'missing requested viewport evidence: ' + missing_text
|
|
859
|
+
)
|
|
860
|
+
details = quality.get('details') if isinstance(quality.get('details'), dict) else {}
|
|
861
|
+
details['viewport_matrix'] = viewport_matrix
|
|
862
|
+
quality['details'] = details
|
|
836
863
|
return {
|
|
837
864
|
'label': label,
|
|
838
865
|
'ok': bool((capture or {}).get('url')) and quality['valid'],
|
|
@@ -1042,6 +1069,13 @@ attempt_record = {
|
|
|
1042
1069
|
'captured_baselines': attempt_captured_baselines,
|
|
1043
1070
|
'result': attempt_result,
|
|
1044
1071
|
}
|
|
1072
|
+
if attempt_captured_baselines:
|
|
1073
|
+
latest_viewport_status = None
|
|
1074
|
+
for baseline in attempt_captured_baselines.values():
|
|
1075
|
+
if isinstance(baseline, dict) and isinstance(baseline.get('viewport_matrix'), dict):
|
|
1076
|
+
latest_viewport_status = baseline.get('viewport_matrix')
|
|
1077
|
+
if latest_viewport_status:
|
|
1078
|
+
s['viewport_matrix_status'] = latest_viewport_status
|
|
1045
1079
|
attempt_history.append(attempt_record)
|
|
1046
1080
|
recon_results['attempt_history'] = attempt_history
|
|
1047
1081
|
recon_results['baselines'] = {}
|
package/runtime/lib/util.py
CHANGED
|
@@ -948,8 +948,153 @@ def join_url_path(base_url, target_path=''):
|
|
|
948
948
|
return urljoin(base.rstrip('/') + '/', path.lstrip('/'))
|
|
949
949
|
|
|
950
950
|
|
|
951
|
-
def
|
|
951
|
+
def slugify_viewport_name(value, fallback='viewport'):
|
|
952
|
+
text = re.sub(r'[^a-zA-Z0-9]+', '-', str(value or '').strip().lower()).strip('-')
|
|
953
|
+
return text or fallback
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
def normalize_viewport_matrix(value):
|
|
957
|
+
matrix = value
|
|
958
|
+
if isinstance(matrix, dict):
|
|
959
|
+
for key in ('viewports', 'viewport_matrix', 'matrix'):
|
|
960
|
+
if isinstance(matrix.get(key), list):
|
|
961
|
+
matrix = matrix.get(key)
|
|
962
|
+
break
|
|
963
|
+
else:
|
|
964
|
+
matrix = [matrix]
|
|
965
|
+
if not isinstance(matrix, list):
|
|
966
|
+
return []
|
|
967
|
+
|
|
968
|
+
normalized = []
|
|
969
|
+
for index, item in enumerate(matrix):
|
|
970
|
+
if not isinstance(item, dict):
|
|
971
|
+
continue
|
|
972
|
+
try:
|
|
973
|
+
width = int(item.get('width'))
|
|
974
|
+
height = int(item.get('height'))
|
|
975
|
+
except Exception:
|
|
976
|
+
continue
|
|
977
|
+
if width <= 0 or height <= 0:
|
|
978
|
+
continue
|
|
979
|
+
raw_name = str(item.get('name') or item.get('label') or '').strip()
|
|
980
|
+
name = raw_name or f'{width}x{height}'
|
|
981
|
+
slug = slugify_viewport_name(name, f'viewport-{index + 1}')
|
|
982
|
+
normalized.append({
|
|
983
|
+
'name': name,
|
|
984
|
+
'slug': slug,
|
|
985
|
+
'width': width,
|
|
986
|
+
'height': height,
|
|
987
|
+
})
|
|
988
|
+
return normalized
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
def viewport_screenshot_label(label, viewport):
|
|
992
|
+
return (str(label or 'screenshot').strip() or 'screenshot') + '-' + str(viewport.get('slug') or viewport.get('name') or 'viewport')
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
def viewport_matrix_setup_js(viewport_matrix):
|
|
996
|
+
viewports = normalize_viewport_matrix(viewport_matrix)
|
|
997
|
+
if not viewports:
|
|
998
|
+
return []
|
|
999
|
+
return [
|
|
1000
|
+
'const __riddleProofViewportMatrix = ' + json.dumps(viewports) + ';',
|
|
1001
|
+
'const __riddleProofViewportResults = [];',
|
|
1002
|
+
'const __riddleProofApplyViewport = async (vp) => {',
|
|
1003
|
+
' if (!vp || !vp.width || !vp.height) return;',
|
|
1004
|
+
' await page.setViewportSize({ width: Number(vp.width), height: Number(vp.height) });',
|
|
1005
|
+
'};',
|
|
1006
|
+
'await __riddleProofApplyViewport(__riddleProofViewportMatrix[0]);',
|
|
1007
|
+
]
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
def viewport_matrix_screenshot_js(label, viewport_matrix):
|
|
1011
|
+
viewports = normalize_viewport_matrix(viewport_matrix)
|
|
1012
|
+
if not viewports:
|
|
1013
|
+
return []
|
|
1014
|
+
base_label = str(label or 'screenshot').strip() or 'screenshot'
|
|
1015
|
+
return [
|
|
1016
|
+
'for (const __riddleProofViewport of __riddleProofViewportMatrix) {',
|
|
1017
|
+
' await __riddleProofApplyViewport(__riddleProofViewport);',
|
|
1018
|
+
' await page.waitForTimeout(250);',
|
|
1019
|
+
' const __riddleProofViewportLabel = ' + json.dumps(base_label) + ' + "-" + (__riddleProofViewport.slug || __riddleProofViewport.name || "viewport");',
|
|
1020
|
+
' await saveScreenshot(__riddleProofViewportLabel);',
|
|
1021
|
+
' __riddleProofViewportResults.push({',
|
|
1022
|
+
' name: __riddleProofViewport.name || "",',
|
|
1023
|
+
' slug: __riddleProofViewport.slug || "",',
|
|
1024
|
+
' width: Number(__riddleProofViewport.width) || null,',
|
|
1025
|
+
' height: Number(__riddleProofViewport.height) || null,',
|
|
1026
|
+
' screenshot_label: __riddleProofViewportLabel,',
|
|
1027
|
+
' });',
|
|
1028
|
+
'}',
|
|
1029
|
+
]
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
def viewport_matrix_return_js():
|
|
1033
|
+
return 'typeof __riddleProofViewportMatrix !== "undefined" ? { requested: __riddleProofViewportMatrix, executed: __riddleProofViewportResults } : null'
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
def capture_viewport_matrix_status(state, payload, label=''):
|
|
1037
|
+
requested = normalize_viewport_matrix((state or {}).get('viewport_matrix'))
|
|
1038
|
+
if not requested:
|
|
1039
|
+
return {
|
|
1040
|
+
'status': 'not_requested',
|
|
1041
|
+
'requested': [],
|
|
1042
|
+
'executed': [],
|
|
1043
|
+
'missing': [],
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
artifact_summary = summarize_capture_artifacts(payload)
|
|
1047
|
+
image_items = []
|
|
1048
|
+
for key in ('outputs', 'screenshots', 'artifacts'):
|
|
1049
|
+
for item in artifact_summary.get(key) or []:
|
|
1050
|
+
if isinstance(item, dict):
|
|
1051
|
+
image_items.append(item)
|
|
1052
|
+
|
|
1053
|
+
executed = []
|
|
1054
|
+
missing = []
|
|
1055
|
+
for viewport in requested:
|
|
1056
|
+
expected_label = viewport_screenshot_label(label, viewport)
|
|
1057
|
+
expected_names = {
|
|
1058
|
+
expected_label,
|
|
1059
|
+
expected_label + '.png',
|
|
1060
|
+
expected_label + '.jpg',
|
|
1061
|
+
expected_label + '.jpeg',
|
|
1062
|
+
expected_label + '.webp',
|
|
1063
|
+
expected_label + '.gif',
|
|
1064
|
+
}
|
|
1065
|
+
match = None
|
|
1066
|
+
for item in image_items:
|
|
1067
|
+
name = str(item.get('name') or '')
|
|
1068
|
+
url = str(item.get('url') or '')
|
|
1069
|
+
if name in expected_names and url:
|
|
1070
|
+
match = item
|
|
1071
|
+
break
|
|
1072
|
+
record = {
|
|
1073
|
+
'name': viewport.get('name'),
|
|
1074
|
+
'slug': viewport.get('slug'),
|
|
1075
|
+
'width': viewport.get('width'),
|
|
1076
|
+
'height': viewport.get('height'),
|
|
1077
|
+
'screenshot_label': expected_label,
|
|
1078
|
+
'screenshot_url': str((match or {}).get('url') or ''),
|
|
1079
|
+
}
|
|
1080
|
+
if match:
|
|
1081
|
+
executed.append(record)
|
|
1082
|
+
else:
|
|
1083
|
+
missing.append(record)
|
|
1084
|
+
|
|
1085
|
+
return {
|
|
1086
|
+
'status': 'complete' if not missing else 'incomplete',
|
|
1087
|
+
'requested': requested,
|
|
1088
|
+
'executed': executed,
|
|
1089
|
+
'missing': missing,
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
def build_capture_script(url, capture_script, label, wait_for_selector='', viewport_matrix=None):
|
|
1094
|
+
script_handles_viewport_matrix = '__riddleProofViewportMatrix' in (capture_script or '')
|
|
1095
|
+
effective_viewport_matrix = None if script_handles_viewport_matrix else viewport_matrix
|
|
952
1096
|
pieces = [
|
|
1097
|
+
*viewport_matrix_setup_js(effective_viewport_matrix),
|
|
953
1098
|
'await page.goto(' + json.dumps(url) + ');',
|
|
954
1099
|
]
|
|
955
1100
|
selector = (wait_for_selector or '').strip()
|
|
@@ -958,8 +1103,11 @@ def build_capture_script(url, capture_script, label, wait_for_selector=''):
|
|
|
958
1103
|
pieces.append('await page.waitForTimeout(1500);')
|
|
959
1104
|
if (capture_script or '').strip():
|
|
960
1105
|
pieces.append((capture_script or '').strip().rstrip(';') + ';')
|
|
961
|
-
if
|
|
1106
|
+
if normalize_viewport_matrix(effective_viewport_matrix):
|
|
1107
|
+
pieces.extend(viewport_matrix_screenshot_js(label, effective_viewport_matrix))
|
|
1108
|
+
elif not capture_script_saves_screenshot(capture_script):
|
|
962
1109
|
pieces.append('await saveScreenshot(' + json.dumps(label) + ');')
|
|
1110
|
+
pieces.append('return { viewportMatrix: ' + viewport_matrix_return_js() + ' };')
|
|
963
1111
|
return ' '.join(pieces)
|
|
964
1112
|
|
|
965
1113
|
|
|
@@ -987,7 +1135,7 @@ def capture_static_preview(state, project_dir, label, capture_script, timeout=30
|
|
|
987
1135
|
preview_id = preview.get('id', '')
|
|
988
1136
|
capture_url = join_url_path(preview_url, target_path or state.get('server_path', ''))
|
|
989
1137
|
|
|
990
|
-
script = build_capture_script(capture_url, capture_script, label, state.get('wait_for_selector', ''))
|
|
1138
|
+
script = build_capture_script(capture_url, capture_script, label, state.get('wait_for_selector', ''), state.get('viewport_matrix'))
|
|
991
1139
|
args = {'script': script, 'timeout_sec': 60}
|
|
992
1140
|
apply_auth_context(state, args)
|
|
993
1141
|
shot = invoke_retry('riddle_script', args, retries=3, timeout=max(timeout, 120))
|