{"record":{"id":"ea9a4f106f0eb1d7","repo":"santifer/career-ops","slug":"fact-check-failed-options-label-for-options","errorCode":null,"errorMessage":"Fact check failed${options.label ? ` for ${options.label}` : ''}: ${details.join('; ')}","messagePattern":"Fact check failed(.+?)` : ''\\}: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"verify-cv-facts.mjs","lineNumber":416,"sourceCode":"      .filter(phrase => stripMarkup(targetText).toLowerCase().includes(String(phrase).toLowerCase()));\n  return {\n    verdict: invented.length || unsupportedFacts.length || forbidden.length ? 'block' : warnings.length ? 'warn' : 'pass',\n    invented,\n    unsupportedFacts,\n    forbidden,\n    warnings,\n  };\n}\n\n/** Verify a document and throw when it contains a blocking unsupported claim. */\nexport function assertFacts(targetText, options = {}) {\n  const result = verifyFacts(targetText, options);\n  if (result.verdict === 'block') {\n    const details = [];\n    if (result.invented.length) details.push(`metric-like claims absent from sources: ${result.invented.join(', ')}`);\n    if (result.unsupportedFacts.length) details.push(`non-metric facts absent from sources: ${result.unsupportedFacts.map(({ kind, value }) => `${kind}=${value}`).join(', ')}`);\n    if (result.forbidden.length) details.push(`forbidden phrases found: ${result.forbidden.join(', ')}`);\n    throw new Error(`Fact check failed${options.label ? ` for ${options.label}` : ''}: ${details.join('; ')}`);\n  }\n  return result;\n}\n\n/** Parse the fact-validator command-line arguments. */\nfunction parseCliArgs(args) {\n  const sourcePaths = [];\n  let targetArg = '';\n  let configPath = DEFAULT_CONFIG;\n  let json = false;\n  for (let i = 0; i < args.length; i++) {\n    const arg = args[i];\n    if (arg === '--source' || arg === '--config') {\n      if (!args[i + 1]) throw new Error(`${arg} requires a path`);\n      if (arg === '--source') sourcePaths.push(args[++i]);\n      else configPath = args[++i];\n    } else if (arg === '--help' || arg === '-h') {\n      return { help: true };","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/verify-cv-facts.mjs#L398-L434","documentation":"assertFacts() is the throwing variant of verify-cv-facts.mjs: it re-runs verifyFacts() and, when the verdict is 'block', throws one aggregated error listing metric-like claims absent from sources, unsupported non-metric facts (kind=value for employers/titles/tools), and forbidden phrases found. This is the tool's no-fabrication gate doing its job — the generated document contains claims with no source backing.","triggerScenarios":"Calling assertFacts(generatedText, {sources, config, label}) where the text contains a metric like 'increased revenue by 70%' that appears in no --source file, an employer/title/tool claim not present in the sources, or a phrase listed in forbidden_phrases. Severity of unsupported entries decides block vs warn.","commonSituations":"AI-generated CV/cover-letter text that embellished a number beyond cv.md; tailoring that imported the JD's tool names as the candidate's own; a stale or missing source file (forgetting --source cv.md) so even true claims look unsupported; forbidden-phrase lists catching hype wording.","solutions":["Read the three detail segments in the message: they name each offending claim, so remove or correct those exact strings in the generated document","If a claim is genuinely true, add it to a primary source (cv.md / config) and pass that file via --source, then re-run","If a number is legitimately allowed, add it to allow_metrics in the fact-gate config instead of deleting it silently","Use the non-throwing verifyFacts() export when you want the verdict object and to decide yourself how to handle 'block'"],"exampleFix":"// before\nimport { assertFacts } from './verify-cv-facts.mjs';\nassertFacts(cvText, { sources, label: 'cv' }); // throws on block\n// after\nimport { verifyFacts } from './verify-cv-facts.mjs';\nconst r = verifyFacts(cvText, { sources, label: 'cv' });\nif (r.verdict === 'block') {\n  console.error('blocking claims:', r.invented, r.unsupportedFacts.map((f) => f.value));\n  process.exitCode = 1; // or strip the claims and regenerate\n}","handlingStrategy":"try-catch","validationCode":"// Non-throwing pre-check: get the verdict and decide yourself\nimport { verifyFacts } from './verify-cv-facts.mjs';\nconst r = verifyFacts(text, { sources, config });\nif (r.verdict === 'block') {\n  console.error('blocking claims:', [...r.invented, ...r.unsupportedFacts.map((f) => `${f.kind}=${f.value}`), ...r.forbidden]);\n}","typeGuard":"function isBlockedVerdict(v) {\n  return v != null && typeof v === 'object' && v.verdict === 'block';\n}","tryCatchPattern":"try {\n  assertFacts(text, { sources, config, label: 'cv' });\n} catch (err) {\n  if (String(err.message).startsWith('Fact check failed')) {\n    // parse the three detail segments, strip/correct those claims, regenerate, re-assert\n    const details = err.message.slice('Fact check failed'.length);\n    // ... handle, do not swallow: an unhandled block means fabricated content shipped\n    process.exitCode = 1;\n  } else {\n    throw err;\n  }\n}","preventionTips":["Generate content only from primary sources and re-assert after every edit","Add genuinely-true quantified claims to cv.md/config (or allow_metrics) instead of bypassing the gate","Never catch-and-ignore a block verdict — that is the no-fabrication rule failing open"],"tags":["fact-checking","validation","no-fabrication","cv-generation"],"backgroundTag":"content-validation-failed","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}