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
- Recompute build evidence from the exact source state the run executes against and copy that snapshot's sourceStateId
- Omit run.buildEvidence when no same-state build evidence exists — the field is optional
- 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
- Cache build evidence keyed by sourceStateId, not by branch or commit alone
- Capture build evidence and run evidence against the same worktree snapshot
- Treat a mismatch as a reproducibility red flag: investigate before dropping the field
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
- ${label} must be non-empty
- ${label} must be a canonical sha256 digest
- build evidence path is not a file or symlink: ${path}
- duplicate declared build input
- duplicate declared toolchain
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/2b8ede058888b9f5.
Report an issue: GitHub.