{"record":{"id":"0343b6cabe9ed824","repo":"affaan-m/ECC","slug":"tool-fixture-mismatch","errorCode":"tool.fixture_mismatch","errorMessage":"fixture ${key.slice(0, 16)} was recorded for different arguments","messagePattern":"fixture (.+?) was recorded for different arguments","errorType":"error_code","errorClass":"ReplayError","httpStatus":null,"severity":"error","filePath":"scripts/lib/eval-harness/replay.js","lineNumber":77,"sourceCode":"    };\n    fs.writeFileSync(this.pathFor(key), canonicalJson(record) + '\\n', 'utf8');\n    return record;\n  }\n\n  get(tool, args) {\n    const key = this.key(tool, args);\n    const filePath = this.pathFor(key);\n    if (!fs.existsSync(filePath)) {\n      throw new ReplayError('tool.fixture_missing', `no fixture for ${tool} (${key.slice(0, 16)})`, { tool, key });\n    }\n    let record;\n    try {\n      record = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n    } catch (_error) {\n      throw new ReplayError('tool.fixture_corrupt', `fixture ${key.slice(0, 16)} is not valid JSON`, { tool, key });\n    }\n    if (record.tool !== tool || record.args_hash !== hashValue(args)) {\n      throw new ReplayError('tool.fixture_mismatch', `fixture ${key.slice(0, 16)} was recorded for different arguments`, { tool, key });\n    }\n    if (record.response_hash !== hashValue(record.response)) {\n      throw new ReplayError('tool.fixture_mismatch', `fixture ${key.slice(0, 16)} response hash does not match its content`, { tool, key });\n    }\n    return record;\n  }\n}\n\n/**\n * tools: { name: { effect_class, determinism: 'deterministic'|'nondeterministic', impl(args) } }\n * options: { mode: 'record'|'replay', store: FixtureStore, maxEffectClass: 'SE2', onCall(entry) }\n */\nfunction createReplayer(tools, options = {}) {\n  const mode = options.mode || 'replay';\n  const store = options.store;\n  const maxRank = envelope.effectRank(options.maxEffectClass || 'SE2');\n  if (!['record', 'replay'].includes(mode)) {\n    throw new ReplayError('replay.bad_mode', `mode must be record or replay, got ${mode}`);","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/eval-harness/replay.js#L59-L95","documentation":"FixtureStore.get throws ReplayError('tool.fixture_mismatch') when the fixture found by the content key declares a different tool name or an args_hash that does not match hashValue(args) for the current call. The lookup key is derived from (tool, args), so reaching this check usually means the fixture was mutated or the key derivation changed. It prevents replaying a response recorded for different inputs.","triggerScenarios":"store.get(tool, args) loads a fixture whose `record.tool !== tool` or `record.args_hash !== hashValue(args)` — e.g. someone hand-edited the fixture, replaced a fixture file's contents with another call's record, or the canonical hashing of args changed between record and replay (library version change, key-order normalization difference).","commonSituations":"Copy-pasting or renaming fixture files manually; upgrading the eval-harness/canonical module so hashValue output changes; reusing one fixture directory across two eval suites with colliding tool names; args containing non-deterministic serialization (e.g. object key order, Date objects) that hash differently on replay.","solutions":["Delete the mismatched fixture and re-record it in mode 'record' with the exact current tool name and arguments.","Compare record.args_hash with store.key/hashValue(args) to see whether args or the hash function drifted; if hashValue changed after an upgrade, re-record the whole fixture set.","Ensure args passed to replayer.call are structurally identical to record-time args (same keys, same values, no added optional fields) — canonical hashing is sensitive to content.","Never hand-edit fixture files; treat the fixtures directory as generated output managed solely by record mode."],"exampleFix":"// before: fixture edited to reuse another call's record, args_hash no longer matches\n// fixtures/ab12....json copied over cd34....json\n\n// after: re-record the fixture for the current args\nconst r = createReplayer(tools, { mode: 'record', store });\nr.call('fetchWeather', { city: 'Paris' });","handlingStrategy":"validation","validationCode":"const expectedKey = store.key(tool, args);\nif (!fs.existsSync(store.pathFor(expectedKey))) reRecord(tool, args);","typeGuard":"function argsMatchRecord(record, tool, args, hashValue) {\n  return record.tool === tool && record.args_hash === hashValue(args);\n}","tryCatchPattern":"try {\n  return replayer.call(tool, args);\n} catch (e) {\n  if (e instanceof ReplayError && e.code === 'tool.fixture_mismatch' && e.message.includes('different arguments')) {\n    return reRecordAndReturn(tool, args);\n  }\n  throw e;\n}","preventionTips":["Pass structurally identical args at record and replay time (freeze or deep-clone a shared args object)","Never copy/rename fixture files manually — the filename is a content hash","Re-record the whole fixture set after upgrading the canonical/hash module","Keep args free of non-deterministic fields (Date.now, random ids) at the call boundary"],"tags":["fixture","hash-mismatch","integrity","replay"],"backgroundTag":"checksum-mismatch","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}