@riddledc/riddle-proof 0.5.3 → 0.5.5

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.
@@ -296,6 +296,20 @@ function requirePayload(action, payload, state, result) {
296
296
  }
297
297
  return null;
298
298
  }
299
+ function engineFailureBlocker(result, checkpoint) {
300
+ if (result.ok !== false) return null;
301
+ if (!checkpoint.endsWith("_failed") && !checkpoint.endsWith("_blocked")) return null;
302
+ return {
303
+ code: checkpoint,
304
+ checkpoint,
305
+ message: result.summary || `Riddle Proof engine stopped at ${checkpoint}.`,
306
+ details: compactRecord({
307
+ error: result.error,
308
+ approval: result.approval,
309
+ checkpointContract: result.checkpointContract || null
310
+ })
311
+ };
312
+ }
299
313
  function terminalResult(state, status, result, summary, raw = {}) {
300
314
  setRunStatus(state, status);
301
315
  const metadata = normalizeTerminalMetadata({
@@ -453,6 +467,10 @@ async function routeCheckpoint(request, state, result, agent, input) {
453
467
  terminal: terminalResult(state, "completed", result, result.summary || "Riddle Proof engine completed.")
454
468
  };
455
469
  }
470
+ const failureBlocker = engineFailureBlocker(result, checkpoint);
471
+ if (failureBlocker) {
472
+ return { blocker: failureBlocker };
473
+ }
456
474
  if ([
457
475
  "recon_human_escalation",
458
476
  "verify_human_escalation",
@@ -3266,6 +3266,20 @@ function requirePayload(action, payload, state, result) {
3266
3266
  }
3267
3267
  return null;
3268
3268
  }
3269
+ function engineFailureBlocker(result, checkpoint) {
3270
+ if (result.ok !== false) return null;
3271
+ if (!checkpoint.endsWith("_failed") && !checkpoint.endsWith("_blocked")) return null;
3272
+ return {
3273
+ code: checkpoint,
3274
+ checkpoint,
3275
+ message: result.summary || `Riddle Proof engine stopped at ${checkpoint}.`,
3276
+ details: compactRecord({
3277
+ error: result.error,
3278
+ approval: result.approval,
3279
+ checkpointContract: result.checkpointContract || null
3280
+ })
3281
+ };
3282
+ }
3269
3283
  function terminalResult(state, status, result, summary, raw = {}) {
3270
3284
  setRunStatus(state, status);
3271
3285
  const metadata = normalizeTerminalMetadata({
@@ -3423,6 +3437,10 @@ async function routeCheckpoint(request, state, result, agent, input) {
3423
3437
  terminal: terminalResult(state, "completed", result, result.summary || "Riddle Proof engine completed.")
3424
3438
  };
3425
3439
  }
3440
+ const failureBlocker = engineFailureBlocker(result, checkpoint);
3441
+ if (failureBlocker) {
3442
+ return { blocker: failureBlocker };
3443
+ }
3426
3444
  if ([
3427
3445
  "recon_human_escalation",
3428
3446
  "verify_human_escalation",
@@ -2,7 +2,7 @@ import {
2
2
  createDisabledRiddleProofAgentAdapter,
3
3
  readRiddleProofRunStatus,
4
4
  runRiddleProofEngineHarness
5
- } from "./chunk-MLJJIKTP.js";
5
+ } from "./chunk-GC7C2NRA.js";
6
6
  import "./chunk-GVPCSXN7.js";
7
7
  import "./chunk-TMMKRKY5.js";
8
8
  export {
package/dist/index.cjs CHANGED
@@ -3825,6 +3825,20 @@ function requirePayload(action, payload, state, result) {
3825
3825
  }
3826
3826
  return null;
3827
3827
  }
3828
+ function engineFailureBlocker(result, checkpoint) {
3829
+ if (result.ok !== false) return null;
3830
+ if (!checkpoint.endsWith("_failed") && !checkpoint.endsWith("_blocked")) return null;
3831
+ return {
3832
+ code: checkpoint,
3833
+ checkpoint,
3834
+ message: result.summary || `Riddle Proof engine stopped at ${checkpoint}.`,
3835
+ details: compactRecord({
3836
+ error: result.error,
3837
+ approval: result.approval,
3838
+ checkpointContract: result.checkpointContract || null
3839
+ })
3840
+ };
3841
+ }
3828
3842
  function terminalResult(state, status, result, summary, raw = {}) {
3829
3843
  setRunStatus(state, status);
3830
3844
  const metadata = normalizeTerminalMetadata({
@@ -3982,6 +3996,10 @@ async function routeCheckpoint(request, state, result, agent, input) {
3982
3996
  terminal: terminalResult(state, "completed", result, result.summary || "Riddle Proof engine completed.")
3983
3997
  };
3984
3998
  }
3999
+ const failureBlocker = engineFailureBlocker(result, checkpoint);
4000
+ if (failureBlocker) {
4001
+ return { blocker: failureBlocker };
4002
+ }
3985
4003
  if ([
3986
4004
  "recon_human_escalation",
3987
4005
  "verify_human_escalation",
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  createDisabledRiddleProofAgentAdapter,
14
14
  readRiddleProofRunStatus,
15
15
  runRiddleProofEngineHarness
16
- } from "./chunk-MLJJIKTP.js";
16
+ } from "./chunk-GC7C2NRA.js";
17
17
  import {
18
18
  runRiddleProof
19
19
  } from "./chunk-VSEKLCNU.js";
@@ -318,6 +318,11 @@ function writeDepsManifest(projectDir, fingerprint, installCmd) {
318
318
  writeFileSync(manifestPath, JSON.stringify({ fingerprint, install_cmd: installCmd }, null, 2));
319
319
  }
320
320
 
321
+ function dependencyInstallTimeoutMs() {
322
+ const parsed = Number.parseInt(process.env.RIDDLE_PROOF_INSTALL_TIMEOUT_MS || "", 10);
323
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 600000;
324
+ }
325
+
321
326
  export function ensureDeps({ projectDir, reuseFrom = "" } = {}) {
322
327
  const fingerprint = computeDependencyFingerprint(projectDir);
323
328
  if (!fingerprint) return "no_package_json";
@@ -341,7 +346,7 @@ export function ensureDeps({ projectDir, reuseFrom = "" } = {}) {
341
346
 
342
347
  const installCmd = detectInstallCommand(projectDir);
343
348
  if (!installCmd) return "no_install_command";
344
- const installResult = runSafe(`${installCmd} 2>&1 | tail -5`, projectDir, 300000);
349
+ const installResult = runSafe(`${installCmd} 2>&1 | tail -5`, projectDir, dependencyInstallTimeoutMs());
345
350
  if (!installResult.ok) {
346
351
  throw new Error(`dependency install failed in ${projectDir}: ${installResult.output.slice(0, 300)}`);
347
352
  }
@@ -380,6 +385,9 @@ async function main() {
380
385
  case "ensure-deps":
381
386
  ok({ status: ensureDeps(payload) });
382
387
  return;
388
+ case "dependency-fingerprint":
389
+ ok({ fingerprint: computeDependencyFingerprint(payload.projectDir) });
390
+ return;
383
391
  default:
384
392
  throw new Error(`Unsupported command: ${command}`);
385
393
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -6,7 +6,7 @@ workspace root by default:
6
6
  <workspace>/.riddle-proof-worktrees/riddle-proof-<run_id>-after
7
7
  """
8
8
 
9
- import json, subprocess as sp, os, sys, shutil
9
+ import json, subprocess as sp, os, sys, shutil, time
10
10
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
11
11
  from util import load_state, save_state, git, shell_quote
12
12
 
@@ -37,6 +37,15 @@ SKILLS_ROOT = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__fil
37
37
  WORKSPACE_CORE = os.path.join(SKILLS_ROOT, 'lib', 'workspace-core.mjs')
38
38
 
39
39
 
40
+ def dependency_timeout_seconds():
41
+ raw = os.environ.get('RIDDLE_PROOF_INSTALL_TIMEOUT_MS', '').strip()
42
+ try:
43
+ millis = int(raw)
44
+ except Exception:
45
+ millis = 600000
46
+ return max(660, int((millis + 999) / 1000) + 30)
47
+
48
+
40
49
  def workspace_core(command, payload, timeout=180):
41
50
  if not os.path.exists(WORKSPACE_CORE):
42
51
  raise SystemExit('workspace core helper missing: ' + WORKSPACE_CORE)
@@ -64,10 +73,89 @@ def ensure_deps(project_dir, reuse_from=''):
64
73
  payload = {'projectDir': project_dir}
65
74
  if reuse_from:
66
75
  payload['reuseFrom'] = reuse_from
67
- result = workspace_core('ensure-deps', payload, timeout=300)
76
+ result = workspace_core('ensure-deps', payload, timeout=dependency_timeout_seconds())
68
77
  return result.get('status', '')
69
78
 
70
79
 
80
+ def dependency_fingerprint(project_dir):
81
+ if not project_dir:
82
+ return ''
83
+ result = workspace_core('dependency-fingerprint', {'projectDir': project_dir}, timeout=30)
84
+ return result.get('fingerprint', '') or ''
85
+
86
+
87
+ def record_setup_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': 'setup',
98
+ 'action': 'run',
99
+ 'status': 'running',
100
+ 'started_at': ts,
101
+ 'workflow_file': 'riddle-proof-setup.lobster',
102
+ }
103
+ runtime_step['phase'] = phase
104
+ runtime_step['phase_status'] = status
105
+ if status == 'running':
106
+ runtime_step['phase_started_at'] = ts
107
+ runtime_step.pop('phase_finished_at', None)
108
+ else:
109
+ runtime_step['phase_finished_at'] = ts
110
+ if summary:
111
+ runtime_step['summary'] = summary
112
+ current['current_runtime_step'] = runtime_step
113
+ events = current.get('runtime_events') if isinstance(current.get('runtime_events'), list) else []
114
+ events.append({
115
+ 'ts': ts,
116
+ 'kind': 'workflow.phase.' + ('started' if status == 'running' else 'finished'),
117
+ 'step': 'setup',
118
+ 'phase': phase,
119
+ 'summary': summary or (phase + ' ' + status),
120
+ 'details': {'status': status},
121
+ })
122
+ current['runtime_events'] = events[-100:]
123
+ current['runtime_updated_at'] = ts
124
+ save_state(current)
125
+ for key in ('current_runtime_step', 'runtime_events', 'runtime_updated_at'):
126
+ if key in current:
127
+ s[key] = current[key]
128
+
129
+
130
+ def ensure_deps_phase(phase, project_dir, reuse_from='', summary=''):
131
+ record_setup_phase(phase, 'running', summary)
132
+ try:
133
+ status = ensure_deps(project_dir, reuse_from=reuse_from)
134
+ except BaseException as exc:
135
+ record_setup_phase(phase, 'failed', str(exc)[:300])
136
+ raise
137
+ record_setup_phase(phase, 'completed', status or 'no dependency install needed')
138
+ return status
139
+
140
+
141
+ def dependencies_match(left_dir, right_dir):
142
+ left = dependency_fingerprint(left_dir)
143
+ right = dependency_fingerprint(right_dir)
144
+ return bool(left and right and left == right)
145
+
146
+
147
+ def compatible_reuse_source(source_dir, target_dirs):
148
+ if not source_dir:
149
+ return ''
150
+ source = dependency_fingerprint(source_dir)
151
+ if not source:
152
+ return ''
153
+ for target_dir in target_dirs:
154
+ if target_dir and dependency_fingerprint(target_dir) == source:
155
+ return source_dir
156
+ return ''
157
+
158
+
71
159
  def resolve_worktree_root(repo_dir):
72
160
  configured = (s.get('worktree_root') or os.environ.get('RIDDLE_PROOF_WORKTREE_ROOT') or '').strip()
73
161
  if configured:
@@ -338,17 +426,36 @@ print('After worktree: ' + AFTER_DIR + ' (' + AFTER_WORKTREE_BRANCH + ' -> ' + b
338
426
  apply_repo_profile(AFTER_DIR)
339
427
  save_state(s)
340
428
 
429
+ target_dependency_dirs = [AFTER_DIR]
430
+ if reference in ('before', 'both'):
431
+ target_dependency_dirs.append(BEFORE_DIR)
432
+
341
433
  reuse_source = repo_dir if os.path.exists(os.path.join(repo_dir, 'package.json')) else ''
342
- shared_status = ensure_deps(reuse_source) if reuse_source else ''
343
- if shared_status:
344
- print('Shared deps status: ' + shared_status)
434
+ shared_reuse_source = compatible_reuse_source(reuse_source, target_dependency_dirs)
435
+ shared_status = ''
436
+ if shared_reuse_source:
437
+ shared_status = ensure_deps_phase('shared_deps', shared_reuse_source, summary='Ensuring shared repository dependencies.')
438
+ if shared_status:
439
+ print('Shared deps status: ' + shared_status)
440
+ elif reuse_source:
441
+ record_setup_phase(
442
+ 'shared_deps',
443
+ 'completed',
444
+ 'skipped: active workspace dependencies differ from proof worktrees',
445
+ )
446
+ print('Shared deps skipped: active workspace dependencies differ from proof worktrees')
345
447
 
346
448
  before_dep_status = ''
347
449
  if reference in ('before', 'both'):
348
- before_dep_status = ensure_deps(BEFORE_DIR, reuse_from=reuse_source)
450
+ before_dep_status = ensure_deps_phase('before_deps', BEFORE_DIR, reuse_from=shared_reuse_source, summary='Ensuring before-worktree dependencies.')
349
451
  print('Before deps status: ' + before_dep_status)
350
452
 
351
- after_dep_status = ensure_deps(AFTER_DIR, reuse_from=reuse_source)
453
+ after_reuse_source = ''
454
+ if before_dep_status and dependencies_match(BEFORE_DIR, AFTER_DIR):
455
+ after_reuse_source = BEFORE_DIR
456
+ elif shared_reuse_source:
457
+ after_reuse_source = shared_reuse_source
458
+ after_dep_status = ensure_deps_phase('after_deps', AFTER_DIR, reuse_from=after_reuse_source, summary='Ensuring after-worktree dependencies.')
352
459
  print('After deps status: ' + after_dep_status)
353
460
 
354
461
  # Patch Next.js config in after worktree if needed