{"record":{"id":"2a759830be7dcae6","repo":"affaan-m/ECC","slug":"canonical-session-snapshot-requires-workers-inde-2a7598","errorCode":null,"errorMessage":"Canonical session snapshot requires workers[${index}].intent to be an object","messagePattern":"Canonical session snapshot requires workers\\[(.+?)\\]\\.intent to be an object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/session-adapters/canonical-session.js","lineNumber":216,"sourceCode":"\n    ensureString(worker.id, `workers[${index}].id`);\n    ensureString(worker.label, `workers[${index}].label`);\n    ensureString(worker.state, `workers[${index}].state`);\n    ensureString(worker.health, `workers[${index}].health`);\n    ensureOptionalString(worker.branch, `workers[${index}].branch`);\n    ensureOptionalString(worker.worktree, `workers[${index}].worktree`);\n\n    if (!isObject(worker.runtime)) {\n      throw new Error(`Canonical session snapshot requires workers[${index}].runtime to be an object`);\n    }\n\n    ensureString(worker.runtime.kind, `workers[${index}].runtime.kind`);\n    ensureOptionalString(worker.runtime.command, `workers[${index}].runtime.command`);\n    ensureBoolean(worker.runtime.active, `workers[${index}].runtime.active`);\n    ensureBoolean(worker.runtime.dead, `workers[${index}].runtime.dead`);\n\n    if (!isObject(worker.intent)) {\n      throw new Error(`Canonical session snapshot requires workers[${index}].intent to be an object`);\n    }\n\n    ensureStringAllowEmpty(worker.intent.objective, `workers[${index}].intent.objective`);\n    ensureArrayOfStrings(worker.intent.seedPaths, `workers[${index}].intent.seedPaths`);\n\n    if (!isObject(worker.outputs)) {\n      throw new Error(`Canonical session snapshot requires workers[${index}].outputs to be an object`);\n    }\n\n    ensureArrayOfStrings(worker.outputs.summary, `workers[${index}].outputs.summary`);\n    ensureArrayOfStrings(worker.outputs.validation, `workers[${index}].outputs.validation`);\n    ensureArrayOfStrings(worker.outputs.remainingRisks, `workers[${index}].outputs.remainingRisks`);\n\n    if (!isObject(worker.artifacts)) {\n      throw new Error(`Canonical session snapshot requires workers[${index}].artifacts to be an object`);\n    }\n  });\n","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/session-adapters/canonical-session.js#L198-L234","documentation":"worker.intent is missing or not a plain object. The schema requires worker.intent to hold objective (string, may be empty) and seedPaths (array of strings). This fires before those field checks, so any non-object intent is rejected outright.","triggerScenarios":"Custom adapter constructs a worker but omits intent. Reading a persisted recording written by an adapter version that put the objective at worker.objective instead of worker.intent.objective. Hand-editing a JSON recording and removing the intent block.","commonSituations":"Adapting a new session source where the task objective is optional and the adapter author skips intent. Migrating snapshots between schema versions without remapping fields. Test fixtures that build minimal worker objects.","solutions":["Always set worker.intent = { objective: <string>, seedPaths: <string[]> } even when empty: { objective: '', seedPaths: [] }.","Follow normalizeClaudeHistorySession's pattern: objective from metadata title or inProgress[0], seedPaths via parseContextSeedPaths(metadata.context).","If loading old recordings, run a migration that nests legacy worker.objective under worker.intent.objective.","Add a fixture-based unit test that builds the minimal worker and runs validateCanonicalSnapshot to catch the omission early."],"exampleFix":"// before\nconst worker = { id:'w1', label:'w1', state:'recorded', health:'healthy', runtime:{...}, outputs:{...}, artifacts:{...} };\n\n// after\nconst worker = {\n  id:'w1', label:'w1', state:'recorded', health:'healthy', runtime:{...},\n  intent: { objective: metadata.title || '', seedPaths: parseContextSeedPaths(metadata.context) },\n  outputs:{...}, artifacts:{...}\n};","handlingStrategy":"type-guard","validationCode":"function ensureWorkerIntent(worker) {\n  if (!worker.intent || typeof worker.intent !== 'object') {\n    worker.intent = { objective: '', seedPaths: [] };\n  }\n  return worker;\n}","typeGuard":"function hasValidIntent(w) {\n  const i = w.intent;\n  return i !== null && typeof i === 'object' && !Array.isArray(i)\n    && typeof i.objective === 'string' && Array.isArray(i.seedPaths);\n}","tryCatchPattern":"try { validateCanonicalSnapshot(snapshot); }\ncatch (err) {\n  if (err.message.includes('.intent to be an object')) {\n    snapshot.workers.forEach(w => { if (!hasValidIntent(w)) ensureWorkerIntent(w); });\n    validateCanonicalSnapshot(snapshot);\n  } else throw err;\n}","preventionTips":["Always set intent:{objective:'',seedPaths:[]} as the default in worker builders.","For claude-history, derive intent via the metadata helpers in normalizeClaudeHistorySession.","Pin a schema test that asserts intent shape for every adapter."],"tags":["canonical-session","schema-validation","intent"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}