{"record":{"id":"72845cc1a8e5e5c3","repo":"ruvnet/ruflo","slug":"label-must-be-an-iso-timestamp","errorCode":null,"errorMessage":"${label} must be an ISO timestamp","messagePattern":"(.+?) must be an ISO timestamp","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts","lineNumber":17,"sourceCode":"import { createHash } from 'node:crypto';\nimport type { RunEvidence, RunReceipt } from './contract.js';\nimport { canonicalJson } from './repository-state.js';\n\nconst DIGEST = /^sha256:[0-9a-f]{64}$/;\n\nfunction sha256(value: string): string {\n  return `sha256:${createHash('sha256').update(value).digest('hex')}`;\n}\n\nfunction copy<T>(value: T): T {\n  return structuredClone(value);\n}\n\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","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts#L1-L35","documentation":"In InMemoryRunReceiptReference.recordRun, the timestamp() helper validates run.startedAt and run.completedAt with Date.parse and throws `${label} must be an ISO timestamp` when the result is not finite. The labels embedded in the message are 'startedAt' and 'completedAt'. Receipt ordering and identity depend on these values, so only parseable ISO 8601 strings are accepted.","triggerScenarios":"recordRun with startedAt: 'yesterday', completedAt: '12/31/2026 10:00', '', '2026-31-12T00:00:00Z', a locale-formatted log timestamp, a raw epoch number (1767225600000 instead of an ISO string), or a Date object serialized by a non-ISO toString path.","commonSituations":"Reusing timestamps captured by other tooling (MM/DD/YYYY or 'Jan 5, 2026' forms); passing Date.now() output or Date objects directly; strings assembled from log lines whose format drifts across locales/timezones.","solutions":["Generate both timestamps with new Date().toISOString() at capture time","Pre-check Number.isFinite(Date.parse(value)) for both fields before recordRun","When integrating epoch-millisecond sources, convert first: new Date(ms).toISOString()"],"exampleFix":"// before\nreceipt.recordRun({ ..., startedAt: String(Date.now() - 60000), completedAt: String(Date.now()) });\n\n// after\nreceipt.recordRun({ ..., startedAt: new Date(Date.now() - 60000).toISOString(), completedAt: new Date().toISOString() });","handlingStrategy":"validation","validationCode":"function assertIsoTimestamp(value: string, label: string): void {\n  if (!Number.isFinite(Date.parse(value))) {\n    throw new TypeError(`${label} is not an ISO timestamp: ${JSON.stringify(value)}`);\n  }\n}\nassertIsoTimestamp(run.startedAt, 'startedAt');\nassertIsoTimestamp(run.completedAt, 'completedAt');","typeGuard":"function isIsoTimestamp(value: unknown): value is string {\n  return typeof value === 'string' && Number.isFinite(Date.parse(value));\n}","tryCatchPattern":"try {\n  ledger.recordRun(run);\n} catch (error) {\n  if (error instanceof Error && error.message.endsWith('must be an ISO timestamp')) {\n    // re-stamp both timestamps from one clock and retry once\n    run.startedAt = new Date(startedMs).toISOString();\n    run.completedAt = new Date(completedMs).toISOString();\n    ledger.recordRun(run);\n  } else throw error;\n}","preventionTips":["Generate all timestamps with new Date().toISOString() at capture time","Never pass Date objects, Date.now() numbers, or locale-formatted strings into RunEvidence","Validate both timestamp fields at the boundary where RunEvidence is constructed"],"tags":["validation","timestamp","iso-8601","run-receipt"],"backgroundTag":"invalid-timestamp-format","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}