@riddledc/riddle-proof 0.5.13 → 0.5.15
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
package/runtime/lib/recon.py
CHANGED
|
@@ -244,7 +244,7 @@ def score_route_candidate(path, tokens):
|
|
|
244
244
|
def choose_target_path(explicit_path, prod_url, route_hints, tokens=None, explicit_source=''):
|
|
245
245
|
explicit = (explicit_path or '').strip()
|
|
246
246
|
explicit_source = (explicit_source or '').strip()
|
|
247
|
-
is_meaningful_explicit = bool(explicit) and (explicit != '/' or explicit_source
|
|
247
|
+
is_meaningful_explicit = bool(explicit) and (explicit != '/' or bool(explicit_source))
|
|
248
248
|
if is_meaningful_explicit:
|
|
249
249
|
return explicit if explicit.startswith('/') else '/' + explicit
|
|
250
250
|
candidates = route_candidates(route_hints, prod_url)
|
|
@@ -719,7 +719,7 @@ initial_target_path = choose_target_path(s.get('server_path', ''), s.get('prod_u
|
|
|
719
719
|
selected_route = next((item for item in route_options if item.get('path') == initial_target_path), None)
|
|
720
720
|
initial_hypothesis = {
|
|
721
721
|
'target_path': initial_target_path,
|
|
722
|
-
'path_source': 'state.server_path' if (s.get('server_path') or '').strip() and (s.get('server_path') != '/' or server_path_source) else ((selected_route or {}).get('reason') or 'fallback root'),
|
|
722
|
+
'path_source': ('state.server_path:' + server_path_source) if (s.get('server_path') or '').strip() and (s.get('server_path') != '/' or server_path_source) else ((selected_route or {}).get('reason') or 'fallback root'),
|
|
723
723
|
'reference': requested_reference,
|
|
724
724
|
'mode': s.get('mode', 'server'),
|
|
725
725
|
'wait_for_selector': (s.get('wait_for_selector') or '').strip(),
|
package/runtime/lib/verify.py
CHANGED
|
@@ -7,7 +7,7 @@ while good captures produce a structured evidence packet that the supervising ag
|
|
|
7
7
|
must assess before the wrapper routes back into author/implement/recon work or ship.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
import json, os, sys
|
|
10
|
+
import json, os, sys, time
|
|
11
11
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
12
12
|
from util import (
|
|
13
13
|
append_capture_diagnostic,
|
|
@@ -83,6 +83,53 @@ def auto_screenshot_for_mode(verification_mode):
|
|
|
83
83
|
return normalized_verification_mode(verification_mode) not in STRUCTURED_FIRST_MODES
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
def record_verify_phase(phase, status='running', summary=''):
|
|
87
|
+
global s
|
|
88
|
+
ts = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
|
89
|
+
try:
|
|
90
|
+
current = load_state()
|
|
91
|
+
except Exception:
|
|
92
|
+
current = dict(s)
|
|
93
|
+
runtime_step = current.get('current_runtime_step') if isinstance(current.get('current_runtime_step'), dict) else {}
|
|
94
|
+
if not runtime_step:
|
|
95
|
+
runtime_step = {
|
|
96
|
+
'step': 'verify',
|
|
97
|
+
'action': 'run',
|
|
98
|
+
'status': 'running',
|
|
99
|
+
'started_at': ts,
|
|
100
|
+
'workflow_file': 'riddle-proof-verify.lobster',
|
|
101
|
+
}
|
|
102
|
+
runtime_step['step'] = 'verify'
|
|
103
|
+
runtime_step['action'] = 'run'
|
|
104
|
+
runtime_step['status'] = 'running'
|
|
105
|
+
runtime_step['workflow_file'] = 'riddle-proof-verify.lobster'
|
|
106
|
+
runtime_step['phase'] = phase
|
|
107
|
+
runtime_step['phase_status'] = status
|
|
108
|
+
if status == 'running':
|
|
109
|
+
runtime_step['phase_started_at'] = ts
|
|
110
|
+
runtime_step.pop('phase_finished_at', None)
|
|
111
|
+
else:
|
|
112
|
+
runtime_step['phase_finished_at'] = ts
|
|
113
|
+
if summary:
|
|
114
|
+
runtime_step['summary'] = summary
|
|
115
|
+
current['current_runtime_step'] = runtime_step
|
|
116
|
+
events = current.get('runtime_events') if isinstance(current.get('runtime_events'), list) else []
|
|
117
|
+
events.append({
|
|
118
|
+
'ts': ts,
|
|
119
|
+
'kind': 'workflow.phase.' + ('started' if status == 'running' else 'finished'),
|
|
120
|
+
'step': 'verify',
|
|
121
|
+
'phase': phase,
|
|
122
|
+
'summary': summary or (phase + ' ' + status),
|
|
123
|
+
'details': {'status': status},
|
|
124
|
+
})
|
|
125
|
+
current['runtime_events'] = events[-100:]
|
|
126
|
+
current['runtime_updated_at'] = ts
|
|
127
|
+
save_state(current)
|
|
128
|
+
for key in ('current_runtime_step', 'runtime_events', 'runtime_updated_at'):
|
|
129
|
+
if key in current:
|
|
130
|
+
s[key] = current[key]
|
|
131
|
+
|
|
132
|
+
|
|
86
133
|
def payload_has_capture_artifacts(payload):
|
|
87
134
|
if not isinstance(payload, dict):
|
|
88
135
|
return False
|
|
@@ -108,6 +155,7 @@ def capture_payload_error(payload):
|
|
|
108
155
|
|
|
109
156
|
def abort_capture_failure(state, results, expected_path, message, raw_payload):
|
|
110
157
|
summary = 'After capture failed before usable proof artifacts were produced: ' + str(message).strip()
|
|
158
|
+
record_verify_phase('capture', 'failed', summary)
|
|
111
159
|
observation = {
|
|
112
160
|
'valid': False,
|
|
113
161
|
'reason': summary,
|
|
@@ -979,16 +1027,20 @@ if existing_prod:
|
|
|
979
1027
|
print('Prod baseline: ' + existing_prod)
|
|
980
1028
|
|
|
981
1029
|
# AFTER (always from after worktree)
|
|
1030
|
+
record_verify_phase('build', 'running', 'Building after worktree for verify capture.')
|
|
982
1031
|
print('Cleaning after .next cache...')
|
|
983
1032
|
sp.run('rm -rf .next', shell=True, cwd=after_dir, capture_output=True)
|
|
984
1033
|
|
|
985
1034
|
print('Building after worktree...')
|
|
986
1035
|
br = sp.run(build_cmd, shell=True, cwd=after_dir, capture_output=True, text=True, timeout=600)
|
|
987
1036
|
if br.returncode != 0:
|
|
1037
|
+
record_verify_phase('build', 'failed', 'After build failed: ' + br.stderr[:300])
|
|
988
1038
|
raise SystemExit('After build failed: ' + br.stderr[:500])
|
|
1039
|
+
record_verify_phase('build', 'completed', 'After worktree build completed.')
|
|
989
1040
|
|
|
990
1041
|
after_payload = {}
|
|
991
1042
|
static_reason = should_use_static_preview(after_dir, s) if mode == 'server' else ''
|
|
1043
|
+
record_verify_phase('capture', 'running', 'Capturing after-proof evidence.')
|
|
992
1044
|
if mode == 'server' and not static_reason:
|
|
993
1045
|
build_dir, server_command, server_exclude = prepare_server_preview(after_dir, s)
|
|
994
1046
|
|
|
@@ -1051,8 +1103,10 @@ else:
|
|
|
1051
1103
|
after_observation = evaluate_capture_quality(after_payload, expected_path, verification_mode)
|
|
1052
1104
|
results['after']['observation'] = after_observation
|
|
1053
1105
|
results['after']['supporting_artifacts'] = collect_supporting_artifacts(after_payload)
|
|
1106
|
+
record_verify_phase('capture', 'completed', 'After-proof capture completed.')
|
|
1054
1107
|
|
|
1055
1108
|
# Structured proof summary
|
|
1109
|
+
record_verify_phase('assessment', 'running', 'Assessing verify evidence bundle.')
|
|
1056
1110
|
s['verify_results'] = results
|
|
1057
1111
|
s['stage'] = 'verify'
|
|
1058
1112
|
assertions = s.get('parsed_assertions')
|
|
@@ -1211,6 +1265,7 @@ s['evidence_notes'] = [
|
|
|
1211
1265
|
]
|
|
1212
1266
|
|
|
1213
1267
|
save_state(s)
|
|
1268
|
+
record_verify_phase('assessment', 'completed', 'Verify evidence assessment completed.')
|
|
1214
1269
|
|
|
1215
1270
|
assessment_status = 'awaiting_supervising_agent' if s.get('verify_status') == 'evidence_captured' else 'capture_incomplete'
|
|
1216
1271
|
|
|
@@ -207,6 +207,30 @@ class FakeRiddle:
|
|
|
207
207
|
'largeVisibleElements': [{'tag': 'button', 'text': 'Reset Game'}],
|
|
208
208
|
}),
|
|
209
209
|
}
|
|
210
|
+
if (
|
|
211
|
+
'preview.example.com' in script
|
|
212
|
+
and '/pricing' not in script
|
|
213
|
+
and '/games/tic-tac-toe' not in script
|
|
214
|
+
and '/wrong' not in script
|
|
215
|
+
):
|
|
216
|
+
return {
|
|
217
|
+
'ok': True,
|
|
218
|
+
'screenshots': [{'url': 'https://cdn.example.com/home-before.png'}],
|
|
219
|
+
'outputs': [{'name': 'before.png', 'url': 'https://cdn.example.com/home-before.png'}],
|
|
220
|
+
'console': state_console({
|
|
221
|
+
'bodyTextLength': 180,
|
|
222
|
+
'visibleTextSample': 'Riddle Proof homepage hero Start Free',
|
|
223
|
+
'interactiveElements': 4,
|
|
224
|
+
'visibleInteractiveElements': 4,
|
|
225
|
+
'pathname': '/',
|
|
226
|
+
'title': 'Riddle',
|
|
227
|
+
'buttons': ['Start Free'],
|
|
228
|
+
'headings': ['Riddle Proof'],
|
|
229
|
+
'links': [],
|
|
230
|
+
'canvasCount': 0,
|
|
231
|
+
'largeVisibleElements': [{'tag': 'button', 'text': 'Start Free'}],
|
|
232
|
+
}),
|
|
233
|
+
}
|
|
210
234
|
raise AssertionError(f'unexpected riddle_script payload: {script}')
|
|
211
235
|
raise AssertionError(f'unexpected invoke_retry tool: {tool}')
|
|
212
236
|
|
|
@@ -581,6 +605,53 @@ def run_recon_prefers_route_literals_over_import_paths():
|
|
|
581
605
|
shutil.rmtree(tempdir, ignore_errors=True)
|
|
582
606
|
|
|
583
607
|
|
|
608
|
+
def run_recon_prefers_hint_root_over_single_route_literal():
|
|
609
|
+
tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-hint-root-'))
|
|
610
|
+
state_path = tempdir / 'state.json'
|
|
611
|
+
try:
|
|
612
|
+
route_snippet = "export const routes = [{ path: '/docs/riddle-proof/markdown', element: <Docs /> }];\n"
|
|
613
|
+
state = base_state(tempdir, reference='before')
|
|
614
|
+
make_project(tempdir / 'before', route_snippet)
|
|
615
|
+
make_project(tempdir / 'after', route_snippet)
|
|
616
|
+
state.update({
|
|
617
|
+
'server_path': '/',
|
|
618
|
+
'server_path_source': 'hint_cache',
|
|
619
|
+
'capture_hint': {
|
|
620
|
+
'source': 'hint_cache',
|
|
621
|
+
'applied_fields': ['server_path'],
|
|
622
|
+
'selected': {'server_path': '/'},
|
|
623
|
+
},
|
|
624
|
+
'change_request': 'Make a tiny harmless homepage copy tweak',
|
|
625
|
+
'success_criteria': 'The homepage hero copy reflects the tiny tweak.',
|
|
626
|
+
})
|
|
627
|
+
write_state(state_path, state)
|
|
628
|
+
os.environ['RIDDLE_PROOF_STATE_FILE'] = str(state_path)
|
|
629
|
+
|
|
630
|
+
fake = FakeRiddle()
|
|
631
|
+
load_util_with_fake(fake)
|
|
632
|
+
load_module('recon_hint_root_preference', RECON_PATH)
|
|
633
|
+
after_recon = json.loads(state_path.read_text())
|
|
634
|
+
|
|
635
|
+
current_plan = after_recon['recon_results']['current_plan']
|
|
636
|
+
candidate_paths = [item['path'] for item in current_plan['route_candidates']]
|
|
637
|
+
assert current_plan['target_path'] == '/', current_plan
|
|
638
|
+
assert current_plan['path_source'] == 'state.server_path:hint_cache', current_plan
|
|
639
|
+
assert '/docs/riddle-proof/markdown' in candidate_paths, candidate_paths
|
|
640
|
+
details = after_recon['recon_results']['attempt_history'][-1]['observations']['before']['details']
|
|
641
|
+
assert details['observed_path'] == '/'
|
|
642
|
+
assert 'Start Free' in details['visible_text_sample'], details
|
|
643
|
+
assert details['buttons'] == ['Start Free'], details
|
|
644
|
+
|
|
645
|
+
return {
|
|
646
|
+
'ok': True,
|
|
647
|
+
'target_path': current_plan['target_path'],
|
|
648
|
+
'path_source': current_plan['path_source'],
|
|
649
|
+
'candidate_paths': candidate_paths,
|
|
650
|
+
}
|
|
651
|
+
finally:
|
|
652
|
+
shutil.rmtree(tempdir, ignore_errors=True)
|
|
653
|
+
|
|
654
|
+
|
|
584
655
|
def run_author_applies_supervisor_packet():
|
|
585
656
|
tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-supervisor-apply-'))
|
|
586
657
|
state_path = tempdir / 'state.json'
|
|
@@ -697,6 +768,13 @@ def run_verify_requests_supervisor_assessment():
|
|
|
697
768
|
assert after_details['observed_path_raw'] == '/s/pv-after/pricing', after_details
|
|
698
769
|
assert 'Buy Now' in after_details['visible_text_sample'], after_details
|
|
699
770
|
assert after_details['buttons'] == ['Buy Now'], after_details
|
|
771
|
+
runtime_events = after_verify.get('runtime_events') or []
|
|
772
|
+
assert any(event.get('kind') == 'workflow.phase.started' and event.get('step') == 'verify' and event.get('phase') == 'build' for event in runtime_events)
|
|
773
|
+
assert any(event.get('kind') == 'workflow.phase.finished' and event.get('step') == 'verify' and event.get('phase') == 'build' for event in runtime_events)
|
|
774
|
+
assert any(event.get('kind') == 'workflow.phase.started' and event.get('step') == 'verify' and event.get('phase') == 'capture' for event in runtime_events)
|
|
775
|
+
assert any(event.get('kind') == 'workflow.phase.finished' and event.get('step') == 'verify' and event.get('phase') == 'capture' for event in runtime_events)
|
|
776
|
+
assert any(event.get('kind') == 'workflow.phase.started' and event.get('step') == 'verify' and event.get('phase') == 'assessment' for event in runtime_events)
|
|
777
|
+
assert any(event.get('kind') == 'workflow.phase.finished' and event.get('step') == 'verify' and event.get('phase') == 'assessment' for event in runtime_events)
|
|
700
778
|
|
|
701
779
|
return {
|
|
702
780
|
'ok': True,
|
|
@@ -1182,6 +1260,7 @@ if __name__ == '__main__':
|
|
|
1182
1260
|
'verify_quality_ignores_proof_telemetry_console_text': run_verify_quality_ignores_proof_telemetry_console_text(),
|
|
1183
1261
|
'recon_then_author_request': run_recon_then_author_request(),
|
|
1184
1262
|
'recon_route_literal_preference': run_recon_prefers_route_literals_over_import_paths(),
|
|
1263
|
+
'recon_hint_root_preference': run_recon_prefers_hint_root_over_single_route_literal(),
|
|
1185
1264
|
'author_applies_supervisor_packet': run_author_applies_supervisor_packet(),
|
|
1186
1265
|
'verify_requests_supervisor_assessment': run_verify_requests_supervisor_assessment(),
|
|
1187
1266
|
'verify_structured_evidence_without_screenshot': run_verify_structured_evidence_without_screenshot(),
|