{"record":{"id":"cf24c370ca77f7df","repo":"affaan-m/ECC","slug":"replay-bad-declaration","errorCode":"replay.bad_declaration","errorMessage":"tool ${name} must declare an effect_class","messagePattern":"tool (.+?) must declare an effect_class","errorType":"error_code","errorClass":"ReplayError","httpStatus":null,"severity":"error","filePath":"scripts/lib/eval-harness/replay.js","lineNumber":102,"sourceCode":"}\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}`);\n  }\n  if (!store) {\n    throw new ReplayError('replay.no_store', 'a FixtureStore is required');\n  }\n  for (const [name, tool] of Object.entries(tools)) {\n    if (!envelope.EFFECT_CLASSES.includes(tool.effect_class)) {\n      throw new ReplayError('replay.bad_declaration', `tool ${name} must declare an effect_class`);\n    }\n    if (!['deterministic', 'nondeterministic'].includes(tool.determinism)) {\n      throw new ReplayError('replay.bad_declaration', `tool ${name} must declare determinism`);\n    }\n  }\n\n  const calls = [];\n  const emit = (entry) => {\n    calls.push(entry);\n    if (typeof options.onCall === 'function') {\n      options.onCall(entry);\n    }\n  };\n\n  return {\n    mode,\n    calls,\n    call(name, args = {}) {","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/eval-harness/replay.js#L84-L120","documentation":"createReplayer validates every entry in the tools map and throws ReplayError('replay.bad_declaration') when a tool's effect_class is missing or not one of envelope.EFFECT_CLASSES. Effect classes (SE0..SE3) drive the effect fence that decides whether a tool may run or be replayed, so an undeclared class makes the tool unusable and the whole replayer refuses to start.","triggerScenarios":"Passing a tool object without an effect_class property, with a typo ('se2', 'SE_2'), or with a custom class name not present in envelope.EFFECT_CLASSES — e.g. `{ name: { impl(args) {...}, determinism: 'deterministic' } }`.","commonSituations":"Adding a new tool and forgetting the effect_class field; renaming effect classes in a library upgrade so old declarations no longer match; hand-writing tool registries instead of using a shared declaration helper; copy-pasting tool defs from an older example that used a different field name.","solutions":["Add a valid effect_class from envelope.EFFECT_CLASSES to the tool declaration, e.g. { effect_class: 'SE2', determinism: 'deterministic', impl }.","Log the declared class for the offending tool (the error message names it) and compare against the allowed list from require('./envelope').EFFECT_CLASSES.","Fix case/typo issues — classes are exact strings like 'SE2'; 'se2' will not pass.","After a library upgrade, re-check all tool declarations against the new EFFECT_CLASSES list and update renamed classes."],"exampleFix":"// before\nconst tools = {\n  fetchWeather: { determinism: 'deterministic', impl: fetchWeather },\n};\n\n// after\nconst tools = {\n  fetchWeather: { effect_class: 'SE2', determinism: 'deterministic', impl: fetchWeather },\n};","handlingStrategy":"validation","validationCode":"const { EFFECT_CLASSES } = require('./envelope');\nfor (const [name, tool] of Object.entries(tools)) {\n  if (!EFFECT_CLASSES.includes(tool.effect_class)) {\n    throw new Error(`tool ${name} has invalid effect_class: ${tool.effect_class}`);\n  }\n}","typeGuard":"function declaresEffectClass(tool, EFFECT_CLASSES) {\n  return typeof tool === 'object' && tool !== null &&\n    EFFECT_CLASSES.includes(tool.effect_class);\n}","tryCatchPattern":"try {\n  return createReplayer(tools, { mode, store });\n} catch (e) {\n  if (e instanceof ReplayError && e.code === 'replay.bad_declaration' && e.message.includes('effect_class')) {\n    console.error('Fix tool declarations:', e.message);\n  }\n  throw e;\n}","preventionTips":["Create tools through a makeTool({ effect_class, determinism, impl }) factory that validates fields","Import EFFECT_CLASSES and validate declarations in a unit test for the tools registry","After upgrading the harness, diff EFFECT_CLASSES against declarations in use","Use exact string constants (e.g. const SE2 = 'SE2') instead of inline literals"],"tags":["configuration","validation","effect-class","declaration"],"backgroundTag":"schema-validation-failed","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"}