{"record":{"id":"fd3a6645377af067","repo":"santifer/career-ops","slug":"invalid-reportnum-entryreport-use-the-numeri","errorCode":null,"errorMessage":"invalid reportNum \"${entryReport}\" (use the numeric report number)","messagePattern":"invalid reportNum \"(.+?)\" \\(use the numeric report number\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"generate-pdf.mjs","lineNumber":1271,"sourceCode":"  // single render is: it is the anchor readStyleTokens() and the cv.md read\n  // already use, so one profile.yml supplies every setting.\n  const cvSectionOrder = readCvSectionOrder(resolve(workspaceRoot, 'config', 'profile.yml'));\n\n  for (let i = 0; i < manifest.length; i++) {\n    const spec = manifest[i];\n    try {\n      if (!spec || typeof spec.input !== 'string' || typeof spec.output !== 'string') {\n        throw new Error('each entry needs a string \"input\" and \"output\"');\n      }\n\n      const entryFormat = (spec.format || globals.format).toLowerCase();\n      if (!validFormats.includes(entryFormat)) {\n        throw new Error(`invalid format \"${entryFormat}\" (use: ${validFormats.join(', ')})`);\n      }\n\n      const entryReport = (spec.reportNum ?? '').toString().trim();\n      if (entryReport && !/^\\d+$/.test(entryReport)) {\n        throw new Error(`invalid reportNum \"${entryReport}\" (use the numeric report number)`);\n      }\n\n      // Resolve manifest-supplied input/output relative to the manifest's own\n      // directory, not process.cwd(), so a manifest renders identically wherever\n      // the batch is launched from. Absolute paths in the manifest still win\n      // (resolve() ignores the base when the tail is absolute).\n      const entryInput = resolve(manifestDir, spec.input);\n      const entryOutput = resolve(manifestDir, spec.output);\n\n      // Path-containment guards (realpath-based): keep the read and write inside\n      // the tracker workspace even through a symlinked ancestor. A batch\n      // manifest that escapes the workspace is malformed/tampered and is\n      // recorded as a per-entry failure rather than read or written.\n      assertInsideWorkspace(entryInput, 'input');\n      if (!isWorkspaceOutputPath(entryOutput, workspaceRoot)) {\n        throw new Error(`output escapes the tracker workspace: ${entryOutput}`);\n      }\n","sourceCodeStart":1253,"sourceCodeEnd":1289,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/generate-pdf.mjs#L1253-L1289","documentation":"The optional per-entry reportNum must be all digits after trimming (/^\\d+$/). It exists to tag rendered PDFs with their tracker report number, so decorated forms are rejected: 'R-064', 'report 64', '#64' all fail; '064' and '64' pass. Empty string / omitted is fine.","triggerScenarios":"A manifest generator writes report references as 'report-064' or '#064' from UI-facing strings; someone pastes the report column including its markdown link syntax; negative or zero-prefixed-with-letter ids from an external tracker export.","commonSituations":"Glue code between an ATS/dashboard and the batch renderer that formats ids for display; manifests assembled by hand from tracker notes.","solutions":["Use the bare numeric report number: \"reportNum\": \"064\".","Strip decorations in the generator: `String(ref).replace(/[^0-9]/g, '')` before writing the manifest.","If the entry has no associated report, omit reportNum entirely rather than writing a placeholder."],"exampleFix":"// before\n{ \"input\": \"x.html\", \"output\": \"x.pdf\", \"reportNum\": \"R-064\" }\n\n// after\n{ \"input\": \"x.html\", \"output\": \"x.pdf\", \"reportNum\": \"064\" }","handlingStrategy":"validation","validationCode":"const bad = manifest\n  .filter(e => e.reportNum !== undefined && e.reportNum !== '' && !/^\\d+$/.test(String(e.reportNum).trim()))\n  .map(e => JSON.stringify(e.reportNum));\nif (bad.length) { console.error(`reportNum must be bare digits, got: ${bad.join(', ')}`); process.exit(1); }","typeGuard":"/** @param {unknown} v */\nfunction isValidReportNum(v) {\n  if (v === undefined || v === null || v === '') return true; // optional\n  return /^\\d+$/.test(String(v).trim());\n}","tryCatchPattern":null,"preventionTips":["Normalize report ids in generators: String(ref).replace(/[^0-9]/g, '') before writing the manifest.","Copy report numbers from the tracker's # column, not from decorated UI strings.","Omit reportNum when an entry has no tracked report — never use a placeholder."],"tags":["batch","manifest","identifier","validation"],"backgroundTag":"invalid-identifier-format","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}