{"record":{"id":"06db42f01db52339","repo":"ruvnet/ruflo","slug":"run-completedat-precedes-startedat","errorCode":null,"errorMessage":"run completedAt precedes startedAt","messagePattern":"run completedAt precedes startedAt","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts","lineNumber":32,"sourceCode":"\nfunction timestamp(value: string, label: string): number {\n  const parsed = Date.parse(value);\n  if (!Number.isFinite(parsed)) throw new Error(`${label} must be an ISO timestamp`);\n  return parsed;\n}\n\nfunction validateRun(run: RunEvidence): void {\n  if (!run.executionId.trim() || !run.sessionId.trim() || !run.workloadId.trim()) {\n    throw new Error('run execution, session, and workload identity are required');\n  }\n  if (!DIGEST.test(run.sourceState.sourceStateId)) throw new Error('run sourceStateId is invalid');\n  if (!DIGEST.test(run.commandDigest) || !DIGEST.test(run.evidenceDigest)) {\n    throw new Error('run command and evidence digests must be canonical sha256 values');\n  }\n  if (!Number.isSafeInteger(run.exitCode)) throw new Error('run exitCode must be a safe integer');\n  const started = timestamp(run.startedAt, 'startedAt');\n  const completed = timestamp(run.completedAt, 'completedAt');\n  if (completed < started) throw new Error('run completedAt precedes startedAt');\n  if (\n    run.buildEvidence !== undefined\n    && run.buildEvidence.sourceStateId !== run.sourceState.sourceStateId\n  ) {\n    throw new Error('build evidence belongs to a different source state');\n  }\n}\n\n/**\n * Unsigned, non-durable, content-addressed in-memory conformance ledger.\n *\n * Exact retries converge on one receipt. Reusing an execution ID with changed\n * evidence is refused rather than rewriting history. These receipts are local\n * debugging evidence and cannot authorize enforce mode or release.\n */\nexport class InMemoryRunReceiptReference {\n  readonly referenceOnly = true;\n  private readonly receipts = new Map<string, RunReceipt>();","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts#L14-L50","documentation":"After both timestamps parse successfully, validateRun requires Date.parse(completedAt) >= Date.parse(startedAt); a strictly earlier completion time throws. Equal timestamps are allowed. The ordering guarantee protects receipt semantics: a run cannot finish before it starts on the recording clock.","triggerScenarios":"startedAt and completedAt captured on different hosts whose clocks skew; completedAt copied from a previous run's record; async code that stamps completedAt before startedAt (misordered awaits); startedAt refreshed after a queue delay while completedAt stayed stale.","commonSituations":"Distributed runners where each machine stamps its own time; replaying recorded events out of order; timestamps taken from file mtimes or log lines written at different pipeline stages.","solutions":["Capture startedAt immediately before launching the command and completedAt immediately after it exits, on the same host","If clocks may skew, reconcile offsets (NTP/PTP or a shared authority) before recording","Pre-validate Date.parse(completedAt) >= Date.parse(startedAt) and fail fast locally with context"],"exampleFix":"// before: timestamps captured in different code paths\nconst startedAt = queueEnqueueTime;   // earlier, from another host\nconst completedAt = new Date().toISOString();\n\n// after: both stamped around execution on one clock\nconst startedAt = new Date().toISOString();\nconst completedAt = new Date().toISOString();\nif (Date.parse(completedAt) < Date.parse(startedAt)) throw new Error('clock moved backwards');","handlingStrategy":"validation","validationCode":"function assertRunInterval(run: { startedAt: string; completedAt: string }): void {\n  const started = Date.parse(run.startedAt);\n  const completed = Date.parse(run.completedAt);\n  if (!(Number.isFinite(started) && Number.isFinite(completed) && completed >= started)) {\n    throw new RangeError(`completedAt ${run.completedAt} precedes startedAt ${run.startedAt}`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  ledger.recordRun(run);\n} catch (error) {\n  if (error instanceof Error && error.message === 'run completedAt precedes startedAt') {\n    // skew detected: re-stamp both from local clock preserving true duration\n    const duration = measuredDurationMs;\n    const end = Date.now();\n    run.completedAt = new Date(end).toISOString();\n    run.startedAt = new Date(end - duration).toISOString();\n    ledger.recordRun(run);\n  } else throw error;\n}","preventionTips":["Stamp startedAt just before launching the command and completedAt just after it exits, on one host","Keep machines time-synchronized (NTP) when evidence is assembled across hosts","Assert completedAt >= startedAt locally before submission"],"tags":["validation","timestamp","clock-skew","run-receipt"],"backgroundTag":"timestamp-order-violation","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}