{"record":{"id":"68f9b29500933c52","repo":"santifer/career-ops","slug":"no-config-cv-facts-json-forbidden-advisory-phrase-checks-did","errorCode":null,"errorMessage":"⚠️  No config/cv-facts.json — forbidden/advisory phrase checks did not run.","messagePattern":"⚠️  No config/cv-facts\\.json — forbidden/advisory phrase checks did not run\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"generate-pdf.mjs","lineNumber":1360,"sourceCode":"  // carries the numbers a reader acts on, but the CV path enforced the gate only\n  // as an instructed step in the mode prompts — so a programmatic caller (a\n  // bridge, a script, a batch run) rendered inflated metrics in silence. Gate the\n  // normalized HTML, which is the document that actually prints.\n  if (!skipFactCheck && cvMarkdown) {\n    // Imported lazily, INSIDE the guard. A static import is resolved at module\n    // load whether or not this branch runs, and the page-budget/batch suites copy\n    // generate-pdf.mjs alone into a temp workspace — a static import of a sibling\n    // that isn't copied made every one of those suites die with\n    // ERR_MODULE_NOT_FOUND before reaching the behaviour under test. Those\n    // fixtures also ship no cv.md, so this branch is never entered there. If the\n    // module is genuinely missing in a real workspace this throws and the render\n    // fails, which is the correct direction to fail for a fact gate.\n    const { assertFacts } = await import('./verify-cv-facts.mjs');\n    const factCheck = assertFacts(html, { label: basename(inputPath) });\n    // Ahead of the verdict, because it qualifies it: with no config the phrase\n    // lists are empty, so a \"passed\" below covers metrics and facts only.\n    if (factCheck.configMissing) {\n      console.warn('⚠️  No config/cv-facts.json — forbidden/advisory phrase checks did not run.');\n    }\n    if (factCheck.verdict === 'warn') {\n      console.warn(`⚠️  CV fact check warning: ${basename(inputPath)}`);\n      for (const phrase of factCheck.warnings) console.warn(`  - advisory phrase: ${phrase}`);\n    } else {\n      console.log('✅ Fact check passed');\n    }\n  }\n\n  return renderHtmlToPdf(html, outputPath, {\n    format,\n    baseDir: dirname(inputPath),\n    reportNum,\n    inputPath,\n    maxPages,\n    strictPages,\n    styleTokens: readStyleTokens(resolve(workspaceRoot, 'config', 'profile.yml')),\n  });","sourceCodeStart":1342,"sourceCodeEnd":1378,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/generate-pdf.mjs#L1342-L1378","documentation":"generate-pdf.mjs runs a fact gate via verify-cv-facts.mjs's assertFacts(), which checks the HTML for forbidden and advisory phrases using lists loaded from config/cv-facts.json. When that config file is missing, assertFacts reports configMissing and the phrase checks cannot run — this warning clarifies that any subsequent 'passed' verdict covers metrics/facts only, not phrase screening.","triggerScenarios":"PDF generation reaches the fact-check step, `await import('./verify-cv-facts.mjs')` succeeds, but assertFacts() returns factCheck.configMissing === true because config/cv-facts.json does not exist in the repo. Emitted at generate-pdf.mjs:1359 before the verdict output.","commonSituations":"Fresh clone or new project where config/cv-facts.json was never created (it is an optional gate); user deleted the config after too many advisory warnings; running generate-pdf.mjs outside the repo root so the relative path does not resolve.","solutions":["Create config/cv-facts.json with forbidden/advisory phrase lists (even empty lists) to make the gate explicit","Run from the repository root so the relative config path resolves","If phrase screening is intentionally not wanted, ignore this warning — the fact/metric checks still ran"],"exampleFix":"// before: file missing\n$ node generate-pdf.mjs output/cv.html\n⚠️  No config/cv-facts.json — forbidden/advisory phrase checks did not run.\n// after: minimal config\necho '{\"forbidden\":[],\"advisory\":[]}' > config/cv-facts.json\n$ node generate-pdf.mjs output/cv.html\n✅ Fact check passed","handlingStrategy":"validation","validationCode":"import { existsSync } from 'fs';\nif (!existsSync('config/cv-facts.json')) console.warn('cv-facts.json missing: forbidden/advisory phrase checks will be skipped');","typeGuard":"const factConfigExists = () => existsSync('config/cv-facts.json') && (() => { try { JSON.parse(readFileSync('config/cv-facts.json','utf8')); return true; } catch { return false; } })();","tryCatchPattern":null,"preventionTips":["Create config/cv-facts.json (even with empty lists) so the fact gate is explicit","Run generate-pdf.mjs from the repo root so relative config paths resolve","Remember that a 'passed' verdict without the config only covers metrics/facts, not phrase screening"],"tags":["config","fact-check","cv","pdf"],"backgroundTag":"config-file-not-found","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}