@riddledc/riddle-proof 0.5.23 → 0.5.25
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/{chunk-M4MISBQV.js → chunk-46355FJD.js} +14 -1
- package/dist/engine-harness.cjs +14 -1
- package/dist/engine-harness.js +1 -1
- package/dist/index.cjs +14 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/runtime/lib/verify.py +9 -1
- package/runtime/tests/recon_verify_smoke.py +101 -0
|
@@ -89,8 +89,20 @@ function loadRunState(input) {
|
|
|
89
89
|
state_path: statePath
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
|
+
function isProtectedFinalStatus(status) {
|
|
93
|
+
return status === "ready_to_ship" || status === "shipped" || status === "completed";
|
|
94
|
+
}
|
|
95
|
+
function shouldPreserveFinalizedRunState(filePath, incoming) {
|
|
96
|
+
const existing = readJson(filePath);
|
|
97
|
+
if (!existing?.finalized || !isProtectedFinalStatus(existing.status)) return false;
|
|
98
|
+
if (!incoming.finalized) return true;
|
|
99
|
+
if (existing.status === incoming.status) return false;
|
|
100
|
+
return !(existing.status === "ready_to_ship" && incoming.status === "shipped");
|
|
101
|
+
}
|
|
92
102
|
function persist(state) {
|
|
93
|
-
if (state.state_path)
|
|
103
|
+
if (!state.state_path) return;
|
|
104
|
+
if (shouldPreserveFinalizedRunState(state.state_path, state)) return;
|
|
105
|
+
writeJson(state.state_path, state);
|
|
94
106
|
}
|
|
95
107
|
function recordEvent(state, event) {
|
|
96
108
|
appendRunEvent(state, event);
|
|
@@ -325,6 +337,7 @@ function engineFailureBlocker(result, checkpoint) {
|
|
|
325
337
|
}
|
|
326
338
|
function terminalResult(state, status, result, summary, raw = {}) {
|
|
327
339
|
setRunStatus(state, status);
|
|
340
|
+
if (isProtectedFinalStatus(status)) state.finalized = true;
|
|
328
341
|
const metadata = normalizeTerminalMetadata({
|
|
329
342
|
riddleState: result ? fullRiddleState(result, state) : null,
|
|
330
343
|
engineResult: result
|
package/dist/engine-harness.cjs
CHANGED
|
@@ -3091,8 +3091,20 @@ function loadRunState(input) {
|
|
|
3091
3091
|
state_path: statePath
|
|
3092
3092
|
});
|
|
3093
3093
|
}
|
|
3094
|
+
function isProtectedFinalStatus(status) {
|
|
3095
|
+
return status === "ready_to_ship" || status === "shipped" || status === "completed";
|
|
3096
|
+
}
|
|
3097
|
+
function shouldPreserveFinalizedRunState(filePath, incoming) {
|
|
3098
|
+
const existing = readJson(filePath);
|
|
3099
|
+
if (!existing?.finalized || !isProtectedFinalStatus(existing.status)) return false;
|
|
3100
|
+
if (!incoming.finalized) return true;
|
|
3101
|
+
if (existing.status === incoming.status) return false;
|
|
3102
|
+
return !(existing.status === "ready_to_ship" && incoming.status === "shipped");
|
|
3103
|
+
}
|
|
3094
3104
|
function persist(state) {
|
|
3095
|
-
if (state.state_path)
|
|
3105
|
+
if (!state.state_path) return;
|
|
3106
|
+
if (shouldPreserveFinalizedRunState(state.state_path, state)) return;
|
|
3107
|
+
writeJson(state.state_path, state);
|
|
3096
3108
|
}
|
|
3097
3109
|
function recordEvent(state, event) {
|
|
3098
3110
|
appendRunEvent(state, event);
|
|
@@ -3327,6 +3339,7 @@ function engineFailureBlocker(result, checkpoint) {
|
|
|
3327
3339
|
}
|
|
3328
3340
|
function terminalResult(state, status, result, summary, raw = {}) {
|
|
3329
3341
|
setRunStatus(state, status);
|
|
3342
|
+
if (isProtectedFinalStatus(status)) state.finalized = true;
|
|
3330
3343
|
const metadata = normalizeTerminalMetadata({
|
|
3331
3344
|
riddleState: result ? fullRiddleState(result, state) : null,
|
|
3332
3345
|
engineResult: result
|
package/dist/engine-harness.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -3647,8 +3647,20 @@ function loadRunState(input) {
|
|
|
3647
3647
|
state_path: statePath
|
|
3648
3648
|
});
|
|
3649
3649
|
}
|
|
3650
|
+
function isProtectedFinalStatus(status) {
|
|
3651
|
+
return status === "ready_to_ship" || status === "shipped" || status === "completed";
|
|
3652
|
+
}
|
|
3653
|
+
function shouldPreserveFinalizedRunState(filePath, incoming) {
|
|
3654
|
+
const existing = readJson(filePath);
|
|
3655
|
+
if (!existing?.finalized || !isProtectedFinalStatus(existing.status)) return false;
|
|
3656
|
+
if (!incoming.finalized) return true;
|
|
3657
|
+
if (existing.status === incoming.status) return false;
|
|
3658
|
+
return !(existing.status === "ready_to_ship" && incoming.status === "shipped");
|
|
3659
|
+
}
|
|
3650
3660
|
function persist(state) {
|
|
3651
|
-
if (state.state_path)
|
|
3661
|
+
if (!state.state_path) return;
|
|
3662
|
+
if (shouldPreserveFinalizedRunState(state.state_path, state)) return;
|
|
3663
|
+
writeJson(state.state_path, state);
|
|
3652
3664
|
}
|
|
3653
3665
|
function recordEvent(state, event) {
|
|
3654
3666
|
appendRunEvent(state, event);
|
|
@@ -3883,6 +3895,7 @@ function engineFailureBlocker(result, checkpoint) {
|
|
|
3883
3895
|
}
|
|
3884
3896
|
function terminalResult(state, status, result, summary, raw = {}) {
|
|
3885
3897
|
setRunStatus(state, status);
|
|
3898
|
+
if (isProtectedFinalStatus(status)) state.finalized = true;
|
|
3886
3899
|
const metadata = normalizeTerminalMetadata({
|
|
3887
3900
|
riddleState: result ? fullRiddleState(result, state) : null,
|
|
3888
3901
|
engineResult: result
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
package/runtime/lib/verify.py
CHANGED
|
@@ -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.
|
|
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
|
|
|
@@ -126,6 +126,44 @@ 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
|
+
'console': [
|
|
158
|
+
'RIDDLE_PROOF_STATE:' + json.dumps(page_state),
|
|
159
|
+
'RIDDLE_PROOF_EVIDENCE:' + json.dumps(proof_evidence),
|
|
160
|
+
],
|
|
161
|
+
'_artifact_json': {
|
|
162
|
+
'proof.json': {
|
|
163
|
+
'script_error': 'Error: intentional capture script failure after evidence',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}
|
|
129
167
|
if 'window.__riddleProofEvidence' in script or 'globalThis.__riddleProofEvidence' in script:
|
|
130
168
|
page_state = {
|
|
131
169
|
'bodyTextLength': 36,
|
|
@@ -1226,6 +1264,68 @@ def run_verify_audio_rejects_failed_nested_proof_evidence():
|
|
|
1226
1264
|
shutil.rmtree(tempdir, ignore_errors=True)
|
|
1227
1265
|
|
|
1228
1266
|
|
|
1267
|
+
def run_verify_preserves_proof_evidence_on_capture_script_error():
|
|
1268
|
+
tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-verify-script-error-evidence-'))
|
|
1269
|
+
state_path = tempdir / 'state.json'
|
|
1270
|
+
try:
|
|
1271
|
+
state = base_state(tempdir, reference='before')
|
|
1272
|
+
state.update({
|
|
1273
|
+
'recon_status': 'ready_for_proof_plan',
|
|
1274
|
+
'author_status': 'ready',
|
|
1275
|
+
'proof_plan_status': 'ready',
|
|
1276
|
+
'implementation_status': 'changes_detected',
|
|
1277
|
+
'before_cdn': 'https://cdn.example.com/before.png',
|
|
1278
|
+
'verification_mode': 'audio',
|
|
1279
|
+
'server_path': '/sequencer',
|
|
1280
|
+
'proof_plan': 'Measure the rendered synth transient envelope and compare attack/energy metrics.',
|
|
1281
|
+
'capture_script': (
|
|
1282
|
+
"await page.evaluate(() => { "
|
|
1283
|
+
"document.body.dataset.throwAfterProofEvidence = '1'; "
|
|
1284
|
+
"window.__riddleProofEvidence = { "
|
|
1285
|
+
"proof_evidence_present: false, "
|
|
1286
|
+
"evidence_summary: 'Captured structured audio evidence before the capture script threw.', "
|
|
1287
|
+
"checks: { route_ok: true, ui_context_ok: true, source_audio_ok: false } "
|
|
1288
|
+
"}; }); "
|
|
1289
|
+
"throw new Error('intentional capture script failure after evidence');"
|
|
1290
|
+
),
|
|
1291
|
+
'recon_results': {
|
|
1292
|
+
'baselines': {'before': {'path': '/sequencer', 'url': 'https://cdn.example.com/before.png'}},
|
|
1293
|
+
},
|
|
1294
|
+
})
|
|
1295
|
+
write_state(state_path, state)
|
|
1296
|
+
os.environ['RIDDLE_PROOF_STATE_FILE'] = str(state_path)
|
|
1297
|
+
|
|
1298
|
+
fake = FakeRiddle()
|
|
1299
|
+
load_util_with_fake(fake)
|
|
1300
|
+
load_module('verify_preserves_script_error_evidence', VERIFY_PATH)
|
|
1301
|
+
after_verify = json.loads(state_path.read_text())
|
|
1302
|
+
|
|
1303
|
+
assert after_verify['verify_status'] == 'capture_incomplete'
|
|
1304
|
+
observation = after_verify['verify_results']['after']['observation']
|
|
1305
|
+
assert observation['valid'] is False
|
|
1306
|
+
artifact_summary = observation['details']['artifact_summary']
|
|
1307
|
+
assert artifact_summary['proof_script_error'] is True
|
|
1308
|
+
supporting = after_verify['verify_results']['after']['supporting_artifacts']
|
|
1309
|
+
assert supporting['has_structured_payload'] is True
|
|
1310
|
+
assert supporting['proof_evidence_present'] is True
|
|
1311
|
+
assert 'Captured structured audio evidence before the capture script threw' in supporting['proof_evidence_sample']
|
|
1312
|
+
capture_quality = after_verify['verify_decision_request']['capture_quality']
|
|
1313
|
+
assert capture_quality['decision'] == 'failed_proof_evidence'
|
|
1314
|
+
assert capture_quality['recommended_stage'] == 'author'
|
|
1315
|
+
assert 'proof_evidence_present=false' in capture_quality['summary']
|
|
1316
|
+
assert 'source_audio_ok' in capture_quality['summary']
|
|
1317
|
+
assert 'Structured proof evidence gate' in after_verify['proof_summary']
|
|
1318
|
+
|
|
1319
|
+
return {
|
|
1320
|
+
'ok': True,
|
|
1321
|
+
'verify_status': after_verify['verify_status'],
|
|
1322
|
+
'decision': capture_quality['decision'],
|
|
1323
|
+
'proof_script_error': artifact_summary['proof_script_error'],
|
|
1324
|
+
}
|
|
1325
|
+
finally:
|
|
1326
|
+
shutil.rmtree(tempdir, ignore_errors=True)
|
|
1327
|
+
|
|
1328
|
+
|
|
1229
1329
|
def run_verify_capture_retry():
|
|
1230
1330
|
tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-capture-retry-'))
|
|
1231
1331
|
state_path = tempdir / 'state.json'
|
|
@@ -1543,6 +1643,7 @@ if __name__ == '__main__':
|
|
|
1543
1643
|
'verify_structured_evidence_without_screenshot': run_verify_structured_evidence_without_screenshot(),
|
|
1544
1644
|
'verify_audio_requires_proof_evidence': run_verify_audio_requires_proof_evidence(),
|
|
1545
1645
|
'verify_audio_rejects_failed_nested_proof_evidence': run_verify_audio_rejects_failed_nested_proof_evidence(),
|
|
1646
|
+
'verify_preserves_proof_evidence_on_capture_script_error': run_verify_preserves_proof_evidence_on_capture_script_error(),
|
|
1546
1647
|
'verify_capture_retry': run_verify_capture_retry(),
|
|
1547
1648
|
'missing_baseline_guard': run_verify_missing_baseline(),
|
|
1548
1649
|
'ship_supervisor_gate': run_ship_missing_supervisor_gate(),
|