{"record":{"id":"1b4bfb1928b36ff0","repo":"affaan-m/ECC","slug":"canonical-session-snapshot-requires-aggregates-to","errorCode":null,"errorMessage":"Canonical session snapshot requires aggregates to be an object","messagePattern":"Canonical session snapshot requires aggregates to be an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/session-adapters/canonical-session.js","lineNumber":236,"sourceCode":"\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');\n  }\n\n  for (const [state, count] of Object.entries(snapshot.aggregates.states)) {\n    ensureString(state, 'aggregates.states key');\n    ensureInteger(count, `aggregates.states.${state}`);","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/session-adapters/canonical-session.js#L218-L254","documentation":"validateCanonicalSnapshot requires snapshot.aggregates to be a plain object holding roll-up counts derived from workers. This error fires when aggregates is missing, null, or a non-object, meaning the snapshot was built without its summary section. Aggregates drive dashboards so the validator treats them as mandatory.","triggerScenarios":"Passing a snapshot without the aggregates block to persistCanonicalSnapshot or a normalize* adapter; constructing a snapshot manually with only workers.","commonSituations":"Older snapshot format without aggregates loaded into current code; a transform that spread workers but dropped aggregates; partial JSON written by a crashed writer.","solutions":["Add an aggregates object with workerCount (integer equal to workers.length), states {}, and healths {} to the snapshot.","Compute aggregates from the workers array before persisting (counts by state and health).","Regenerate the snapshot via the current adapters, which build aggregates automatically.","Validate snapshots right after construction to catch missing aggregates."],"exampleFix":"// before\nconst snapshot = { workers };\npersistCanonicalSnapshot(snapshot);\n// after\nconst snapshot = {\n  workers,\n  aggregates: {\n    workerCount: workers.length,\n    states: {},\n    healths: {}\n  }\n};\npersistCanonicalSnapshot(snapshot);","handlingStrategy":"validation","validationCode":"if (!snapshot.aggregates || typeof snapshot.aggregates !== 'object' || Array.isArray(snapshot.aggregates)) throw new Error('snapshot.aggregates must be an object');","typeGuard":"function hasAggregates(s) { return !!s && typeof s.aggregates === 'object' && s.aggregates !== null && !Array.isArray(s.aggregates); }","tryCatchPattern":"try { persistCanonicalSnapshot(snapshot); } catch (err) { if (String(err.message).includes('aggregates to be an object')) { console.error('Snapshot missing aggregates:', err.message); /* rebuild aggregates from workers */ } else { throw err; } }","preventionTips":["Build snapshots through a single constructor that always emits aggregates.","Compute aggregates from workers with a shared deriveAggregates() helper.","Never assemble snapshots field-by-field in ad hoc scripts.","Validate snapshots immediately after construction."],"tags":["schema-validation","session-snapshot"],"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"}