{"record":{"id":"053dcd7e58241849","repo":"koala73/worldmonitor","slug":"whywewinbody-must-not-repeat-whywewin","errorCode":null,"errorMessage":" whyWeWinBody must not repeat whyWeWin","messagePattern":" whyWeWinBody must not repeat whyWeWin","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/build-comparison-pages.mjs","lineNumber":468,"sourceCode":"  if (faqs.length < 8 || faqs.length > 12) {\n    throw new Error(page.slug + ' FAQ count must be 8-12, got ' + faqs.length);\n  }\n  const faqNames = new Set();\n  for (const [question] of faqs) {\n    const key = String(question).trim().toLowerCase();\n    if (faqNames.has(key)) {\n      throw new Error(page.slug + ' duplicate FAQ question: ' + question);\n    }\n    faqNames.add(key);\n  }\n  const merged = { ...page, ...narrative, faqs };\n  delete merged.extraFaqs;\n  const whyBody = (merged.whyWeWinBody ?? []).join(' ').replace(/\\s+/g, ' ').trim();\n  if (!whyBody) {\n    throw new Error(page.slug + ' is missing whyWeWinBody');\n  }\n  if (whyBody === merged.whyWeWin.replace(/\\s+/g, ' ').trim()) {\n    throw new Error(page.slug + ' whyWeWinBody must not repeat whyWeWin');\n  }\n  if (merged.heading && !(merged.headingProse?.length || merged.competitorProfiles?.length)) {\n    throw new Error(page.slug + ' H2 \"' + merged.heading + '\" has no following prose');\n  }\n  if (!merged.evaluationHeading || !merged.evaluationProse?.length) {\n    throw new Error(page.slug + ' is missing the evaluation section');\n  }\n  if (!merged.switchHeading || !merged.switchProse?.length) {\n    throw new Error(page.slug + ' is missing the switch-trigger section');\n  }\n  if (!merged.methodologyProse?.length) {\n    throw new Error(page.slug + ' is missing methodology prose');\n  }\n  return merged;\n}\n\nexport const COMPARISON_PAGES = COMPARISON_PAGE_SEEDS.map(applyComparisonNarrative);\n","sourceCodeStart":450,"sourceCodeEnd":486,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/build-comparison-pages.mjs#L450-L486","documentation":"applyComparisonNarrative rejects narratives whose whyWeWinBody, after whitespace normalization, is identical to the whyWeWin summary text. The body must be expanded prose distinct from the headline claim, so verbatim duplication is treated as an authoring mistake.","triggerScenarios":"Running the build when String(whyWeWinBody joined) === String(whyWeWin) after both are collapsed to single spaces and trimmed — typically because whyWeWinBody was set to [whyWeWinText].","commonSituations":"Copy-pasting the whyWeWin sentence into whyWeWinBody as a placeholder; a generator/template that emits the same string into both fields.","solutions":["Replace whyWeWinBody content with distinct expanded paragraphs, not the whyWeWin sentence.","If the text was duplicated as a placeholder, author real supporting prose.","Compare both strings normalized the same way the script does before committing.","Keep whyWeWin as a short claim and whyWeWinBody as 1-3 supporting paragraphs."],"exampleFix":"// before\nwhyWeWin: 'One map for every feed.',\nwhyWeWinBody: ['One map for every feed.']\n// after\nwhyWeWin: 'One map for every feed.',\nwhyWeWinBody: ['WorldMonitor overlays maritime, aviation and conflict data so analysts stop switching tabs.']","handlingStrategy":"validation","validationCode":"const norm = (s) => String(s).replace(/\\s+/g, ' ').trim();\nif (norm((narrative.whyWeWinBody ?? []).join(' ')) === norm(narrative.whyWeWin ?? '')) throw new Error('whyWeWinBody repeats whyWeWin');","typeGuard":"const bodyDistinct = (n) => norm((n.whyWeWinBody ?? []).join(' ')) !== norm(n.whyWeWin ?? '');","tryCatchPattern":"try {\n  page = applyComparisonNarrative(page);\n} catch (e) {\n  if (String(e.message).includes('must not repeat whyWeWin')) {\n    console.error('Write distinct body prose for:', e.message);\n  } else throw e;\n}","preventionTips":["Never initialize whyWeWinBody with the whyWeWin text; start from the expanded outline.","Review diffs that touch whyWeWin fields for verbatim copies.","Add a test asserting body length exceeds the headline text length."],"tags":["build-time","content-validation","duplicates"],"backgroundTag":"schema-validation-failed","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"}