@riddledc/riddle-proof 0.5.24 → 0.5.26

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.5.24",
3
+ "version": "0.5.26",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -218,8 +218,15 @@ def abort_capture_failure(state, results, expected_path, message, raw_payload):
218
218
  def build_probe_capture_script(base_script='', verification_mode='proof'):
219
219
  pieces = []
220
220
  script = (base_script or '').strip()
221
+ pieces.append('let __riddleProofCaptureScriptError = null;')
221
222
  if script:
222
- pieces.append(script.rstrip(';') + ';')
223
+ pieces.extend([
224
+ 'try {',
225
+ script.rstrip(';') + ';',
226
+ '} catch (err) {',
227
+ ' __riddleProofCaptureScriptError = err;',
228
+ '}',
229
+ ])
223
230
  pieces.extend([
224
231
  f'await page.waitForTimeout({HYDRATION_WAIT_MS});',
225
232
  'const pageState = await page.evaluate(() => {',
@@ -273,6 +280,7 @@ def build_probe_capture_script(base_script='', verification_mode='proof'):
273
280
  ])
274
281
  if auto_screenshot_for_mode(verification_mode) and not capture_script_saves_screenshot(script):
275
282
  pieces.append("await saveScreenshot('after-proof');")
283
+ pieces.append('if (__riddleProofCaptureScriptError) throw __riddleProofCaptureScriptError;')
276
284
  pieces.append('return { pageState, proofEvidence: __riddleProofEvidenceValue };')
277
285
  return ' '.join(pieces)
278
286
 
@@ -367,6 +375,7 @@ def extract_page_state(payload):
367
375
 
368
376
 
369
377
  def extract_proof_evidence(payload):
378
+ payload = enrich_capture_payload(payload)
370
379
  evidence = []
371
380
  for text in iter_console_messages(payload.get('console') or []):
372
381
  raw_evidence = proof_evidence_console_payload(text)
@@ -126,6 +126,52 @@ class FakeRiddle:
126
126
  'RIDDLE_PROOF_EVIDENCE ' + json.dumps(proof_evidence),
127
127
  ],
128
128
  }
129
+ if 'throwAfterProofEvidence' in script:
130
+ assert '__riddleProofCaptureScriptError' in script
131
+ page_state = {
132
+ 'bodyTextLength': 96,
133
+ 'visibleTextSample': 'Neon step sequencer audio workbench',
134
+ 'interactiveElements': 0,
135
+ 'visibleInteractiveElements': 0,
136
+ 'pathname': '/s/pv-after/sequencer',
137
+ 'title': 'Sequencer',
138
+ 'buttons': [],
139
+ 'headings': ['Sequencer'],
140
+ 'links': [],
141
+ 'canvasCount': 1,
142
+ 'largeVisibleElements': [{'tag': 'canvas', 'text': ''}],
143
+ }
144
+ proof_evidence = {
145
+ 'proof_evidence_present': False,
146
+ 'evidence_summary': 'Captured structured audio evidence before the capture script threw.',
147
+ 'checks': {
148
+ 'route_ok': True,
149
+ 'ui_context_ok': True,
150
+ 'source_audio_ok': False,
151
+ },
152
+ }
153
+ return {
154
+ 'ok': True,
155
+ 'outputs': [{'name': 'proof.json', 'url': 'https://cdn.example.com/proof.json'}],
156
+ 'result': {'pageState': page_state},
157
+ '_artifact_json': {
158
+ 'console.json': {
159
+ 'summary': {'total_entries': 3, 'log_count': 2, 'warn_count': 0, 'error_count': 1, 'info_count': 0},
160
+ 'entries': {
161
+ 'log': [
162
+ {'message': 'RIDDLE_PROOF_STATE:' + json.dumps(page_state)},
163
+ {'message': 'RIDDLE_PROOF_EVIDENCE:' + json.dumps(proof_evidence)},
164
+ ],
165
+ 'warn': [],
166
+ 'error': [{'message': 'Uncaught exception: intentional capture script failure after evidence'}],
167
+ 'info': [],
168
+ },
169
+ },
170
+ 'proof.json': {
171
+ 'script_error': 'Error: intentional capture script failure after evidence',
172
+ },
173
+ },
174
+ }
129
175
  if 'window.__riddleProofEvidence' in script or 'globalThis.__riddleProofEvidence' in script:
