ruvnet/ruflo · error · Error

build evidence belongs to a different source state

Error message

build evidence belongs to a different source state

What it means

When run.buildEvidence is provided, validateRun requires buildEvidence.sourceStateId to equal run.sourceState.sourceStateId. The ledger refuses to attach build evidence produced against a different repository snapshot than the run executed in, because the receipt would then claim a reproducibility the run does not have.

Source

Thrown at v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts:37

}

function validateRun(run: RunEvidence): void {
  if (!run.executionId.trim() || !run.sessionId.trim() || !run.workloadId.trim()) {
    throw new Error('run execution, session, and workload identity are required');
  }
  if (!DIGEST.test(run.sourceState.sourceStateId)) throw new Error('run sourceStateId is invalid');
  if (!DIGEST.test(run.commandDigest) || !DIGEST.test(run.evidenceDigest)) {
    throw new Error('run command and evidence digests must be canonical sha256 values');
  }
  if (!Number.isSafeInteger(run.exitCode)) throw new Error('run exitCode must be a safe integer');
  const started = timestamp(run.startedAt, 'startedAt');
  const completed = timestamp(run.completedAt, 'completedAt');
  if (completed < started) throw new Error('run completedAt precedes startedAt');
  if (
    run.buildEvidence !== undefined
    && run.buildEvidence.sourceStateId !== run.sourceState.sourceStateId
  ) {
    throw new Error('build evidence belongs to a different source state');
  }
}

/**
 * Unsigned, non-durable, content-addressed in-memory conformance ledger.
 *
 * Exact retries converge on one receipt. Reusing an execution ID with changed
 * evidence is refused rather than rewriting history. These receipts are local
 * debugging evidence and cannot authorize enforce mode or release.
 */
export class InMemoryRunReceiptReference {
  readonly referenceOnly = true;
  private readonly receipts = new Map<string, RunReceipt>();
  private readonly executionReceipts = new Map<string, string>();

  constructor(private readonly now: () => number = Date.now) {}

  recordRun(run: RunEvidence): RunReceipt {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Recompute build evidence from the exact source state the run executes against and copy that snapshot's sourceStateId
  2. Omit run.buildEvidence when no same-state build evidence exists — the field is optional
  3. Pre-check buildEvidence.sourceStateId === run.sourceState.sourceStateId before recordRun

Example fix

// before: cached build evidence from an earlier snapshot
receipt.recordRun({ ..., sourceState, buildEvidence: cachedBuild });

// after: only attach evidence bound to the run's snapshot
receipt.recordRun({ ..., sourceState, buildEvidence: cachedBuild.sourceStateId === sourceState.sourceStateId ? cachedBuild : undefined });
Defensive patterns

Strategy: validation

Validate before calling

function buildEvidenceMatches(build: { sourceStateId: string } | undefined, run: { sourceState: { sourceStateId: string } }): boolean {
  return build === undefined || build.sourceStateId === run.sourceState.sourceStateId;
}

Type guard

function isSameSourceStateBuild(build: unknown, sourceStateId: string): build is { sourceStateId: string } {
  if (build === undefined) return true;
  if (typeof build !== 'object' || build === null) return false;
  return (build as { sourceStateId?: unknown }).sourceStateId === sourceStateId;
}

Try / catch

try {
  ledger.recordRun(run);
} catch (error) {
  if (error instanceof Error && error.message === 'build evidence belongs to a different source state') {
    run.buildEvidence = undefined; // drop stale build evidence rather than recording a false claim
    ledger.recordRun(run);
  } else throw error;
}

Prevention

When it happens

Trigger: Reusing a cached BuildEvidence object after the worktree changed between build and run; building in one worktree or commit and running in another; a concurrent write shifting the source state between the build-evidence capture and the run capture.

Common situations: CI caching build evidence keyed only by branch name; multi-worktree setups where build and run check out different commits; long-lived local caches invalidated after a rebase or partial stash.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/2b8ede058888b9f5. Report an issue: GitHub.