{"record":{"id":"325afb87d121d80f","repo":"affaan-m/ECC","slug":"skillpath-is-required","errorCode":null,"errorMessage":"skillPath is required","messagePattern":"skillPath is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/skill-evolution/provenance.js","lineNumber":31,"sourceCode":"  IMPORTED: 'imported',\n  UNKNOWN: 'unknown',\n});\n\nfunction resolveRepoRoot(repoRoot) {\n  if (repoRoot) {\n    return path.resolve(repoRoot);\n  }\n\n  return path.resolve(__dirname, '..', '..', '..');\n}\n\nfunction resolveHomeDir(homeDir) {\n  return homeDir ? path.resolve(homeDir) : os.homedir();\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 isWithinRoot(targetPath, rootPath) {\n  const relativePath = path.relative(rootPath, targetPath);\n  return relativePath === '' || (\n    !relativePath.startsWith('..')\n    && !path.isAbsolute(relativePath)\n  );\n}\n","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/skill-evolution/provenance.js#L13-L49","documentation":"Thrown by normalizeSkillDir in the provenance module (reached via readProvenance, writeProvenance, classifySkillPath, requiresProvenance, getProvenancePath) when skillPath is falsy or not a string. The function accepts either a skill directory or a path ending in SKILL.md (it strips the basename).","triggerScenarios":"Calling readProvenance(undefined), readProvenance(null), readProvenance(123), readProvenance(''), or passing a skill object/config instead of its filesystem path.","commonSituations":"Loop variable undefined (e.g. mapping over an array with a missing entry); destructuring a skill record and passing the whole object instead of record.path; calling provenance APIs before resolving the skill dir.","solutions":["Pass the skill directory path as a non-empty string.","Either the directory or the SKILL.md file path works — basename 'SKILL.md' is auto-stripped.","Guard the caller: if (!skillPath) return early."],"exampleFix":"// before\nreadProvenance(skill?.path?.dir); // undefined when skill is null\n\n// after\nif (skill?.path?.dir) readProvenance(skill.path.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}\nreadProvenance(skillPath);","typeGuard":"function isNonEmptyString(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  readProvenance(skillPath);\n} catch (err) {\n  if (/skillPath is required/.test(err.message)) return null;\n  throw err;\n}","preventionTips":["Resolve skill directories once at the boundary and pass them down as strings.","Distinguish the skill record from its path — never pass the record object.","Add a type check in shared helpers that wrap provenance APIs."],"tags":["provenance","path","validation"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}