{"record":{"id":"174004b82c9a446f","repo":"koala73/worldmonitor","slug":"formatcrawlableintelbrief-requires-a-country-name","errorCode":null,"errorMessage":"formatCrawlableIntelBrief requires a country name","messagePattern":"formatCrawlableIntelBrief requires a country name","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/build-crawlable-corpus.mjs","lineNumber":3269,"sourceCode":"      .trim();\n    if (next === current) break;\n    current = next;\n  }\n  return current;\n}\n\nfunction applyCrawlableBriefEmphasis(escaped) {\n  return escaped.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>').replace(/\\*\\*/g, '');\n}\n\n// Frozen intel briefs are markdown-ish LLM text. Country pages are prerendered\n// HTML for crawlers, so convert emphasis and promote the five section titles\n// rather than injecting the string into a <p>. Always rewrite the \"means for\"\n// title from the page's country name: stored briefs still contain ISO codes\n// from the TIER1-only prompt fallback (#7738).\nexport function formatCrawlableIntelBrief(text, countryName) {\n  const name = String(countryName || '').trim();\n  if (!name) throw new Error('formatCrawlableIntelBrief requires a country name');\n  const out = [];\n  let listOpen = false;\n  const closeList = () => {\n    if (listOpen) {\n      out.push('          </ul>');\n      listOpen = false;\n    }\n  };\n  const openList = () => {\n    if (!listOpen) {\n      out.push('          <ul>');\n      listOpen = true;\n    }\n  };\n\n  for (const rawLine of String(text || '').split('\\n')) {\n    const trimmed = unwrapBriefEmphasisLine(rawLine.trim());\n    if (!trimmed) {","sourceCodeStart":3251,"sourceCodeEnd":3287,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/build-crawlable-corpus.mjs#L3251-L3287","documentation":"formatCrawlableIntelBrief(text, countryName) converts a stored markdown-ish LLM intel brief into prerendered HTML for country pages. It requires a non-empty trimmed countryName because it rewrites the brief's 'means for' section title using the page's country name — stored briefs may still contain ISO codes from the TIER1-only prompt fallback (issue #7738). When countryName is missing, empty, or whitespace-only the function throws rather than emitting HTML with a blank or wrong title.","triggerScenarios":"Calling formatCrawlableIntelBrief(briefText, countryName) with countryName undefined, null, '', or any value whose String() coercion trims to empty — typically during country-page generation when the country record failed to load or a refactored field name passes undefined.","commonSituations":"Generating crawlable country pages from cached/stored data where the display-name field is absent or empty for some records; a refactor renamed the data key so undefined is passed; iterating a partial country list; calling the formatter standalone (e.g. in a test) with only the text argument.","solutions":["Pass the resolved country name as the second argument, e.g. formatCrawlableIntelBrief(brief.text, country.displayName).","Guard before calling: if the country record has no non-blank name, skip and log instead of formatting.","If upstream data can legitimately lack a name, decide on an explicit fallback (e.g. ISO code) upstream — never pass an empty string to satisfy the check.","Grep all call sites of formatCrawlableIntelBrief after refactors to ensure none pass a renamed or missing field."],"exampleFix":"// before\nconst html = formatCrawlableIntelBrief(brief.text, row.country_nm);\n// after\nconst name = country?.displayName ?? row.name;\nif (!name || !name.trim()) throw new Error(`no country name for ${row.code}`);\nconst html = formatCrawlableIntelBrief(brief.text, name);","handlingStrategy":"type-guard","validationCode":"if (!countryName || !String(countryName).trim()) {\n  throw new Error(`formatCrawlableIntelBrief: missing countryName for brief`);\n}","typeGuard":"const hasCountryName = (v) => typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":"let html;\ntry {\n  html = formatCrawlableIntelBrief(brief.text, country.displayName);\n} catch (err) {\n  if (err.message === 'formatCrawlableIntelBrief requires a country name') {\n    console.warn(`Skipping brief for ${country?.code}: missing display name`);\n    return null;\n  }\n  throw err;\n}","preventionTips":["Pass the country record's canonical displayName field, not a loosely named column.","Validate country records (non-blank name) before the page-generation loop.","Grep all formatCrawlableIntelBrief call sites after renaming data fields.","Treat an empty country name upstream as a data bug instead of defaulting to an empty string."],"tags":["missing-argument","build-time","html-generation"],"backgroundTag":"missing-required-argument","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}