{"record":{"id":"bb047328bfb73a9a","repo":"affaan-m/ECC","slug":"skill-id-is-required","errorCode":null,"errorMessage":"skill_id is required","messagePattern":"skill_id is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/skill-evolution/tracker.js","lineNumber":58,"sourceCode":"  }\n\n  return numericValue;\n}\n\nfunction normalizeExecutionRecord(input, options = {}) {\n  if (!input || typeof input !== 'object' || Array.isArray(input)) {\n    throw new Error('skill execution payload must be an object');\n  }\n\n  const skillId = input.skill_id || input.skillId;\n  const skillVersion = input.skill_version || input.skillVersion;\n  const taskDescription = input.task_description || input.task_attempted || input.taskAttempted;\n  const outcome = input.outcome;\n  const recordedAt = input.recorded_at || options.now || new Date().toISOString();\n  const userFeedback = input.user_feedback || input.userFeedback || null;\n\n  if (typeof skillId !== 'string' || skillId.trim().length === 0) {\n    throw new Error('skill_id is required');\n  }\n\n  if (typeof skillVersion !== 'string' || skillVersion.trim().length === 0) {\n    throw new Error('skill_version is required');\n  }\n\n  if (typeof taskDescription !== 'string' || taskDescription.trim().length === 0) {\n    throw new Error('task_description is required');\n  }\n\n  if (!VALID_OUTCOMES.has(outcome)) {\n    throw new Error('outcome must be one of success, failure, or partial');\n  }\n\n  if (userFeedback !== null && !VALID_FEEDBACK.has(userFeedback)) {\n    throw new Error('user_feedback must be accepted, corrected, rejected, or null');\n  }\n","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/skill-evolution/tracker.js#L40-L76","documentation":"Thrown by normalizeExecutionRecord when skill_id (or the camelCase alias skillId) is not a non-empty trimmed string. It is the first required-field check after the object guard.","triggerScenarios":"recordSkillRun({ skill_version, task_description, outcome }) with no skill_id; skill_id: '' or '   '; skill_id: 123 (number, not string).","commonSituations":"Recording a run before the skill id is resolved; field-name mismatch (e.g. sending `id` or `skill` instead of `skill_id`); trimming reveals whitespace-only value.","solutions":["Provide skill_id as a non-empty string (skillId alias also accepted).","Resolve the id from the skill's frontmatter `name` before recording.","Validate the field exists and trims to non-empty."],"exampleFix":"// before\nrecordSkillRun({ skill_version: '1.0.0', task_description: 'x', outcome: 'success' });\n\n// after\nrecordSkillRun({ skill_id: skill.frontmatter.name, skill_version, task_description, outcome });","handlingStrategy":"validation","validationCode":"function requireSkillId(input) {\n  const id = input.skill_id || input.skillId;\n  if (typeof id !== 'string' || id.trim().length === 0) {\n    throw new TypeError('skill_id missing');\n  }\n  return id;\n}","typeGuard":"function hasSkillId(input) {\n  const id = input?.skill_id || input?.skillId;\n  return typeof id === 'string' && id.trim().length > 0;\n}","tryCatchPattern":"try {\n  recordSkillRun(input);\n} catch (err) {\n  if (/skill_id is required/.test(err.message)) { input.skill_id = fallbackId; recordSkillRun(input); }\n  else throw err;\n}","preventionTips":["Derive skill_id from the skill's frontmatter `name` field.","Validate the payload in a shared normalize function before recording.","Reject telemetry for skills that have no stable id."],"tags":["tracker","required-field","validation"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}