@riddledc/riddle-proof 0.5.15 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.5.15",
3
+ "version": "0.5.16",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -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):
@@ -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,
@@ -1028,14 +1029,17 @@ if existing_prod:
1028
1029
 
1029
1030
  # AFTER (always from after worktree)
1030
1031
  record_verify_phase('build', 'running', 'Building after worktree for verify capture.')
1031
- print('Cleaning after .next cache...')
1032
- sp.run('rm -rf .next', shell=True, cwd=after_dir, capture_output=True)
1033
-
1034
1032
  print('Building after worktree...')
1035
- br = sp.run(build_cmd, shell=True, cwd=after_dir, capture_output=True, text=True, timeout=600)
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.')
1036
1037
  if br.returncode != 0:
1037
1038
  record_verify_phase('build', 'failed', 'After build failed: ' + br.stderr[:300])
1038
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'))
1039
1043
  record_verify_phase('build', 'completed', 'After worktree build completed.')
1040
1044
 
1041
1045
  after_payload = {}
@@ -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'
@@ -1257,6 +1289,7 @@ if __name__ == '__main__':
1257
1289
  'capture_artifact_enrichment': run_capture_artifact_enrichment(),
1258
1290
  'capture_diagnostics_redaction': run_capture_diagnostics_redact_sensitive_values(),
1259
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(),
1260
1293
  'verify_quality_ignores_proof_telemetry_console_text': run_verify_quality_ignores_proof_telemetry_console_text(),
1261
1294
  'recon_then_author_request': run_recon_then_author_request(),
1262
1295
  'recon_route_literal_preference': run_recon_prefers_route_literals_over_import_paths(),