@riddledc/riddle-proof 0.5.2 → 0.5.4

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/index.cjs CHANGED
@@ -947,6 +947,87 @@ function updateState(statePath, mutate) {
947
947
  writeState(statePath, state);
948
948
  return state;
949
949
  }
950
+ function nowIso() {
951
+ return (/* @__PURE__ */ new Date()).toISOString();
952
+ }
953
+ function appendRuntimeEventToState(state, event) {
954
+ const events = Array.isArray(state.runtime_events) ? state.runtime_events : [];
955
+ state.runtime_events = [...events, event].slice(-RUNTIME_EVENT_LIMIT);
956
+ state.runtime_updated_at = event.ts;
957
+ }
958
+ function beginRuntimeStep(statePath, action, step, workflowPath) {
959
+ const timer = {
960
+ startedAt: nowIso(),
961
+ startedMs: Date.now()
962
+ };
963
+ updateState(statePath, (state) => {
964
+ const current = {
965
+ step,
966
+ action,
967
+ status: "running",
968
+ started_at: timer.startedAt,
969
+ workflow_file: import_node_path2.default.basename(workflowPath)
970
+ };
971
+ state.current_runtime_step = current;
972
+ appendRuntimeEventToState(state, {
973
+ ts: timer.startedAt,
974
+ kind: "workflow.step.started",
975
+ step,
976
+ action,
977
+ summary: `Started ${step} workflow step.`,
978
+ details: {
979
+ workflow_file: import_node_path2.default.basename(workflowPath)
980
+ }
981
+ });
982
+ });
983
+ return timer;
984
+ }
985
+ function finishRuntimeStep(statePath, action, result, timer) {
986
+ const finishedAt = nowIso();
987
+ const durationMs = Date.now() - timer.startedMs;
988
+ const summary = result.haltedForApproval ? `${result.step} halted for approval.` : result.ok ? `Finished ${result.step} workflow step.` : `${result.step} workflow step failed.`;
989
+ updateState(statePath, (state) => {
990
+ const completed = {
991
+ step: result.step,
992
+ action,
993
+ status: result.haltedForApproval ? "approval_required" : result.ok ? "completed" : "failed",
994
+ started_at: timer.startedAt,
995
+ finished_at: finishedAt,
996
+ duration_ms: durationMs,
997
+ ok: result.ok,
998
+ halted_for_approval: result.haltedForApproval || false,
999
+ auto_approved: result.autoApproved || false,
1000
+ error: result.error || null
1001
+ };
1002
+ state.current_runtime_step = null;
1003
+ state.last_runtime_step = completed;
1004
+ appendRuntimeEventToState(state, {
1005
+ ts: finishedAt,
1006
+ kind: "workflow.step.finished",
1007
+ step: result.step,
1008
+ action,
1009
+ summary,
1010
+ details: completed
1011
+ });
1012
+ });
1013
+ return {
1014
+ ...result,
1015
+ started_at: timer.startedAt,
1016
+ finished_at: finishedAt,
1017
+ duration_ms: durationMs
1018
+ };
1019
+ }
1020
+ function executedStep(res, extra = {}) {
1021
+ const output = {
1022
+ step: res.step,
1023
+ ok: res.ok,
1024
+ haltedForApproval: res.haltedForApproval || false,
1025
+ autoApproved: res.autoApproved || false,
1026
+ ...extra
1027
+ };
1028
+ if (typeof res.duration_ms === "number") output.duration_ms = res.duration_ms;
1029
+ return output;
1030
+ }
950
1031
  function hasSupervisorProofAssessment(state) {
951
1032
  const proofAssessment = state?.proof_assessment || {};
952
1033
  const source = String(proofAssessment?.source || state?.proof_assessment_source || "").trim().toLowerCase();
@@ -1409,53 +1490,74 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1409
1490
  const lobsterPrefix = process.env.RIDDLE_PROOF_LOBSTER_SCRIPT ? [process.env.RIDDLE_PROOF_LOBSTER_SCRIPT] : [];
1410
1491
  const runOne = (step) => {
1411
1492
  const args = step === "setup" ? buildSetupArgs(params, config) : {};
1493
+ const stepWorkflowFile = workflowFile(config.riddleProofDir, step);
1494
+ const timer = beginRuntimeStep(config.statePath, action, step, stepWorkflowFile);
1412
1495
  let output;
1413
1496
  try {
1414
1497
  output = JSON.parse(
1415
- (0, import_node_child_process.execFileSync)(lobsterCommand, [...lobsterPrefix, "run", "--file", workflowFile(config.riddleProofDir, step), "--args-json", JSON.stringify(args)], {
1498
+ (0, import_node_child_process.execFileSync)(lobsterCommand, [...lobsterPrefix, "run", "--file", stepWorkflowFile, "--args-json", JSON.stringify(args)], {
1416
1499
  encoding: "utf-8",
1417
1500
  env
1418
1501
  })
1419
1502
  );
1420
1503
  } catch (error) {
1421
- return {
1504
+ return finishRuntimeStep(config.statePath, action, {
1422
1505
  ok: false,
1423
1506
  step,
1424
1507
  error: error?.message || String(error),
1425
1508
  stdout: String(error?.stdout || ""),
1426
1509
  stderr: String(error?.stderr || "")
1427
- };
1510
+ }, timer);
1428
1511
  }
1429
1512
  if (output?.status === "needs_approval") {
1430
1513
  if (!params.auto_approve) {
1431
- return {
1514
+ return finishRuntimeStep(config.statePath, action, {
1432
1515
  ok: false,
1433
1516
  haltedForApproval: true,
1434
1517
  step,
1435
1518
  approval: output.requiresApproval || null,
1436
1519
  raw: output
1437
- };
1520
+ }, timer);
1438
1521
  }
1439
1522
  const token = output?.requiresApproval?.resumeToken;
1440
- if (!token) throw new Error(`${step} requested approval without a resume token.`);
1441
- const resumed = JSON.parse(
1442
- (0, import_node_child_process.execFileSync)(lobsterCommand, [...lobsterPrefix, "resume", "--token", token, "--approve", "yes"], {
1443
- encoding: "utf-8",
1444
- env
1445
- })
1446
- );
1447
- return {
1523
+ if (!token) {
1524
+ return finishRuntimeStep(config.statePath, action, {
1525
+ ok: false,
1526
+ step,
1527
+ error: `${step} requested approval without a resume token.`,
1528
+ raw: output
1529
+ }, timer);
1530
+ }
1531
+ let resumed;
1532
+ try {
1533
+ resumed = JSON.parse(
1534
+ (0, import_node_child_process.execFileSync)(lobsterCommand, [...lobsterPrefix, "resume", "--token", token, "--approve", "yes"], {
1535
+ encoding: "utf-8",
1536
+ env
1537
+ })
1538
+ );
1539
+ } catch (error) {
1540
+ return finishRuntimeStep(config.statePath, action, {
1541
+ ok: false,
1542
+ step,
1543
+ autoApproved: true,
1544
+ error: error?.message || String(error),
1545
+ stdout: String(error?.stdout || ""),
1546
+ stderr: String(error?.stderr || "")
1547
+ }, timer);
1548
+ }
1549
+ return finishRuntimeStep(config.statePath, action, {
1448
1550
  ok: resumed?.ok !== false,
1449
1551
  step,
1450
1552
  autoApproved: true,
1451
1553
  raw: resumed
1452
- };
1554
+ }, timer);
1453
1555
  }
1454
- return {
1556
+ return finishRuntimeStep(config.statePath, action, {
1455
1557
  ok: output?.ok !== false,
1456
1558
  step,
1457
1559
  raw: output
1458
- };
1560
+ }, timer);
1459
1561
  };
1460
1562
  let effectiveAdvanceStage = params.advance_stage || null;
1461
1563
  const recordAttempt = (stage, status, summary, extra = {}) => {
@@ -1581,7 +1683,7 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1581
1683
  let state = readState(config.statePath);
1582
1684
  if (!state || !state.workspace_ready || params.advance_stage === "setup") {
1583
1685
  const setupRes = runOne("setup");
1584
- executed.push({ step: "setup", ok: setupRes.ok, haltedForApproval: setupRes.haltedForApproval || false, autoApproved: setupRes.autoApproved || false });
1686
+ executed.push(executedStep(setupRes));
1585
1687
  if (!setupRes.ok || setupRes.haltedForApproval) {
1586
1688
  return failedRun("setup", setupRes.haltedForApproval ? "setup halted for approval" : "setup failed", setupRes, {
1587
1689
  checkpoint: "setup_blocked"
@@ -1791,7 +1893,7 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1791
1893
  }
1792
1894
  if (!state?.recon_results || state?.stage === "setup" || state?.stage === "preflight" || ["needs_agent_decision", "needs_supervisor_judgment"].includes(state?.recon_status || "") || requestedStage === "recon") {
1793
1895
  const reconRes = runOne("recon");
1794
- executed.push({ step: "recon", ok: reconRes.ok, haltedForApproval: reconRes.haltedForApproval || false, autoApproved: reconRes.autoApproved || false });
1896
+ executed.push(executedStep(reconRes));
1795
1897
  if (!reconRes.ok || reconRes.haltedForApproval) {
1796
1898
  return failedRun("recon", reconRes.haltedForApproval ? "recon halted for approval" : "recon failed", reconRes, {
1797
1899
  checkpoint: "recon_failed",
@@ -1839,7 +1941,7 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1839
1941
  state = readState(config.statePath);
1840
1942
  if (!authorReady(state) || effectiveAdvanceStage === "author") {
1841
1943
  const authorRes = runOne("author");
1842
- executed.push({ step: "author", ok: authorRes.ok, haltedForApproval: authorRes.haltedForApproval || false, autoApproved: authorRes.autoApproved || false });
1944
+ executed.push(executedStep(authorRes));
1843
1945
  if (!authorRes.ok || authorRes.haltedForApproval) {
1844
1946
  return failedRun("author", authorRes.haltedForApproval ? "author halted for approval" : "author failed", authorRes, {
1845
1947
  checkpoint: "author_failed",
@@ -1954,7 +2056,7 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1954
2056
  }
1955
2057
  if (effectiveAdvanceStage === "implement") {
1956
2058
  const implementRes = runOne("implement");
1957
- executed.push({ step: "implement", ok: implementRes.ok, haltedForApproval: implementRes.haltedForApproval || false, autoApproved: implementRes.autoApproved || false });
2059
+ executed.push(executedStep(implementRes));
1958
2060
  if (implementRes.haltedForApproval) {
1959
2061
  return failedRun("implement", "implement halted for approval", implementRes, {
1960
2062
  checkpoint: "implement_blocked",
@@ -2044,7 +2146,7 @@ ${implementRes.stderr || ""}`;
2044
2146
  let verifyRes = { ok: true, step: "verify", reusedEvidence: canReuseVerifyEvidence };
2045
2147
  if (!canReuseVerifyEvidence) {
2046
2148
  verifyRes = runOne("verify");
2047
- executed.push({ step: "verify", ok: verifyRes.ok, haltedForApproval: verifyRes.haltedForApproval || false, autoApproved: verifyRes.autoApproved || false });
2149
+ executed.push(executedStep(verifyRes));
2048
2150
  if (!verifyRes.ok || verifyRes.haltedForApproval) {
2049
2151
  return failedRun("verify", verifyRes.haltedForApproval ? "verify halted for approval" : "verify failed", verifyRes, {
2050
2152
  checkpoint: "verify_failed",
@@ -2053,7 +2155,7 @@ ${implementRes.stderr || ""}`;
2053
2155
  });
2054
2156
  }
2055
2157
  } else {
2056
- executed.push({ step: "verify", ok: true, reusedEvidence: true, haltedForApproval: false, autoApproved: false });
2158
+ executed.push(executedStep(verifyRes, { reusedEvidence: true }));
2057
2159
  }
2058
2160
  state = readState(config.statePath);
2059
2161
  const verifyStatus = state?.verify_status || ((state?.after_cdn || "").trim() ? "evidence_captured" : "capture_incomplete");
@@ -2190,7 +2292,7 @@ ${implementRes.stderr || ""}`;
2190
2292
  details: { ...verifyDetails, shipGate }
2191
2293
  });
2192
2294
  const shipRes = runOne("ship");
2193
- executed.push({ step: "ship", ok: shipRes.ok, haltedForApproval: shipRes.haltedForApproval || false, autoApproved: shipRes.autoApproved || false });
2295
+ executed.push(executedStep(shipRes));
2194
2296
  if (!shipRes.ok || shipRes.haltedForApproval) {
2195
2297
  const shipNextAction = shipRes?.error && String(shipRes.error).includes("temporary proof branch") ? "product bug: ship resolved a temporary proof branch; resolve the PR head branch before retrying ship" : "inspect the ship error, confirm the PR head branch and verified commit, then retry ship";
2196
2298
  return failedRun("ship", shipRes.haltedForApproval ? "ship halted for approval" : "ship failed", shipRes, {
@@ -2369,7 +2471,7 @@ ${implementRes.stderr || ""}`;
2369
2471
  return shipGateBlocked(state, executed, { shipAssessment: shipAssessment.raw });
2370
2472
  }
2371
2473
  const shipRes = runOne("ship");
2372
- executed.push({ step: "ship", ok: shipRes.ok, haltedForApproval: shipRes.haltedForApproval || false, autoApproved: shipRes.autoApproved || false });
2474
+ executed.push(executedStep(shipRes));
2373
2475
  if (!shipRes.ok || shipRes.haltedForApproval) {
2374
2476
  return failedRun("ship", shipRes.haltedForApproval ? "ship halted for approval" : "ship failed", shipRes, {
2375
2477
  checkpoint: "ship_failed",
@@ -2495,7 +2597,7 @@ function createRiddleProofEngine(pluginConfig = {}) {
2495
2597
  }
2496
2598
  };
2497
2599
  }
2498
- var import_node_child_process, import_node_fs2, import_node_path2;
2600
+ var import_node_child_process, import_node_fs2, import_node_path2, RUNTIME_EVENT_LIMIT;
2499
2601
  var init_proof_run_engine = __esm({
2500
2602
  "src/proof-run-engine.ts"() {
2501
2603
  "use strict";
@@ -2503,6 +2605,7 @@ var init_proof_run_engine = __esm({
2503
2605
  import_node_fs2 = require("fs");
2504
2606
  import_node_path2 = __toESM(require("path"), 1);
2505
2607
  init_proof_run_core();
2608
+ RUNTIME_EVENT_LIMIT = 100;
2506
2609
  }
2507
2610
  });
2508
2611
 
@@ -3460,6 +3563,22 @@ function createHarnessStatePath(stateDir) {
3460
3563
  const stamp = timestamp2().replace(/\D/g, "").slice(0, 14) || "unknown";
3461
3564
  return import_node_path3.default.join(stateDir, `riddle-proof-run-${stamp}-${import_node_crypto2.default.randomUUID().slice(0, 8)}.json`);
3462
3565
  }
3566
+ function createEngineStatePath(state, config) {
3567
+ const existing = nonEmptyString(state.request.engine_state_path);
3568
+ if (existing) return existing;
3569
+ const harnessStatePath = nonEmptyString(state.state_path);
3570
+ if (harnessStatePath) {
3571
+ const dir = import_node_path3.default.dirname(harnessStatePath);
3572
+ const base = import_node_path3.default.basename(harnessStatePath);
3573
+ if (base.startsWith("riddle-proof-run-")) {
3574
+ return import_node_path3.default.join(dir, base.replace("riddle-proof-run-", "riddle-proof-state-"));
3575
+ }
3576
+ return import_node_path3.default.join(dir, `${base}.engine-state.json`);
3577
+ }
3578
+ const stateDir = config?.stateDir || "/tmp";
3579
+ const stamp = timestamp2().replace(/\D/g, "").slice(0, 14) || "unknown";
3580
+ return import_node_path3.default.join(stateDir, `riddle-proof-state-${stamp}-${import_node_crypto2.default.randomUUID().slice(0, 8)}.json`);
3581
+ }
3463
3582
  function ensureParent(filePath) {
3464
3583
  (0, import_node_fs3.mkdirSync)(import_node_path3.default.dirname(filePath), { recursive: true });
3465
3584
  }
@@ -3502,6 +3621,18 @@ function heartbeat(state, input) {
3502
3621
  function jsonParam(payload) {
3503
3622
  return JSON.stringify(payload);
3504
3623
  }
3624
+ function redactedWorkflowParams(params) {
3625
+ const secretKeys = /* @__PURE__ */ new Set([
3626
+ "auth_localStorage_json",
3627
+ "auth_cookies_json",
3628
+ "auth_headers_json"
3629
+ ]);
3630
+ const output = {};
3631
+ for (const [key, value] of Object.entries(params)) {
3632
+ output[key] = secretKeys.has(key) && value ? "[redacted]" : value;
3633
+ }
3634
+ return output;
3635
+ }
3505
3636
  function engineStatePath(result, state) {
3506
3637
  return nonEmptyString(result.state_path) || nonEmptyString(state.request.engine_state_path);
3507
3638
  }
@@ -3694,6 +3825,20 @@ function requirePayload(action, payload, state, result) {
3694
3825
  }
3695
3826
  return null;
3696
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
+ }
3697
3842
  function terminalResult(state, status, result, summary, raw = {}) {
3698
3843
  setRunStatus(state, status);
3699
3844
  const metadata = normalizeTerminalMetadata({
@@ -3851,6 +3996,10 @@ async function routeCheckpoint(request, state, result, agent, input) {
3851
3996
  terminal: terminalResult(state, "completed", result, result.summary || "Riddle Proof engine completed.")
3852
3997
  };
3853
3998
  }
3999
+ const failureBlocker = engineFailureBlocker(result, checkpoint);
4000
+ if (failureBlocker) {
4001
+ return { blocker: failureBlocker };
4002
+ }
3854
4003
  if ([
3855
4004
  "recon_human_escalation",
3856
4005
  "verify_human_escalation",
@@ -4008,6 +4157,7 @@ function readRiddleProofRunStatus(state_path) {
4008
4157
  async function runRiddleProofEngineHarness(input) {
4009
4158
  const state = loadRunState(input);
4010
4159
  state.request = normalizeRunParams({ ...state.request, ...input.request });
4160
+ state.request.engine_state_path = nonEmptyString(input.resume_params?.state_path) || nonEmptyString(state.request.engine_state_path) || createEngineStatePath(state, input.config);
4011
4161
  const request = state.request;
4012
4162
  const agent = input.agent || createDisabledRiddleProofAgentAdapter();
4013
4163
  const maxIterations = Math.max(
@@ -4063,24 +4213,41 @@ async function runRiddleProofEngineHarness(input) {
4063
4213
  branch: state.branch || null
4064
4214
  }
4065
4215
  });
4216
+ const engineCallStartedAt = timestamp2();
4217
+ const engineCallStartedMs = Date.now();
4066
4218
  recordEvent(state, {
4067
4219
  kind: "engine.call",
4068
4220
  checkpoint: "engine_call",
4069
4221
  stage,
4070
4222
  summary: "Calling Riddle Proof engine.",
4071
- details: { params: nextParams }
4223
+ details: {
4224
+ params: redactedWorkflowParams(nextParams),
4225
+ started_at: engineCallStartedAt
4226
+ }
4072
4227
  });
4073
4228
  let result;
4074
4229
  try {
4075
4230
  result = await engine.execute(nextParams);
4076
4231
  } catch (error) {
4077
4232
  const message = error instanceof Error ? error.message : String(error);
4233
+ recordEvent(state, {
4234
+ kind: "engine.exception",
4235
+ checkpoint: "engine_call_failed",
4236
+ stage,
4237
+ summary: message,
4238
+ details: {
4239
+ duration_ms: Date.now() - engineCallStartedMs,
4240
+ started_at: engineCallStartedAt,
4241
+ finished_at: timestamp2()
4242
+ }
4243
+ });
4078
4244
  return blockerResult(state, lastResult, {
4079
4245
  code: "riddle_engine_exception",
4080
4246
  checkpoint: "engine_call_failed",
4081
4247
  message
4082
4248
  });
4083
4249
  }
4250
+ const engineCallDurationMs = Date.now() - engineCallStartedMs;
4084
4251
  lastResult = result;
4085
4252
  const engineState = engineStatePath(result, state);
4086
4253
  if (engineState) state.request.engine_state_path = engineState;
@@ -4105,7 +4272,10 @@ async function runRiddleProofEngineHarness(input) {
4105
4272
  details: {
4106
4273
  ok: result.ok ?? null,
4107
4274
  engine_state_path: engineState || null,
4108
- checkpoint: result.checkpoint || null
4275
+ checkpoint: result.checkpoint || null,
4276
+ duration_ms: engineCallDurationMs,
4277
+ started_at: engineCallStartedAt,
4278
+ finished_at: timestamp2()
4109
4279
  }
4110
4280
  });
4111
4281
  const routed = await routeCheckpoint(request, state, result, agent, input);
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  createDisabledRiddleProofAgentAdapter,
14
14
  readRiddleProofRunStatus,
15
15
  runRiddleProofEngineHarness
16
- } from "./chunk-5GZZZ6JA.js";
16
+ } from "./chunk-GC7C2NRA.js";
17
17
  import {
18
18
  runRiddleProof
19
19
  } from "./chunk-VSEKLCNU.js";
@@ -944,6 +944,88 @@ function updateState(statePath, mutate) {
944
944
  writeState(statePath, state);
945
945
  return state;
946
946
  }
947
+ var RUNTIME_EVENT_LIMIT = 100;
948
+ function nowIso() {
949
+ return (/* @__PURE__ */ new Date()).toISOString();
950
+ }
951
+ function appendRuntimeEventToState(state, event) {
952
+ const events = Array.isArray(state.runtime_events) ? state.runtime_events : [];
953
+ state.runtime_events = [...events, event].slice(-RUNTIME_EVENT_LIMIT);
954
+ state.runtime_updated_at = event.ts;
955
+ }
956
+ function beginRuntimeStep(statePath, action, step, workflowPath) {
957
+ const timer = {
958
+ startedAt: nowIso(),
959
+ startedMs: Date.now()
960
+ };
961
+ updateState(statePath, (state) => {
962
+ const current = {
963
+ step,
964
+ action,
965
+ status: "running",
966
+ started_at: timer.startedAt,
967
+ workflow_file: import_node_path2.default.basename(workflowPath)
968
+ };
969
+ state.current_runtime_step = current;
970
+ appendRuntimeEventToState(state, {
971
+ ts: timer.startedAt,
972
+ kind: "workflow.step.started",
973
+ step,
974
+ action,
975
+ summary: `Started ${step} workflow step.`,
976
+ details: {
977
+ workflow_file: import_node_path2.default.basename(workflowPath)
978
+ }
979
+ });
980
+ });
981
+ return timer;
982
+ }
983
+ function finishRuntimeStep(statePath, action, result, timer) {
984
+ const finishedAt = nowIso();
985
+ const durationMs = Date.now() - timer.startedMs;
986
+ const summary = result.haltedForApproval ? `${result.step} halted for approval.` : result.ok ? `Finished ${result.step} workflow step.` : `${result.step} workflow step failed.`;
987
+ updateState(statePath, (state) => {
988
+ const completed = {
989
+ step: result.step,
990
+ action,
991
+ status: result.haltedForApproval ? "approval_required" : result.ok ? "completed" : "failed",
992
+ started_at: timer.startedAt,
993
+ finished_at: finishedAt,
994
+ duration_ms: durationMs,
995
+ ok: result.ok,
996
+ halted_for_approval: result.haltedForApproval || false,
997
+ auto_approved: result.autoApproved || false,
998
+ error: result.error || null
999
+ };
1000
+ state.current_runtime_step = null;
1001
+ state.last_runtime_step = completed;
1002
+ appendRuntimeEventToState(state, {
1003
+ ts: finishedAt,
1004
+ kind: "workflow.step.finished",
1005
+ step: result.step,
1006
+ action,
1007
+ summary,
1008
+ details: completed
1009
+ });
1010
+ });
1011
+ return {
1012
+ ...result,
1013
+ started_at: timer.startedAt,
1014
+ finished_at: finishedAt,
1015
+ duration_ms: durationMs
1016
+ };
1017
+ }
1018
+ function executedStep(res, extra = {}) {
1019
+ const output = {
1020
+ step: res.step,
1021
+ ok: res.ok,
1022
+ haltedForApproval: res.haltedForApproval || false,
1023
+ autoApproved: res.autoApproved || false,
1024
+ ...extra
1025
+ };
1026
+ if (typeof res.duration_ms === "number") output.duration_ms = res.duration_ms;
1027
+ return output;
1028
+ }
947
1029
  function hasSupervisorProofAssessment(state) {
948
1030
  const proofAssessment = state?.proof_assessment || {};
949
1031
  const source = String(proofAssessment?.source || state?.proof_assessment_source || "").trim().toLowerCase();
@@ -1406,53 +1488,74 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1406
1488
  const lobsterPrefix = process.env.RIDDLE_PROOF_LOBSTER_SCRIPT ? [process.env.RIDDLE_PROOF_LOBSTER_SCRIPT] : [];
1407
1489
  const runOne = (step) => {
1408
1490
  const args = step === "setup" ? buildSetupArgs(params, config) : {};
1491
+ const stepWorkflowFile = workflowFile(config.riddleProofDir, step);
1492
+ const timer = beginRuntimeStep(config.statePath, action, step, stepWorkflowFile);
1409
1493
  let output;
1410
1494
  try {
1411
1495
  output = JSON.parse(
1412
- (0, import_node_child_process.execFileSync)(lobsterCommand, [...lobsterPrefix, "run", "--file", workflowFile(config.riddleProofDir, step), "--args-json", JSON.stringify(args)], {
1496
+ (0, import_node_child_process.execFileSync)(lobsterCommand, [...lobsterPrefix, "run", "--file", stepWorkflowFile, "--args-json", JSON.stringify(args)], {
1413
1497
  encoding: "utf-8",
1414
1498
  env
1415
1499
  })
1416
1500
  );
1417
1501
  } catch (error) {
1418
- return {
1502
+ return finishRuntimeStep(config.statePath, action, {
1419
1503
  ok: false,
1420
1504
  step,
1421
1505
  error: error?.message || String(error),
1422
1506
  stdout: String(error?.stdout || ""),
1423
1507
  stderr: String(error?.stderr || "")
1424
- };
1508
+ }, timer);
1425
1509
  }
1426
1510
  if (output?.status === "needs_approval") {
1427
1511
  if (!params.auto_approve) {
1428
- return {
1512
+ return finishRuntimeStep(config.statePath, action, {
1429
1513
  ok: false,
1430
1514
  haltedForApproval: true,
1431
1515
  step,
1432
1516
  approval: output.requiresApproval || null,
1433
1517
  raw: output
1434
- };
1518
+ }, timer);
1435
1519
  }
1436
1520
  const token = output?.requiresApproval?.resumeToken;
1437
- if (!token) throw new Error(`${step} requested approval without a resume token.`);
1438
- const resumed = JSON.parse(
1439
- (0, import_node_child_process.execFileSync)(lobsterCommand, [...lobsterPrefix, "resume", "--token", token, "--approve", "yes"], {
1440
- encoding: "utf-8",
1441
- env
1442
- })
1443
- );
1444
- return {
1521
+ if (!token) {
1522
+ return finishRuntimeStep(config.statePath, action, {
1523
+ ok: false,
1524
+ step,
1525
+ error: `${step} requested approval without a resume token.`,
1526
+ raw: output
1527
+ }, timer);
1528
+ }
1529
+ let resumed;
1530
+ try {
1531
+ resumed = JSON.parse(
1532
+ (0, import_node_child_process.execFileSync)(lobsterCommand, [...lobsterPrefix, "resume", "--token", token, "--approve", "yes"], {
1533
+ encoding: "utf-8",
1534
+ env
1535
+ })
1536
+ );
1537
+ } catch (error) {
1538
+ return finishRuntimeStep(config.statePath, action, {
1539
+ ok: false,
1540
+ step,
1541
+ autoApproved: true,
1542
+ error: error?.message || String(error),
1543
+ stdout: String(error?.stdout || ""),
1544
+ stderr: String(error?.stderr || "")
1545
+ }, timer);
1546
+ }
1547
+ return finishRuntimeStep(config.statePath, action, {
1445
1548
  ok: resumed?.ok !== false,
1446
1549
  step,
1447
1550
  autoApproved: true,
1448
1551
  raw: resumed
1449
- };
1552
+ }, timer);
1450
1553
  }
1451
- return {
1554
+ return finishRuntimeStep(config.statePath, action, {
1452
1555
  ok: output?.ok !== false,
1453
1556
  step,
1454
1557
  raw: output
1455
- };
1558
+ }, timer);
1456
1559
  };
1457
1560
  let effectiveAdvanceStage = params.advance_stage || null;
1458
1561
  const recordAttempt = (stage, status, summary, extra = {}) => {
@@ -1578,7 +1681,7 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1578
1681
  let state = readState(config.statePath);
1579
1682
  if (!state || !state.workspace_ready || params.advance_stage === "setup") {
1580
1683
  const setupRes = runOne("setup");
1581
- executed.push({ step: "setup", ok: setupRes.ok, haltedForApproval: setupRes.haltedForApproval || false, autoApproved: setupRes.autoApproved || false });
1684
+ executed.push(executedStep(setupRes));
1582
1685
  if (!setupRes.ok || setupRes.haltedForApproval) {
1583
1686
  return failedRun("setup", setupRes.haltedForApproval ? "setup halted for approval" : "setup failed", setupRes, {
1584
1687
  checkpoint: "setup_blocked"
@@ -1788,7 +1891,7 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1788
1891
  }
1789
1892
  if (!state?.recon_results || state?.stage === "setup" || state?.stage === "preflight" || ["needs_agent_decision", "needs_supervisor_judgment"].includes(state?.recon_status || "") || requestedStage === "recon") {
1790
1893
  const reconRes = runOne("recon");
1791
- executed.push({ step: "recon", ok: reconRes.ok, haltedForApproval: reconRes.haltedForApproval || false, autoApproved: reconRes.autoApproved || false });
1894
+ executed.push(executedStep(reconRes));
1792
1895
  if (!reconRes.ok || reconRes.haltedForApproval) {
1793
1896
  return failedRun("recon", reconRes.haltedForApproval ? "recon halted for approval" : "recon failed", reconRes, {
1794
1897
  checkpoint: "recon_failed",
@@ -1836,7 +1939,7 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1836
1939
  state = readState(config.statePath);
1837
1940
  if (!authorReady(state) || effectiveAdvanceStage === "author") {
1838
1941
  const authorRes = runOne("author");
1839
- executed.push({ step: "author", ok: authorRes.ok, haltedForApproval: authorRes.haltedForApproval || false, autoApproved: authorRes.autoApproved || false });
1942
+ executed.push(executedStep(authorRes));
1840
1943
  if (!authorRes.ok || authorRes.haltedForApproval) {
1841
1944
  return failedRun("author", authorRes.haltedForApproval ? "author halted for approval" : "author failed", authorRes, {
1842
1945
  checkpoint: "author_failed",
@@ -1951,7 +2054,7 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1951
2054
  }
1952
2055
  if (effectiveAdvanceStage === "implement") {
1953
2056
  const implementRes = runOne("implement");
1954
- executed.push({ step: "implement", ok: implementRes.ok, haltedForApproval: implementRes.haltedForApproval || false, autoApproved: implementRes.autoApproved || false });
2057
+ executed.push(executedStep(implementRes));
1955
2058
  if (implementRes.haltedForApproval) {
1956
2059
  return failedRun("implement", "implement halted for approval", implementRes, {
1957
2060
  checkpoint: "implement_blocked",
@@ -2041,7 +2144,7 @@ ${implementRes.stderr || ""}`;
2041
2144
  let verifyRes = { ok: true, step: "verify", reusedEvidence: canReuseVerifyEvidence };
2042
2145
  if (!canReuseVerifyEvidence) {
2043
2146
  verifyRes = runOne("verify");
2044
- executed.push({ step: "verify", ok: verifyRes.ok, haltedForApproval: verifyRes.haltedForApproval || false, autoApproved: verifyRes.autoApproved || false });
2147
+ executed.push(executedStep(verifyRes));
2045
2148
  if (!verifyRes.ok || verifyRes.haltedForApproval) {
2046
2149
  return failedRun("verify", verifyRes.haltedForApproval ? "verify halted for approval" : "verify failed", verifyRes, {
2047
2150
  checkpoint: "verify_failed",
@@ -2050,7 +2153,7 @@ ${implementRes.stderr || ""}`;
2050
2153
  });
2051
2154
  }
2052
2155
  } else {
2053
- executed.push({ step: "verify", ok: true, reusedEvidence: true, haltedForApproval: false, autoApproved: false });
2156
+ executed.push(executedStep(verifyRes, { reusedEvidence: true }));
2054
2157
  }
2055
2158
  state = readState(config.statePath);
2056
2159
  const verifyStatus = state?.verify_status || ((state?.after_cdn || "").trim() ? "evidence_captured" : "capture_incomplete");
@@ -2187,7 +2290,7 @@ ${implementRes.stderr || ""}`;
2187
2290
  details: { ...verifyDetails, shipGate }
2188
2291
  });
2189
2292
  const shipRes = runOne("ship");
2190
- executed.push({ step: "ship", ok: shipRes.ok, haltedForApproval: shipRes.haltedForApproval || false, autoApproved: shipRes.autoApproved || false });
2293
+ executed.push(executedStep(shipRes));
2191
2294
  if (!shipRes.ok || shipRes.haltedForApproval) {
2192
2295
  const shipNextAction = shipRes?.error && String(shipRes.error).includes("temporary proof branch") ? "product bug: ship resolved a temporary proof branch; resolve the PR head branch before retrying ship" : "inspect the ship error, confirm the PR head branch and verified commit, then retry ship";
2193
2296
  return failedRun("ship", shipRes.haltedForApproval ? "ship halted for approval" : "ship failed", shipRes, {
@@ -2366,7 +2469,7 @@ ${implementRes.stderr || ""}`;
2366
2469
  return shipGateBlocked(state, executed, { shipAssessment: shipAssessment.raw });
2367
2470
  }
2368
2471
  const shipRes = runOne("ship");
2369
- executed.push({ step: "ship", ok: shipRes.ok, haltedForApproval: shipRes.haltedForApproval || false, autoApproved: shipRes.autoApproved || false });
2472
+ executed.push(executedStep(shipRes));
2370
2473
  if (!shipRes.ok || shipRes.haltedForApproval) {
2371
2474
  return failedRun("ship", shipRes.haltedForApproval ? "ship halted for approval" : "ship failed", shipRes, {
2372
2475
  checkpoint: "ship_failed",