{"record":{"id":"334bf19cc7080e21","repo":"ruvnet/ruflo","slug":"executionid-run-executionid-already-records-dif","errorCode":null,"errorMessage":"executionId ${run.executionId} already records different evidence","messagePattern":"executionId (.+?) already records different evidence","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts","lineNumber":61,"sourceCode":" *\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>();\n  private readonly executionReceipts = new Map<string, string>();\n\n  constructor(private readonly now: () => number = Date.now) {}\n\n  recordRun(run: RunEvidence): RunReceipt {\n    validateRun(run);\n    const canonical = canonicalJson(run);\n    const receiptId = sha256(canonical);\n    const existingExecution = this.executionReceipts.get(run.executionId);\n    if (existingExecution !== undefined && existingExecution !== receiptId) {\n      throw new Error(`executionId ${run.executionId} already records different evidence`);\n    }\n    const existing = this.receipts.get(receiptId);\n    if (existing) return copy(existing);\n\n    const receipt: RunReceipt = {\n      ...copy(run),\n      receiptId,\n      recordedAt: new Date(this.now()).toISOString(),\n    };\n    this.receipts.set(receiptId, receipt);\n    this.executionReceipts.set(run.executionId, receiptId);\n    return copy(receipt);\n  }\n\n  get(receiptId: string): RunReceipt | undefined {\n    const receipt = this.receipts.get(receiptId);\n    return receipt ? copy(receipt) : undefined;\n  }","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts#L43-L79","documentation":"The ledger is content-addressed: receiptId = sha256(canonicalJson(run)), and one executionId may map to exactly one receipt. recordRun throws when the same executionId arrives with a different canonical form — even a one-millisecond difference in startedAt yields a new receiptId and trips the guard. Exact retries converge on the existing receipt; changed evidence is refused rather than rewriting history.","triggerScenarios":"Retrying recordRun after a transient failure while rebuilding the RunEvidence object (fresh timestamps each attempt); changing exitCode or digests for the 'same' execution; two workers recording different results under one executionId.","commonSituations":"Retry wrappers that regenerate timestamps instead of replaying the original payload; duplicate message delivery causing a real re-execution that reuses the executionId; executionIds seeded from counters that reset across process restarts.","solutions":["On retry/redelivery, replay the byte-identical RunEvidence — cache the first attempt's object and resend it","For a genuinely new attempt or re-run, mint a fresh executionId (crypto.randomUUID())","Treat the thrown conflict as 'already recorded' only after comparing the prior receipt's digests to confirm divergence vs. duplication"],"exampleFix":"// before: evidence rebuilt on every retry (new startedAt each time)\nasync function record(retry) {\n  const run = buildRunEvidence(); // fresh timestamps => different digest\n  await ledger.recordRun(run);\n}\n\n// after: build once, replay the identical object on retry\nconst run = buildRunEvidence();\nawait withRetry(() => ledger.recordRun(run));","handlingStrategy":"try-catch","validationCode":"const firstAttempt = new Map<string, RunEvidence>();\nfunction replayStableEvidence(run: RunEvidence): RunEvidence {\n  const prior = firstAttempt.get(run.executionId);\n  if (prior) return prior; // byte-identical retry keeps one receipt\n  firstAttempt.set(run.executionId, run);\n  return run;\n}","typeGuard":null,"tryCatchPattern":"try {\n  ledger.recordRun(run);\n} catch (error) {\n  if (error instanceof Error && error.message.includes('already records different evidence')) {\n    // same executionId with different evidence: either a duplicate re-run or a bug\n    const existing = ledger.getReceipt?.(run.executionId);\n    if (existing && existing.commandDigest === run.commandDigest) return existing; // benign duplicate\n    throw new Error(`executionId ${run.executionId} diverged between attempts; mint a new executionId`);\n  }\n  throw error;\n}","preventionTips":["Build the RunEvidence object once per attempt and replay it verbatim on retries","Mint a fresh executionId (crypto.randomUUID()) for every genuine re-execution","Do not seed executionIds from counters that reset across restarts"],"tags":["idempotency","content-addressing","run-receipt","conflict"],"backgroundTag":"idempotency-key-conflict","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"}