{"record":{"id":"4519e427d912f10d","repo":"affaan-m/ECC","slug":"canonical-session-snapshot-requires-workers-inde-4519e4","errorCode":null,"errorMessage":"Canonical session snapshot requires workers[${index}].artifacts to be an object","messagePattern":"Canonical session snapshot requires workers\\[(.+?)\\]\\.artifacts to be an object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/session-adapters/canonical-session.js","lineNumber":231,"sourceCode":"    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\n  if (!isObject(snapshot.aggregates)) {\n    throw new Error('Canonical session snapshot requires aggregates to be an object');\n  }\n\n  ensureInteger(snapshot.aggregates.workerCount, 'aggregates.workerCount');\n  if (snapshot.aggregates.workerCount !== snapshot.workers.length) {\n    throw new Error('Canonical session snapshot requires aggregates.workerCount to match workers.length');\n  }\n\n  if (!isObject(snapshot.aggregates.states)) {\n    throw new Error('Canonical session snapshot requires aggregates.states to be an object');\n  }\n\n  if (!isObject(snapshot.aggregates.healths)) {\n    throw new Error('Canonical session snapshot requires aggregates.healths to be an object');","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/session-adapters/canonical-session.js#L213-L249","documentation":"worker.artifacts is missing or not a plain object. The schema only requires artifacts to be an object; its inner keys are adapter-specific (e.g. dmux uses statusFile/taskFile/handoffFile, claude-history uses sessionFile/context). This is the last per-worker guard before the loop closes.","triggerScenarios":"Custom adapter omits the artifacts block entirely. Persisted recording edited to delete artifacts. Adapter sets artifacts to null when no files are present.","commonSituations":"New adapter for a session source that exposes no on-disk artifacts, where the author skips the field instead of emitting an empty object. Test fixtures that build a minimal worker.","solutions":["Always emit artifacts as an object, even if empty: artifacts: {} or artifacts: { sessionFile: null }.","Follow the established adapters: dmux maps worker.files.*, claude-history maps sessionFile:session.sessionPath.","When no artifacts exist, use artifacts: {} rather than omitting or nulling the field.","Pin a unit test that validates the full normalize* output for each adapter."],"exampleFix":"// before\nconst worker = { id:'w1', label:'w1', state:'recorded', health:'healthy', runtime:{...}, intent:{...}, outputs:{...} };\n\n// after\nconst worker = {\n  id:'w1', label:'w1', state:'recorded', health:'healthy', runtime:{...}, intent:{...}, outputs:{...},\n  artifacts: { sessionFile: session.sessionPath, context: metadata.context || null }\n};","handlingStrategy":"type-guard","validationCode":"function ensureWorkerArtifacts(worker) {\n  if (!worker.artifacts || typeof worker.artifacts !== 'object') {\n    worker.artifacts = {};\n  }\n  return worker;\n}","typeGuard":"function hasValidArtifacts(w) {\n  return w.artifacts !== null && typeof w.artifacts === 'object' && !Array.isArray(w.artifacts);\n}","tryCatchPattern":"try { validateCanonicalSnapshot(snapshot); }\ncatch (err) {\n  if (err.message.includes('.artifacts to be an object')) {\n    snapshot.workers.forEach(w => { if (!hasValidArtifacts(w)) ensureWorkerArtifacts(w); });\n    validateCanonicalSnapshot(snapshot);\n  } else throw err;\n}","preventionTips":["Emit at least artifacts:{} even when no files are tracked.","Keep per-adapter artifact keys documented and consistent.","Add a fixture test exercising the full validate path."],"tags":["canonical-session","schema-validation","artifacts"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}