@riddledc/riddle-proof-packs 0.2.0

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/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@riddledc/riddle-proof-packs",
3
+ "version": "0.2.0",
4
+ "description": "Reusable proof pack profiles and metadata helpers for the Riddle proof framework.",
5
+ "license": "MIT",
6
+ "author": "RiddleDC",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/riddledc/integrations",
10
+ "directory": "packages/riddle-proof-packs"
11
+ },
12
+ "type": "module",
13
+ "main": "./dist/index.cjs",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "packs",
26
+ "LICENSE",
27
+ "README.md"
28
+ ],
29
+ "sideEffects": false,
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "scripts": {
34
+ "build": "tsup src/index.ts --format cjs,esm --dts --out-dir dist --clean",
35
+ "clean": "rm -rf dist",
36
+ "lint": "echo 'lint: (not configured)'",
37
+ "test": "npm run build && node test.js"
38
+ },
39
+ "dependencies": {
40
+ "@riddledc/riddle-proof": "workspace:^"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^22.0.0",
44
+ "tsup": "^8.0.1",
45
+ "typescript": "^5.4.5"
46
+ }
47
+ }
@@ -0,0 +1,21 @@
1
+ # auth-smoke
2
+
3
+ Auth Smoke proof pack profile. Include this profile JSON directly in any profile-mode execution path.
4
+
5
+ ## Proof claims and evidence roles
6
+
7
+ - evidence_role: `current_target`
8
+ - atomic claim
9
+ - claim: The entry/home route is reachable and stable under a basic smoke configuration.
10
+ - target: `/` on the configured target URL.
11
+ - setup/actions: minimal route wait and baseline setup to capture DOM and viewport evidence.
12
+ - evidence: route/text assertions plus `body`, overflow, and no-fatal console checks.
13
+ - verdict: pass only if the smoke targets are visible and no high-risk runtime faults are observed.
14
+ - does not prove
15
+ - actual identity auth success or login edge cases.
16
+ - permissions, role-based UI behavior, or backend authorization guarantees.
17
+ - protected workflow outcomes across multiple auth providers.
18
+
19
+ ## Usage
20
+
21
+ Load the profile JSON from `profile.json` and supply it to profile mode or a local runner input file.
@@ -0,0 +1,65 @@
1
+ {
2
+ "version": "riddle-proof.profile.v1",
3
+ "name": "auth-smoke",
4
+ "target": {
5
+ "route": "/",
6
+ "viewports": [
7
+ {
8
+ "name": "mobile",
9
+ "width": 390,
10
+ "height": 844
11
+ },
12
+ {
13
+ "name": "desktop",
14
+ "width": 1440,
15
+ "height": 1000
16
+ }
17
+ ],
18
+ "auth": "none",
19
+ "wait_for_selector": "body",
20
+ "setup_actions": [
21
+ {
22
+ "type": "wait",
23
+ "ms": 250
24
+ }
25
+ ]
26
+ },
27
+ "checks": [
28
+ {
29
+ "type": "selector_visible",
30
+ "selector": "body"
31
+ },
32
+ {
33
+ "type": "text_visible",
34
+ "text": "Example"
35
+ },
36
+ {
37
+ "type": "no_mobile_horizontal_overflow"
38
+ },
39
+ {
40
+ "type": "no_fatal_console_errors"
41
+ }
42
+ ],
43
+ "artifacts": [
44
+ "screenshot",
45
+ "console",
46
+ "dom_summary",
47
+ "proof_json"
48
+ ],
49
+ "failure_policy": {
50
+ "environment_blocked": "neutral",
51
+ "proof_insufficient": "review",
52
+ "product_regression": "fail"
53
+ },
54
+ "metadata": {
55
+ "pack_id": "auth_smoke",
56
+ "pack_public_name": "Auth Smoke Pack",
57
+ "purpose": "Template for baseline page smoke checks where the entry route is publicly reachable.",
58
+ "required_receipts": [
59
+ "entry/home route is reachable",
60
+ "visible route baseline content",
61
+ "overflow and console noise are clean",
62
+ "no-console-error baseline"
63
+ ]
64
+ }
65
+ }
@@ -0,0 +1,21 @@
1
+ # canvas-gameplay
2
+
3
+ Canvas Gameplay proof pack profile. Include this profile JSON directly in any profile-mode execution path.
4
+
5
+ ## Proof claims and evidence roles
6
+
7
+ - evidence_role: `interaction_snapshots`
8
+ - atomic claim
9
+ - claim: gameplay route can be entered, controlled, and rendered through a canvas while remaining stable.
10
+ - target: `/games/example` gameplay route.
11
+ - setup/actions: clear storage, wait for game canvas, install game proof reader, inject a few input actions, and capture pre-action and post-action proof states.
12
+ - evidence: canvas signatures, in-run window values, screenshots, route/visibility checks, and clean console.
13
+ - verdict: pass when movement/interaction and runtime state progress are observed without errors.
14
+ - does not prove
15
+ - long-game correctness, scoring fairness, or anti-cheat outcomes.
16
+ - cross-device gameplay parity beyond the exercised viewport set.
17
+ - backend game logic correctness independent of UI rendering.
18
+
19
+ ## Usage
20
+
21
+ Load the profile JSON from `profile.json` and supply it to profile mode or a local runner input file.
@@ -0,0 +1,225 @@
1
+ {
2
+ "version": "riddle-proof.profile.v1",
3
+ "name": "canvas-gameplay",
4
+ "target": {
5
+ "route": "/",
6
+ "viewports": [
7
+ {
8
+ "name": "mobile",
9
+ "width": 390,
10
+ "height": 844
11
+ },
12
+ {
13
+ "name": "tablet",
14
+ "width": 820,
15
+ "height": 1180
16
+ },
17
+ {
18
+ "name": "desktop",
19
+ "width": 1440,
20
+ "height": 1000
21
+ }
22
+ ],
23
+ "timeout_sec": 300,
24
+ "wait_for_selector": "#game-root canvas",
25
+ "screenshot_mode": "viewport",
26
+ "setup_actions": [
27
+ {
28
+ "type": "clear_storage",
29
+ "storage": "both",
30
+ "reload": true
31
+ },
32
+ {
33
+ "type": "wait_for_selector",
34
+ "selector": "#game-root",
35
+ "timeout_ms": 20000
36
+ },
37
+ {
38
+ "type": "wait_for_selector",
39
+ "selector": "#game-root canvas",
40
+ "timeout_ms": 20000
41
+ },
42
+ {
43
+ "type": "window_eval",
44
+ "label": "install-gameplay-proof-reader",
45
+ "timeout_ms": 10000,
46
+ "store_return_to": "__gameplayProof.ready",
47
+ "script": "window.__riddleGameplayStep=()=>{const api=window.__exampleGameProof;const state=api?.step?.()||api?.read?.()||{};const out={ready:state.ready===true,inputAccepted:state.inputAccepted===true,gameOver:state.gameOver===true,distance:Number(state.distance||0),score:Number(state.score||0),level:Number(state.level||0)};out.ok=out.ready&&out.inputAccepted&&out.gameOver===false&&out.distance>=2&&out.score>=10;window.__gameplayProof={...(window.__gameplayProof||{}),running:out};return out;};const api=window.__exampleGameProof;const initial=api?.read?.()||{};const out={ready:initial.ready===true,inputAccepted:initial.inputAccepted===true,gameOver:initial.gameOver===true,distance:Number(initial.distance||0),score:Number(initial.score||0),level:Number(initial.level||0)};window.__gameplayProof={...(window.__gameplayProof||{}),ready:out};return out;",
48
+ "return_summary_fields": [
49
+ {
50
+ "path": "ready"
51
+ },
52
+ {
53
+ "path": "inputAccepted"
54
+ },
55
+ {
56
+ "path": "distance"
57
+ },
58
+ {
59
+ "path": "score"
60
+ },
61
+ {
62
+ "path": "gameOver"
63
+ }
64
+ ]
65
+ },
66
+ {
67
+ "type": "assert_window_value",
68
+ "path": "__gameplayProof.ready.ready",
69
+ "expected": true,
70
+ "timeout_ms": 10000
71
+ },
72
+ {
73
+ "type": "canvas_signature",
74
+ "selector": "#game-root canvas",
75
+ "label": "ready",
76
+ "store_signature_to": "__gameplayProof.readyCanvas",
77
+ "timeout_ms": 10000
78
+ },
79
+ {
80
+ "type": "screenshot",
81
+ "label": "ready",
82
+ "mode": "viewport"
83
+ },
84
+ {
85
+ "type": "press",
86
+ "key": "Space",
87
+ "after_ms": 120
88
+ },
89
+ {
90
+ "type": "press",
91
+ "key": "ArrowRight",
92
+ "after_ms": 120
93
+ },
94
+ {
95
+ "type": "window_call_until",
96
+ "path": "__riddleGameplayStep",
97
+ "until_path": "__gameplayProof.running.ok",
98
+ "until_expected_value": true,
99
+ "max_calls": 36,
100
+ "interval_ms": 250,
101
+ "timeout_ms": 15000
102
+ },
103
+ {
104
+ "type": "window_eval",
105
+ "label": "capture-running-proof",
106
+ "timeout_ms": 10000,
107
+ "store_return_to": "__gameplayProof.running",
108
+ "script": "return window.__riddleGameplayStep?.()||{};",
109
+ "return_summary_fields": [
110
+ {
111
+ "path": "ok"
112
+ },
113
+ {
114
+ "path": "distance"
115
+ },
116
+ {
117
+ "path": "score"
118
+ },
119
+ {
120
+ "path": "inputAccepted"
121
+ },
122
+ {
123
+ "path": "gameOver"
124
+ }
125
+ ]
126
+ },
127
+ {
128
+ "type": "assert_window_value",
129
+ "path": "__gameplayProof.running.ok",
130
+ "expected": true,
131
+ "timeout_ms": 10000
132
+ },
133
+ {
134
+ "type": "assert_window_value",
135
+ "path": "__gameplayProof.running.gameOver",
136
+ "expected": false,
137
+ "timeout_ms": 10000
138
+ },
139
+ {
140
+ "type": "assert_window_number",
141
+ "path": "__gameplayProof.running.distance",
142
+ "min_value": 2,
143
+ "timeout_ms": 10000
144
+ },
145
+ {
146
+ "type": "assert_window_number",
147
+ "path": "__gameplayProof.running.score",
148
+ "min_value": 10,
149
+ "timeout_ms": 10000
150
+ },
151
+ {
152
+ "type": "canvas_signature",
153
+ "selector": "#game-root canvas",
154
+ "label": "running",
155
+ "store_signature_to": "__gameplayProof.runningCanvas",
156
+ "timeout_ms": 10000
157
+ },
158
+ {
159
+ "type": "screenshot",
160
+ "label": "running",
161
+ "mode": "viewport"
162
+ }
163
+ ]
164
+ },
165
+ "checks": [
166
+ {
167
+ "type": "route_loaded",
168
+ "expected_path": "/games/example"
169
+ },
170
+ {
171
+ "type": "selector_visible",
172
+ "selector": "#game-root"
173
+ },
174
+ {
175
+ "type": "selector_visible",
176
+ "selector": "#game-root canvas"
177
+ },
178
+ {
179
+ "type": "selector_count_equals",
180
+ "selector": "#game-root canvas",
181
+ "expected_count": 1
182
+ },
183
+ {
184
+ "type": "text_absent",
185
+ "pattern": "\\bNaN\\b"
186
+ },
187
+ {
188
+ "type": "text_absent",
189
+ "pattern": "undefined",
190
+ "flags": "i"
191
+ },
192
+ {
193
+ "type": "no_horizontal_overflow",
194
+ "max_overflow_px": 1
195
+ },
196
+ {
197
+ "type": "no_fatal_console_errors"
198
+ },
199
+ {
200
+ "type": "no_console_warnings"
201
+ }
202
+ ],
203
+ "artifacts": [
204
+ "screenshot",
205
+ "console",
206
+ "dom_summary",
207
+ "proof_json"
208
+ ],
209
+ "baseline_policy": "invariant_only",
210
+ "failure_policy": {
211
+ "environment_blocked": "neutral",
212
+ "proof_insufficient": "fail",
213
+ "product_regression": "fail"
214
+ },
215
+ "metadata": {
216
+ "purpose": "Template for gameplay routes where gameplay evidence is primarily rendered through canvas.",
217
+ "pack_id": "canvas_gameplay",
218
+ "pack_public_name": "Canvas Gameplay Pack",
219
+ "required_receipts": [
220
+ "canvas mount and signature capture",
221
+ "route loaded on gameplay path",
222
+ "non-fatal browser evidence"
223
+ ]
224
+ }
225
+ }
@@ -0,0 +1,21 @@
1
+ # gameplay-window-call-until
2
+
3
+ Gameplay Window Call Until proof pack profile. Include this profile JSON directly in any profile-mode execution path.
4
+
5
+ ## Proof claims and evidence roles
6
+
7
+ - evidence_role: `interaction_snapshots`
8
+ - atomic claim
9
+ - claim: gameplay runtime responds to input and can be proven with pre-action and post-action checkpoints.
10
+ - target: `/games/example` gameplay route.
11
+ - setup/actions: initialize and wait for readiness, capture ready state, then perform input actions and poll for progressing game state.
12
+ - evidence: canvas signatures, route/load checks, state assertions (`window_call_until` progression), and clean console.
13
+ - verdict: pass when expected in-run game state transitions are observed before the timeout window.
14
+ - does not prove
15
+ - deterministic behavior across all user inputs.
16
+ - success conditions outside the targeted route.
17
+ - full game logic correctness or backend matchmaking integrity.
18
+
19
+ ## Usage
20
+
21
+ Load the profile JSON from `profile.json` and supply it to profile mode or a local runner input file.
@@ -0,0 +1,101 @@
1
+ {
2
+ "version": "riddle-proof.profile.v1",
3
+ "name": "gameplay-window-call-until",
4
+ "target": {
5
+ "route": "/games/example",
6
+ "viewports": [
7
+ { "name": "mobile", "width": 390, "height": 844 },
8
+ { "name": "tablet", "width": 820, "height": 1180 },
9
+ { "name": "desktop", "width": 1440, "height": 1000 }
10
+ ],
11
+ "timeout_sec": 300,
12
+ "wait_for_selector": "#game-root canvas",
13
+ "screenshot_mode": "viewport",
14
+ "setup_actions": [
15
+ { "type": "clear_storage", "storage": "both", "reload": true },
16
+ { "type": "wait_for_selector", "selector": "#game-root", "timeout_ms": 20000 },
17
+ { "type": "wait_for_selector", "selector": "#game-root canvas", "timeout_ms": 20000 },
18
+ {
19
+ "type": "window_eval",
20
+ "label": "install-gameplay-proof-reader",
21
+ "timeout_ms": 10000,
22
+ "store_return_to": "__gameplayProof.ready",
23
+ "script": "window.__riddleGameplayStep=()=>{const api=window.__exampleGameProof;const state=api?.step?.()||api?.read?.()||{};const out={ready:state.ready===true,inputAccepted:state.inputAccepted===true,gameOver:state.gameOver===true,distance:Number(state.distance||0),score:Number(state.score||0),level:Number(state.level||0)};out.ok=out.ready&&out.inputAccepted&&out.gameOver===false&&out.distance>=2&&out.score>=10;window.__gameplayProof={...(window.__gameplayProof||{}),running:out};return out;};const api=window.__exampleGameProof;const initial=api?.read?.()||{};const out={ready:initial.ready===true,inputAccepted:initial.inputAccepted===true,gameOver:initial.gameOver===true,distance:Number(initial.distance||0),score:Number(initial.score||0),level:Number(initial.level||0)};window.__gameplayProof={...(window.__gameplayProof||{}),ready:out};return out;",
24
+ "return_summary_fields": [
25
+ { "path": "ready" },
26
+ { "path": "inputAccepted" },
27
+ { "path": "distance" },
28
+ { "path": "score" },
29
+ { "path": "gameOver" }
30
+ ]
31
+ },
32
+ { "type": "assert_window_value", "path": "__gameplayProof.ready.ready", "expected": true, "timeout_ms": 10000 },
33
+ {
34
+ "type": "canvas_signature",
35
+ "selector": "#game-root canvas",
36
+ "label": "ready",
37
+ "store_signature_to": "__gameplayProof.readyCanvas",
38
+ "timeout_ms": 10000
39
+ },
40
+ { "type": "screenshot", "label": "ready", "mode": "viewport" },
41
+ { "type": "press", "key": "Space", "after_ms": 120 },
42
+ { "type": "press", "key": "ArrowRight", "after_ms": 120 },
43
+ {
44
+ "type": "window_call_until",
45
+ "path": "__riddleGameplayStep",
46
+ "until_path": "__gameplayProof.running.ok",
47
+ "until_expected_value": true,
48
+ "max_calls": 36,
49
+ "interval_ms": 250,
50
+ "timeout_ms": 15000
51
+ },
52
+ {
53
+ "type": "window_eval",
54
+ "label": "capture-running-proof",
55
+ "timeout_ms": 10000,
56
+ "store_return_to": "__gameplayProof.running",
57
+ "script": "return window.__riddleGameplayStep?.()||{};",
58
+ "return_summary_fields": [
59
+ { "path": "ok" },
60
+ { "path": "distance" },
61
+ { "path": "score" },
62
+ { "path": "inputAccepted" },
63
+ { "path": "gameOver" }
64
+ ]
65
+ },
66
+ { "type": "assert_window_value", "path": "__gameplayProof.running.ok", "expected": true, "timeout_ms": 10000 },
67
+ { "type": "assert_window_value", "path": "__gameplayProof.running.gameOver", "expected": false, "timeout_ms": 10000 },
68
+ { "type": "assert_window_number", "path": "__gameplayProof.running.distance", "min_value": 2, "timeout_ms": 10000 },
69
+ { "type": "assert_window_number", "path": "__gameplayProof.running.score", "min_value": 10, "timeout_ms": 10000 },
70
+ {
71
+ "type": "canvas_signature",
72
+ "selector": "#game-root canvas",
73
+ "label": "running",
74
+ "store_signature_to": "__gameplayProof.runningCanvas",
75
+ "timeout_ms": 10000
76
+ },
77
+ { "type": "screenshot", "label": "running", "mode": "viewport" }
78
+ ]
79
+ },
80
+ "checks": [
81
+ { "type": "route_loaded", "expected_path": "/games/example" },
82
+ { "type": "selector_visible", "selector": "#game-root" },
83
+ { "type": "selector_visible", "selector": "#game-root canvas" },
84
+ { "type": "selector_count_equals", "selector": "#game-root canvas", "expected_count": 1 },
85
+ { "type": "text_absent", "pattern": "\\bNaN\\b" },
86
+ { "type": "text_absent", "pattern": "undefined", "flags": "i" },
87
+ { "type": "no_horizontal_overflow", "max_overflow_px": 1 },
88
+ { "type": "no_fatal_console_errors" },
89
+ { "type": "no_console_warnings" }
90
+ ],
91
+ "artifacts": ["screenshot", "console", "dom_summary", "proof_json"],
92
+ "baseline_policy": "invariant_only",
93
+ "failure_policy": {
94
+ "environment_blocked": "neutral",
95
+ "proof_insufficient": "fail",
96
+ "product_regression": "fail"
97
+ },
98
+ "metadata": {
99
+ "purpose": "Template for gameplay profiles where a route exposes window.__exampleGameProof.read() and optionally step(). Use window_call_until to wait for runtime state such as accepted input, distance, score, or level completion instead of relying on fixed sleeps that vary across hosted browser frame cadence."
100
+ }
101
+ }
@@ -0,0 +1,21 @@
1
+ # handled-recovery-action-malformed-success
2
+
3
+ Handled Recovery Action Malformed Success proof pack profile. Include this profile JSON directly in any profile-mode execution path.
4
+
5
+ ## Proof claims and evidence roles
6
+
7
+ - evidence_role: `interaction_snapshots`
8
+ - atomic claim
9
+ - claim: malformed action responses are handled without surfacing parsing/validation regressions in primary UI.
10
+ - target: `/account` account page with recovery-state controls.
11
+ - setup/actions: seed auth/state mocks, perform a malformed create action, capture pre-action and post-action snapshots.
12
+ - evidence: recovery message assertions, visible state retention checks, request-body capture, and clean console checks.
13
+ - verdict: pass when recovery UI appears, corrupted parser artifacts stay absent, and request intent is captured.
14
+ - does not prove
15
+ - successful action creation path.
16
+ - behavior for other actions/endpoints in the same workflow.
17
+ - role-specific authorization or data persistence effects.
18
+
19
+ ## Usage
20
+
21
+ Load the profile JSON from `profile.json` and supply it to profile mode or a local runner input file.
@@ -0,0 +1,126 @@
1
+ {
2
+ "version": "riddle-proof.profile.v1",
3
+ "name": "handled-recovery-action-malformed-success",
4
+ "target": {
5
+ "route": "/account",
6
+ "viewports": [
7
+ { "name": "mobile", "width": 390, "height": 844 },
8
+ { "name": "tablet", "width": 820, "height": 1180 },
9
+ { "name": "desktop", "width": 1440, "height": 1000 }
10
+ ],
11
+ "timeout_sec": 300,
12
+ "wait_ms": 800,
13
+ "network_mocks": [
14
+ {
15
+ "label": "account-summary",
16
+ "url": "**/api/account/summary",
17
+ "method": "GET",
18
+ "status": 200,
19
+ "content_type": "application/json",
20
+ "required_hit_count": 3,
21
+ "json": {
22
+ "available_time": "4h 0m",
23
+ "active_jobs": 2
24
+ }
25
+ },
26
+ {
27
+ "label": "recent-jobs",
28
+ "url": "**/api/jobs?limit=10",
29
+ "method": "GET",
30
+ "status": 200,
31
+ "content_type": "application/json",
32
+ "required_hit_count": 3,
33
+ "json": {
34
+ "jobs": [
35
+ {
36
+ "id": "job_action_template_survives",
37
+ "status": "completed"
38
+ }
39
+ ]
40
+ }
41
+ },
42
+ {
43
+ "label": "existing-api-keys",
44
+ "url": "**/api/api-keys",
45
+ "method": "GET",
46
+ "status": 200,
47
+ "content_type": "application/json",
48
+ "required_hit_count": 3,
49
+ "json": {
50
+ "keys": [
51
+ {
52
+ "id": "key_action_template_existing",
53
+ "name": "Existing action template key",
54
+ "last4": "0001",
55
+ "status": "active"
56
+ }
57
+ ]
58
+ }
59
+ },
60
+ {
61
+ "label": "create-api-key-malformed-success",
62
+ "url": "**/api/api-keys",
63
+ "method": "POST",
64
+ "status": 200,
65
+ "content_type": "application/json",
66
+ "required_hit_count": 3,
67
+ "max_hit_count": 3,
68
+ "capture_request_body": true,
69
+ "request_body_contains": ["Action template malformed success key"],
70
+ "body": "{not valid create success json"
71
+ }
72
+ ],
73
+ "setup_actions": [
74
+ { "type": "clear_storage", "storage": "both", "reload": true },
75
+ { "type": "wait_for_selector", "selector": "[data-testid='account-page']", "timeout_ms": 30000 },
76
+ { "type": "wait_for_text", "selector": "body", "text": "4h 0m", "timeout_ms": 30000 },
77
+ { "type": "wait_for_text", "selector": "body", "text": "job_action_template_survives", "timeout_ms": 30000 },
78
+ { "type": "wait_for_text", "selector": "body", "text": "Existing action template key", "timeout_ms": 30000 },
79
+ { "type": "fill", "selector": "[data-testid='new-key-name']", "value": "Action template malformed success key" },
80
+ { "type": "clear_console" },
81
+ { "type": "screenshot", "label": "before-malformed-success-action" },
82
+ { "type": "click", "selector": "[data-testid='create-key-button']", "text": "Create API key" },
83
+ { "type": "wait_for_text", "selector": "body", "text": "Failed to create API key", "timeout_ms": 30000 },
84
+ { "type": "assert_text_visible", "selector": "body", "text": "Existing action template key", "timeout_ms": 5000 },
85
+ { "type": "assert_text_visible", "selector": "body", "text": "Active", "timeout_ms": 5000 },
86
+ { "type": "assert_text_absent", "selector": "body", "text": "API Key Created!", "timeout_ms": 1000 },
87
+ { "type": "assert_text_absent", "selector": "body", "text": "Expected property name", "timeout_ms": 1000 },
88
+ { "type": "assert_text_absent", "selector": "body", "text": "SyntaxError", "timeout_ms": 1000 },
89
+ { "type": "assert_text_absent", "selector": "body", "text": "[object Object]", "timeout_ms": 1000 },
90
+ { "type": "screenshot", "label": "malformed-success-action-visible-recovery" }
91
+ ]
92
+ },
93
+ "checks": [
94
+ { "type": "route_loaded", "expected_path": "/account" },
95
+ { "type": "selector_visible", "selector": "[data-testid='account-page']" },
96
+ { "type": "selector_visible", "selector": "[data-testid='api-key-create-error']" },
97
+ { "type": "text_visible", "text": "4h 0m" },
98
+ { "type": "text_visible", "text": "2 active" },
99
+ { "type": "text_visible", "text": "job_action_template_survives" },
100
+ { "type": "text_visible", "text": "Existing action template key" },
101
+ { "type": "text_visible", "text": "Failed to create API key" },
102
+ { "type": "text_visible", "text": "Active" },
103
+ { "type": "text_absent", "text": "API Key Created!" },
104
+ { "type": "text_absent", "text": "Expected property name" },
105
+ { "type": "text_absent", "text": "SyntaxError" },
106
+ { "type": "text_absent", "text": "[object Object]" },
107
+ { "type": "text_absent", "text": "Application error" },
108
+ { "type": "selector_count_equals", "selector": "[data-testid='api-key-create-error']", "expected_count": 1 },
109
+ { "type": "selector_count_equals", "selector": "[data-testid='recent-job-row']", "expected_count": 1 },
110
+ { "type": "selector_count_equals", "selector": "[data-testid='api-key-row']", "expected_count": 1 },
111
+ { "type": "selector_count_equals", "selector": "[data-testid='created-key-modal']", "expected_count": 0 },
112
+ { "type": "no_horizontal_overflow", "max_overflow_px": 1 },
113
+ { "type": "no_fatal_console_errors" },
114
+ { "type": "no_console_warnings" }
115
+ ],
116
+ "artifacts": ["screenshot", "console", "dom_summary", "proof_json"],
117
+ "baseline_policy": "invariant_only",
118
+ "failure_policy": {
119
+ "environment_blocked": "neutral",
120
+ "proof_insufficient": "fail",
121
+ "product_regression": "fail"
122
+ },
123
+ "metadata": {
124
+ "purpose": "Template for handled malformed action-success recovery: prove surrounding state still renders, the failed action shows one recovery message, success UI and raw parser text stay absent, the action request body is captured, and browser console evidence remains clean."
125
+ }
126
+ }