@riddledc/riddle-proof 0.5.14 → 0.5.16
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 +1 -1
- package/runtime/lib/util.py +35 -0
- package/runtime/lib/verify.py +64 -5
- package/runtime/tests/recon_verify_smoke.py +40 -0
package/package.json
CHANGED
package/runtime/lib/util.py
CHANGED
|
@@ -566,6 +566,41 @@ def git(cmd, cwd):
|
|
|
566
566
|
return sp.run(cmd, shell=True, cwd=cwd, capture_output=True, text=True)
|
|
567
567
|
|
|
568
568
|
|
|
569
|
+
def run_project_build(project_dir, build_cmd, timeout=600, clean_cache_dir='.next'):
|
|
570
|
+
"""Build once with existing cache, then retry clean if the cached build fails."""
|
|
571
|
+
attempts = []
|
|
572
|
+
for clean_first in (False, True):
|
|
573
|
+
if clean_first and clean_cache_dir:
|
|
574
|
+
cache_path = os.path.join(project_dir, clean_cache_dir)
|
|
575
|
+
if os.path.exists(cache_path):
|
|
576
|
+
sp.run(
|
|
577
|
+
'rm -rf ' + shell_quote(clean_cache_dir),
|
|
578
|
+
shell=True,
|
|
579
|
+
cwd=project_dir,
|
|
580
|
+
capture_output=True,
|
|
581
|
+
text=True,
|
|
582
|
+
)
|
|
583
|
+
result = sp.run(build_cmd, shell=True, cwd=project_dir, capture_output=True, text=True, timeout=timeout)
|
|
584
|
+
attempts.append({
|
|
585
|
+
'clean_first': clean_first,
|
|
586
|
+
'returncode': int(result.returncode),
|
|
587
|
+
'stderr': (result.stderr or '')[:500],
|
|
588
|
+
})
|
|
589
|
+
if result.returncode == 0:
|
|
590
|
+
return {
|
|
591
|
+
'result': result,
|
|
592
|
+
'clean_retry_used': clean_first,
|
|
593
|
+
'attempts': attempts,
|
|
594
|
+
}
|
|
595
|
+
if not clean_cache_dir or clean_first:
|
|
596
|
+
break
|
|
597
|
+
return {
|
|
598
|
+
'result': result,
|
|
599
|
+
'clean_retry_used': False,
|
|
600
|
+
'attempts': attempts,
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
|
|
569
604
|
def load_package_json(project_dir):
|
|
570
605
|
package_json = os.path.join(project_dir, 'package.json')
|
|
571
606
|
if not os.path.exists(package_json):
|
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,
|
|
@@ -20,6 +20,7 @@ from util import (
|
|
|
20
20
|
load_state,
|
|
21
21
|
prepare_server_preview,
|
|
22
22
|
record_successful_capture_hint,
|
|
23
|
+
run_project_build,
|
|
23
24
|
save_state,
|
|
24
25
|
should_use_static_preview,
|
|
25
26
|
summarize_capture_artifacts,
|
|
@@ -83,6 +84,53 @@ def auto_screenshot_for_mode(verification_mode):
|
|
|
83
84
|
return normalized_verification_mode(verification_mode) not in STRUCTURED_FIRST_MODES
|
|
84
85
|
|
|
85
86
|
|
|
87
|
+
def record_verify_phase(phase, status='running', summary=''):
|
|
88
|
+
global s
|
|
89
|
+
ts = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
|
90
|
+
try:
|
|
91
|
+
current = load_state()
|
|
92
|
+
except Exception:
|
|
93
|
+
current = dict(s)
|
|
94
|
+
runtime_step = current.get('current_runtime_step') if isinstance(current.get('current_runtime_step'), dict) else {}
|
|
95
|
+
if not runtime_step:
|
|
96
|
+
runtime_step = {
|
|
97
|
+
'step': 'verify',
|
|
98
|
+
'action': 'run',
|
|
99
|
+
'status': 'running',
|
|
100
|
+
'started_at': ts,
|
|
101
|
+
'workflow_file': 'riddle-proof-verify.lobster',
|
|
102
|
+
}
|
|
103
|
+
runtime_step['step'] = 'verify'
|
|
104
|
+
runtime_step['action'] = 'run'
|
|
105
|
+
runtime_step['status'] = 'running'
|
|
106
|
+
runtime_step['workflow_file'] = 'riddle-proof-verify.lobster'
|
|
107
|
+
runtime_step['phase'] = phase
|
|
108
|
+
runtime_step['phase_status'] = status
|
|
109
|
+
if status == 'running':
|
|
110
|
+
runtime_step['phase_started_at'] = ts
|
|
111
|
+
runtime_step.pop('phase_finished_at', None)
|
|
112
|
+
else:
|
|
113
|
+
runtime_step['phase_finished_at'] = ts
|
|
114
|
+
if summary:
|
|
115
|
+
runtime_step['summary'] = summary
|
|
116
|
+
current['current_runtime_step'] = runtime_step
|
|
117
|
+
events = current.get('runtime_events') if isinstance(current.get('runtime_events'), list) else []
|
|
118
|
+
events.append({
|
|
119
|
+
'ts': ts,
|
|
120
|
+
'kind': 'workflow.phase.' + ('started' if status == 'running' else 'finished'),
|
|
121
|
+
'step': 'verify',
|
|
122
|
+
'phase': phase,
|
|
123
|
+
'summary': summary or (phase + ' ' + status),
|
|
124
|
+
'details': {'status': status},
|
|
125
|
+
})
|
|
126
|
+
current['runtime_events'] = events[-100:]
|
|
127
|
+
current['runtime_updated_at'] = ts
|
|
128
|
+
save_state(current)
|
|
129
|
+
for key in ('current_runtime_step', 'runtime_events', 'runtime_updated_at'):
|
|
130
|
+
if key in current:
|
|
131
|
+
s[key] = current[key]
|
|
132
|
+
|
|
133
|
+
|
|
86
134
|
def payload_has_capture_artifacts(payload):
|
|
87
135
|
if not isinstance(payload, dict):
|
|
88
136
|
return False
|
|
@@ -108,6 +156,7 @@ def capture_payload_error(payload):
|
|
|
108
156
|
|
|
109
157
|
def abort_capture_failure(state, results, expected_path, message, raw_payload):
|
|
110
158
|
summary = 'After capture failed before usable proof artifacts were produced: ' + str(message).strip()
|
|
159
|
+
record_verify_phase('capture', 'failed', summary)
|
|
111
160
|
observation = {
|
|
112
161
|
'valid': False,
|
|
113
162
|
'reason': summary,
|
|
@@ -979,16 +1028,23 @@ if existing_prod:
|
|
|
979
1028
|
print('Prod baseline: ' + existing_prod)
|
|
980
1029
|
|
|
981
1030
|
# AFTER (always from after worktree)
|
|
982
|
-
|
|
983
|
-
sp.run('rm -rf .next', shell=True, cwd=after_dir, capture_output=True)
|
|
984
|
-
|
|
1031
|
+
record_verify_phase('build', 'running', 'Building after worktree for verify capture.')
|
|
985
1032
|
print('Building after worktree...')
|
|
986
|
-
|
|
1033
|
+
build_attempt = run_project_build(after_dir, build_cmd, timeout=600, clean_cache_dir='.next')
|
|
1034
|
+
br = build_attempt.get('result')
|
|
1035
|
+
if build_attempt.get('clean_retry_used'):
|
|
1036
|
+
print('Verify build recovered after cleaning .next cache.')
|
|
987
1037
|
if br.returncode != 0:
|
|
1038
|
+
record_verify_phase('build', 'failed', 'After build failed: ' + br.stderr[:300])
|
|
988
1039
|
raise SystemExit('After build failed: ' + br.stderr[:500])
|
|
1040
|
+
if build_attempt.get('attempts'):
|
|
1041
|
+
s['verify_build_attempts'] = build_attempt['attempts']
|
|
1042
|
+
s['verify_build_clean_retry_used'] = bool(build_attempt.get('clean_retry_used'))
|
|
1043
|
+
record_verify_phase('build', 'completed', 'After worktree build completed.')
|
|
989
1044
|
|
|
990
1045
|
after_payload = {}
|
|
991
1046
|
static_reason = should_use_static_preview(after_dir, s) if mode == 'server' else ''
|
|
1047
|
+
record_verify_phase('capture', 'running', 'Capturing after-proof evidence.')
|
|
992
1048
|
if mode == 'server' and not static_reason:
|
|
993
1049
|
build_dir, server_command, server_exclude = prepare_server_preview(after_dir, s)
|
|
994
1050
|
|
|
@@ -1051,8 +1107,10 @@ else:
|
|
|
1051
1107
|
after_observation = evaluate_capture_quality(after_payload, expected_path, verification_mode)
|
|
1052
1108
|
results['after']['observation'] = after_observation
|
|
1053
1109
|
results['after']['supporting_artifacts'] = collect_supporting_artifacts(after_payload)
|
|
1110
|
+
record_verify_phase('capture', 'completed', 'After-proof capture completed.')
|
|
1054
1111
|
|
|
1055
1112
|
# Structured proof summary
|
|
1113
|
+
record_verify_phase('assessment', 'running', 'Assessing verify evidence bundle.')
|
|
1056
1114
|
s['verify_results'] = results
|
|
1057
1115
|
s['stage'] = 'verify'
|
|
1058
1116
|
assertions = s.get('parsed_assertions')
|
|
@@ -1211,6 +1269,7 @@ s['evidence_notes'] = [
|
|
|
1211
1269
|
]
|
|
1212
1270
|
|
|
1213
1271
|
save_state(s)
|
|
1272
|
+
record_verify_phase('assessment', 'completed', 'Verify evidence assessment completed.')
|
|
1214
1273
|
|
|
1215
1274
|
assessment_status = 'awaiting_supervising_agent' if s.get('verify_status') == 'evidence_captured' else 'capture_incomplete'
|
|
1216
1275
|
|
|
@@ -508,6 +508,38 @@ def base_state(tempdir: Path, *, reference='before', prod_url=''):
|
|
|
508
508
|
}
|
|
509
509
|
|
|
510
510
|
|
|
511
|
+
def run_project_build_retries_after_clean_failure():
|
|
512
|
+
tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-build-retry-'))
|
|
513
|
+
cache_dir = tempdir / '.next'
|
|
514
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
515
|
+
util = load_module('util_build_retry', UTIL_PATH)
|
|
516
|
+
original_run = util.sp.run
|
|
517
|
+
calls = []
|
|
518
|
+
|
|
519
|
+
def fake_run(cmd, *args, **kwargs):
|
|
520
|
+
calls.append(cmd)
|
|
521
|
+
if cmd == 'npm run build':
|
|
522
|
+
build_attempts = calls.count('npm run build')
|
|
523
|
+
if build_attempts == 1:
|
|
524
|
+
return sp.CompletedProcess(cmd, 1, '', 'stale cache')
|
|
525
|
+
return sp.CompletedProcess(cmd, 0, 'ok', '')
|
|
526
|
+
if cmd == 'rm -rf .next':
|
|
527
|
+
shutil.rmtree(cache_dir, ignore_errors=True)
|
|
528
|
+
return sp.CompletedProcess(cmd, 0, '', '')
|
|
529
|
+
raise AssertionError(f'unexpected command: {cmd}')
|
|
530
|
+
|
|
531
|
+
try:
|
|
532
|
+
util.sp.run = fake_run
|
|
533
|
+
result = util.run_project_build(str(tempdir), 'npm run build', timeout=30, clean_cache_dir='.next')
|
|
534
|
+
assert result['clean_retry_used'] is True
|
|
535
|
+
assert result['result'].returncode == 0
|
|
536
|
+
assert calls == ['npm run build', 'rm -rf .next', 'npm run build']
|
|
537
|
+
assert not cache_dir.exists()
|
|
538
|
+
finally:
|
|
539
|
+
util.sp.run = original_run
|
|
540
|
+
shutil.rmtree(tempdir)
|
|
541
|
+
|
|
542
|
+
|
|
511
543
|
def run_recon_then_author_request():
|
|
512
544
|
tempdir = Path(tempfile.mkdtemp(prefix='riddle-proof-supervisor-request-'))
|
|
513
545
|
state_path = tempdir / 'state.json'
|
|
@@ -768,6 +800,13 @@ def run_verify_requests_supervisor_assessment():
|
|
|
768
800
|
assert after_details['observed_path_raw'] == '/s/pv-after/pricing', after_details
|
|
769
801
|
assert 'Buy Now' in after_details['visible_text_sample'], after_details
|
|
770
802
|
assert after_details['buttons'] == ['Buy Now'], after_details
|
|
803
|
+
runtime_events = after_verify.get('runtime_events') or []
|
|
804
|
+
assert any(event.get('kind') == 'workflow.phase.started' and event.get('step') == 'verify' and event.get('phase') == 'build' for event in runtime_events)
|
|
805
|
+
assert any(event.get('kind') == 'workflow.phase.finished' and event.get('step') == 'verify' and event.get('phase') == 'build' for event in runtime_events)
|
|
806
|
+
assert any(event.get('kind') == 'workflow.phase.started' and event.get('step') == 'verify' and event.get('phase') == 'capture' for event in runtime_events)
|
|
807
|
+
assert any(event.get('kind') == 'workflow.phase.finished' and event.get('step') == 'verify' and event.get('phase') == 'capture' for event in runtime_events)
|
|
808
|
+
assert any(event.get('kind') == 'workflow.phase.started' and event.get('step') == 'verify' and event.get('phase') == 'assessment' for event in runtime_events)
|
|
809
|
+
assert any(event.get('kind') == 'workflow.phase.finished' and event.get('step') == 'verify' and event.get('phase') == 'assessment' for event in runtime_events)
|
|
771
810
|
|
|
772
811
|
return {
|
|
773
812
|
'ok': True,
|
|
@@ -1250,6 +1289,7 @@ if __name__ == '__main__':
|
|
|
1250
1289
|
'capture_artifact_enrichment': run_capture_artifact_enrichment(),
|
|
1251
1290
|
'capture_diagnostics_redaction': run_capture_diagnostics_redact_sensitive_values(),
|
|
1252
1291
|
'apply_auth_context': run_apply_auth_context_passes_supported_auth_payloads(),
|
|
1292
|
+
'run_project_build_retries_after_clean_failure': run_project_build_retries_after_clean_failure(),
|
|
1253
1293
|
'verify_quality_ignores_proof_telemetry_console_text': run_verify_quality_ignores_proof_telemetry_console_text(),
|
|
1254
1294
|
'recon_then_author_request': run_recon_then_author_request(),
|
|
1255
1295
|
'recon_route_literal_preference': run_recon_prefers_route_literals_over_import_paths(),
|