130
176
  page_state = {
131
177
  'bodyTextLength': 36,
@@ -1226,6 +1272,70 @@ def run_verify_audio_rejects_failed_nested_proof_evidence():
1226
1272
  shutil.rmtree(tempdir, ignore_errors=True)
1227
1273
 
1228
1274
 
1275
+ def run_verify_preserves_proof_evidence_on_capture_script_error():
1276
+ tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-verify-script-error-evidence-'))
1277
+ state_path = tempdir / 'state.json'
1278
+ try:
1279
+ state = base_state(tempdir, reference='before')
1280
+ state.update({
1281
+ 'recon_status': 'ready_for_proof_plan',
1282
+ 'author_status': 'ready',
1283
+ 'proof_plan_status': 'ready',
1284
+ 'implementation_status': 'changes_detected',
1285
+ 'before_cdn': 'https://cdn.example.com/before.png',
1286
+ 'verification_mode': 'audio',
1287
+ 'server_path': '/sequencer',
1288
+ 'proof_plan': 'Measure the rendered synth transient envelope and compare attack/energy metrics.',
1289
+ 'capture_script': (
1290
+ "await page.evaluate(() => { "
1291
+ "document.body.dataset.throwAfterProofEvidence = '1'; "
1292
+ "window.__riddleProofEvidence = { "
1293
+ "proof_evidence_present: false, "
1294
+ "evidence_summary: 'Captured structured audio evidence before the capture script threw.', "
1295
+ "checks: { route_ok: true, ui_context_ok: true, source_audio_ok: false } "
1296
+ "}; }); "
1297
+ "throw new Error('intentional capture script failure after evidence');"
1298
+ ),
1299
+ 'recon_results': {
1300
+ 'baselines': {'before': {'path': '/sequencer', 'url': 'https://cdn.example.com/before.png'}},
1301
+ },
1302
+ })
1303
+ write_state(state_path, state)
1304
+ os.environ['RIDDLE_PROOF_STATE_FILE'] = str(state_path)
1305
+
1306
+ fake = FakeRiddle()
1307
+ load_util_with_fake(fake)
1308
+ load_module('verify_preserves_script_error_evidence', VERIFY_PATH)
1309
+ after_verify = json.loads(state_path.read_text())
1310
+
1311
+ assert after_verify['verify_status'] == 'capture_incomplete'
1312
+ observation = after_verify['verify_results']['after']['observation']
1313
+ assert observation['valid'] is False
1314
+ artifact_summary = observation['details']['artifact_summary']
1315
+ assert artifact_summary['proof_script_error'] is True
1316
+ supporting = after_verify['verify_results']['after']['supporting_artifacts']
1317
+ assert supporting['has_structured_payload'] is True
1318
+ assert supporting['proof_evidence_present'] is True
1319
+ assert 'Captured structured audio evidence before the capture script threw' in supporting['proof_evidence_sample']
1320
+ assert after_verify['evidence_bundle']['proof_evidence'] is not None
1321
+ assert after_verify['evidence_bundle']['proof_evidence']['proof_evidence_present'] is False
1322
+ capture_quality = after_verify['verify_decision_request']['capture_quality']
1323
+ assert capture_quality['decision'] == 'failed_proof_evidence'
1324
+ assert capture_quality['recommended_stage'] == 'author'
1325
+ assert 'proof_evidence_present=false' in capture_quality['summary']
1326
+ assert 'source_audio_ok' in capture_quality['summary']
1327
+ assert 'Structured proof evidence gate' in after_verify['proof_summary']
1328
+
1329
+ return {
1330
+ 'ok': True,
1331
+ 'verify_status': after_verify['verify_status'],
1332
+ 'decision': capture_quality['decision'],
1333
+ 'proof_script_error': artifact_summary['proof_script_error'],
1334
+ }
1335
+ finally:
1336
+ shutil.rmtree(tempdir, ignore_errors=True)
1337
+
1338
+
1229
1339
  def run_verify_capture_retry():
1230
1340
  tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-capture-retry-'))
1231
1341
  state_path = tempdir / 'state.json'
@@ -1543,6 +1653,7 @@ if __name__ == '__main__':
1543
1653
  'verify_structured_evidence_without_screenshot': run_verify_structured_evidence_without_screenshot(),
1544
1654
  'verify_audio_requires_proof_evidence': run_verify_audio_requires_proof_evidence(),
1545
1655
  'verify_audio_rejects_failed_nested_proof_evidence': run_verify_audio_rejects_failed_nested_proof_evidence(),
1656
+ 'verify_preserves_proof_evidence_on_capture_script_error': run_verify_preserves_proof_evidence_on_capture_script_error(),
1546
1657
  'verify_capture_retry': run_verify_capture_retry(),
1547
1658
  'missing_baseline_guard': run_verify_missing_baseline(),
1548
1659
  'ship_supervisor_gate': run_ship_missing_supervisor_gate(),