{"record":{"id":"ab988770e7b61d99","repo":"affaan-m/ECC","slug":"skillpath-is-required-ab9887","errorCode":null,"errorMessage":"skillPath is required","messagePattern":"skillPath is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/skill-evolution/versioning.js","lineNumber":18,"sourceCode":"'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\n\nconst { appendFile, ensureDir } = require('../utils');\n\nconst VERSION_DIRECTORY_NAME = '.versions';\nconst EVOLUTION_DIRECTORY_NAME = '.evolution';\nconst EVOLUTION_LOG_TYPES = Object.freeze([\n  'observations',\n  'inspections',\n  'amendments',\n]);\n\nfunction normalizeSkillDir(skillPath) {\n  if (!skillPath || typeof skillPath !== 'string') {\n    throw new Error('skillPath is required');\n  }\n\n  const resolvedPath = path.resolve(skillPath);\n  if (path.basename(resolvedPath) === 'SKILL.md') {\n    return path.dirname(resolvedPath);\n  }\n\n  return resolvedPath;\n}\n\nfunction getSkillFilePath(skillPath) {\n  return path.join(normalizeSkillDir(skillPath), 'SKILL.md');\n}\n\nfunction ensureSkillExists(skillPath) {\n  const skillFilePath = getSkillFilePath(skillPath);\n  if (!fs.existsSync(skillFilePath)) {\n    throw new Error(`Skill file not found: ${skillFilePath}`);","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/skill-evolution/versioning.js#L1-L36","documentation":"Thrown by normalizeSkillDir in the versioning module (reached via getSkillFilePath and the version/amendment log writers) when skillPath is falsy or not a string. Identical contract to provenance.normalizeSkillDir: accepts a directory or a path ending in SKILL.md.","triggerScenarios":"Calling versioning.getSkillFilePath(undefined); versioning.appendObservation(null, ...); passing a skill config object instead of its path.","commonSituations":"Undefined loop/destructure variable; calling versioning APIs before resolving the skill directory; passing the SKILL.md contents instead of the path.","solutions":["Pass the skill directory path (or the SKILL.md file path) as a non-empty string.","Guard callers for undefined before invoking.","Resolve the path once upstream and reuse it."],"exampleFix":"// before\nversioning.getSkillFilePath(skill?.dir);\n\n// after\nif (skill?.dir) versioning.getSkillFilePath(skill.dir);","handlingStrategy":"type-guard","validationCode":"if (typeof skillPath !== 'string' || skillPath.trim().length === 0) {\n  throw new TypeError('skillPath must be a non-empty string');\n}\nversioning.getSkillFilePath(skillPath);","typeGuard":"function isNonEmptyString(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  versioning.getSkillFilePath(skillPath);\n} catch (err) {\n  if (/skillPath is required/.test(err.message)) return null;\n  throw err;\n}","preventionTips":["Resolve the skill directory once and pass it as a string to all versioning calls.","Never substitute the skill record/config object for its path.","Wrap versioning APIs in a shared helper that type-checks the path."],"tags":["versioning","path","validation"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}