{"record":{"id":"91fc7310eb540f98","repo":"santifer/career-ops","slug":"reportnum-must-be-a-numeric-report-number","errorCode":null,"errorMessage":"reportNum must be a numeric report number","messagePattern":"reportNum must be a numeric report number","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"application-artifacts.mjs","lineNumber":32,"sourceCode":"import { fileURLToPath } from 'url';\n\nconst DEFAULT_OUTPUT_ROOT = resolve('output');\nconst DECISIONS = new Set(['reuse', 'reuse-with-edits', 'regenerate']);\n\n/** Convert a user-facing label into a safe, readable path segment. */\nexport function slugifySegment(value, fallback = 'application') {\n  const slug = String(value ?? '')\n    .trim()\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/^-+|-+$/g, '');\n  return slug || fallback;\n}\n\n/** Return all stable paths belonging to one application artifact bundle. */\nexport function applicationArtifactPaths({ reportNum, company, role, version = 1, root = DEFAULT_OUTPUT_ROOT }) {\n  if (!/^\\d+$/.test(String(reportNum ?? ''))) {\n    throw new Error('reportNum must be a numeric report number');\n  }\n  if (!/^\\d+$/.test(String(version ?? '')) || Number(version) < 1) {\n    throw new Error('version must be a positive integer');\n  }\n  const key = `${String(reportNum).padStart(3, '0')}-${slugifySegment(company)}-${slugifySegment(role, 'role')}`;\n  const applicationRoot = join(resolve(root), key);\n  const tailoredRoot = join(applicationRoot, 'cv', 'tailored', `v${String(version).padStart(3, '0')}`);\n  return {\n    key,\n    root: applicationRoot,\n    jd: {\n      current: join(applicationRoot, 'jd', 'current.md'),\n      previous: join(applicationRoot, 'jd', 'previous.md'),\n    },\n    cv: {\n      source: {\n        html: join(applicationRoot, 'cv', 'source', 'original.html'),\n        pdf: join(applicationRoot, 'cv', 'source', 'original.pdf'),","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/application-artifacts.mjs#L14-L50","documentation":"Validation in `applicationArtifactPaths`: the `reportNum` argument, coerced to string, must match `/^\\d+$/` (one or more digits only — no padding, sign, decimal, or suffix). Used to build the deterministic artifact directory key, so any non-numeric input would corrupt the on-disk layout and break lookups.","triggerScenarios":"Passing `reportNum: '042-report'`, `'42.0'`, `'-1'`, `'#42'`, `undefined`/`null` (coerces to 'undefined'), `NaN`, or a report filename slug instead of the bare number.","commonSituations":"Caller passes the report filename (`'042-acme-2024-01-01.md'`) instead of the number; passes a padded-with-dash slug; reads the value from a regex capture that included extra chars; off-by-one in a loop producing `undefined`; using `Number.parseInt` then stringifying `NaN`.","solutions":["Pass the bare integer report number as a number or numeric string: `42` or `'42'`.","Strip non-digits before calling: `reportNum.replace(/\\D/g, '')` (then validate it's non-empty).","If you have a report filename, extract the leading digits first: `(fn.match(/^\\D*(\\d+)/) || [])[1]`.","Guard upstream: `if (!/^\\d+$/.test(String(reportNum))) throw ...`.","Pad for display elsewhere (`padStart(3, '0')`) — the function pads internally; do not pre-pad with non-digit chars."],"exampleFix":"// before\napplicationArtifactPaths({ reportNum: '042-acme.md', company: 'acme', role: 'swe' }); // throws\n// after\napplicationArtifactPaths({ reportNum: 42, company: 'acme', role: 'swe' });","handlingStrategy":"validation","validationCode":"function numericReportNum(n) {\n  const s = String(n ?? '');\n  if (!/^\\d+$/.test(s)) throw new Error(`reportNum must be digits only, got: ${n}`);\n  return s;\n}","typeGuard":"function isNumericReportNum(n) {\n  return /^\\d+$/.test(String(n ?? ''));\n}","tryCatchPattern":null,"preventionTips":["Pass the bare integer (42 or '42'); don't include dashes/suffixes.","Extract the leading number from a filename before passing: `(fn.match(/(\\d+)/) || [])[1]`.","Strip non-digits if accepting user input: `String(n).replace(/\\D/g, '')`.","Guard in the caller with the same regex used internally."],"tags":["validation","path","application-artifacts","report-num"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}