{"record":{"id":"2e4a02e317bf47f2","repo":"koala73/worldmonitor","slug":"pagepath-is-missing-its-recent-developments-section","errorCode":null,"errorMessage":"${pagePath} is missing its recent-developments section","messagePattern":"(.+?) is missing its recent-developments section","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/build-crawlable-corpus.mjs","lineNumber":3500,"sourceCode":"// Durable guard (#7615): the enrichment must be permanent, not a one-off\n// content pass. After rendering, every frozen developments row for this\n// country must be present in the page HTML — a silent drop (wrong slug, lost\n// prop, over-eager filter) fails the build instead of shipping a page whose\n// snapshot claims items the crawler cannot see.\nexport function assertCountryDevelopmentsRendered({\n  pagePath,\n  html,\n  developments,\n  countryCode = '',\n  countryName = '',\n}) {\n  const rawRows = developments && typeof developments === 'object' ? developments : null;\n  // Same publish rules as the renderer, so a withheld thin brief is not\n  // reported as dropped and a repaired heading is looked for as repaired.\n  const rows = rawRows ? normalizeFrozenDevelopments(rawRows, { countryCode, countryName }) : null;\n  if (!rows || !developmentsHasDatedItem(rows)) return;\n  if (!html.includes('data-country-developments')) {\n    throw new Error(`${pagePath} is missing its recent-developments section`);\n  }\n  for (const headline of rows.headlines || []) {\n    // Anchor-scoped: a bare URL substring passes when the URL merely appears\n    // in prose or another link. The headline must render as a link.\n    if (!html.includes(`href=\"${escapeHtml(headline.url)}\"`)) {\n      throw new Error(`${pagePath} dropped frozen headline ${headline.url}`);\n    }\n  }\n  if (rows.brief && typeof rows.brief.text === 'string' && rows.brief.text.trim()) {\n    // Anchor on first AND last content lines after stripping section titles,\n    // bullets, and emphasis markers. Every generated brief opens with the same\n    // boilerplate header, so the first line alone cannot catch a cross-country\n    // swap; markdown conversion means raw `**` and ISO titles never appear.\n    const contentLines = rows.brief.text.trim().split('\\n')\n      .map((line) => unwrapBriefEmphasisLine(line.trim()))\n      .filter(Boolean)\n      .filter((line) => !isBriefSectionHeader(line, { countryCode, countryName }))\n      .map((line) => line.replace(/^(?:[•\\-]\\s*|\\*\\s+)/, '').replace(/\\*\\*/g, ''));","sourceCodeStart":3482,"sourceCodeEnd":3518,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/build-crawlable-corpus.mjs#L3482-L3518","documentation":"During corpus verification (around line 3500), the frozen developments payload is normalized with the same publish rules as the renderer and, if the rows contain dated items, the generated HTML page must contain the data-country-developments marker. If dated developments exist but the marker is absent, the page silently dropped the section, so the build throws to prevent publishing an incomplete country page.","triggerScenarios":"Running the corpus build/verification when a country page's HTML lacks the data-country-developments attribute while the normalized developments contain at least one dated item (developmentsHasDatedItem(rows) is true). Happens when the page template omits the section, the renderer output was stripped, or a render condition hides the section.","commonSituations":"Template refactor removed or renamed the data-country-developments attribute; a conditional render guard (feature flag or thin-brief check) hides the section; frozen data was updated with new dated items but the page was not regenerated; hand-edited HTML dropped the section.","solutions":["Regenerate the country pages so the rendered HTML includes the recent-developments section with the data-country-developments attribute","Restore the data-country-developments attribute in the page template's developments section markup","Check the render condition/feature flag hiding the section for countries with dated items and fix it","Confirm the frozen developments data and the page HTML come from the same build (stale HTML vs fresh data)"],"exampleFix":"// before (template)\n<section class=\"developments\">...</section>\n// after\n<section class=\"developments\" data-country-developments>...</section>","handlingStrategy":"validation","validationCode":"if (rowsHasDatedItems(developments) && !html.includes('data-country-developments')) {\n  throw new Error(`${pagePath}: dated developments exist but section marker missing`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  assertFrozenDevelopmentsRendered({ pagePath, html, developments, countryCode, countryName });\n} catch (err) {\n  if (err.message.includes('missing its recent-developments section')) {\n    console.error(`Template for ${pagePath} dropped the developments section — check data-country-developments marker`);\n  }\n  throw err;\n}","preventionTips":["Keep the data-country-developments attribute in the shared section component so template refactors cannot drop it","Always regenerate pages after updating frozen developments data (never ship stale HTML)","Add a snapshot test asserting dated developments always render the marker","Make any conditional render around the developments section mirror developmentsHasDatedItem"],"tags":["build","html","verification"],"backgroundTag":"internal-invariant-violation","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"}