docs: 14-language title/description/SEO cascade from executive brief#2521
Conversation
…ive brief Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/fcd4df51-75f2-47b6-afdf-4502111c8ac8 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🏷️ Automatic Labeling SummaryThis PR has been automatically labeled based on the files changed and PR metadata. Applied Labels: documentation,size-m Label Categories
For more information, see |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/3b3de874-c99e-4c34-ae9a-968e912baa71 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
There was a problem hiding this comment.
Pull request overview
Implements cascade chain step #2 of the per-language SEO cascade documented in Article-Generation.md: a new pure-function module reads executive-brief_<lang>.md H1 + BLUF and overrides the per-type agent's article.<lang>.md front-matter title: / description: at HTML-render time, with banned-phrase parity to analysis-gate.ts § checkExecutiveBrief.
Changes:
- New
scripts/render-lib/aggregator/seo/localized-brief.ts(pure functionsextractLocalizedBriefSeo+isBannedLocalizedBriefH1) plus barrel re-exports. article-merge.tsaccepts optionallocalizedBriefMarkdown+subfolderand overlays brief-derived fields onto the localized front-matter;render-articles.tsreadsexecutive-brief_<lang>.mdnext toarticle.mdand forwards it.- New
tests/localized-brief-seo.test.ts(18 cases) + extendedtests/article-merge.test.ts(+5 cases);Article-Generation.mdcascade section rewritten to match the runtime.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/render-lib/aggregator/seo/localized-brief.ts | New pure module: brief H1+BLUF → localized SEO candidates, with banned-phrase rejection. |
| scripts/render-lib/article-merge.ts | Optional localizedBriefMarkdown/subfolder inputs; overlays brief-derived title/description over FM. |
| scripts/render-articles.ts | renderOne() reads executive-brief_<lang>.md next to article.md and forwards to merger. |
| scripts/render-lib/aggregator/index.ts | Re-exports the new SEO module from aggregator barrel. |
| scripts/render-lib/index.ts | Re-exports the new SEO module from top-level render-lib barrel. |
| tests/localized-brief-seo.test.ts | New 18-case pure-function test suite for cascade chain step #2. |
| tests/article-merge.test.ts | +5 cases covering brief overrides, banned/empty-brief fall-through, missing input. |
| Article-Generation.md | Expanded cascade documentation with 4-step precedence chain table and 8-surface SEO derivation table. |
Improve Article-Generation.md — 14-language title, description and SEO metadata
Why
PR #2521 documented a 4-step per-language SEO cascade where
executive-brief_<lang>.mdH1 + BLUF supplies the localized<title>/<meta description>. Code review found chain step #2 was aspirational — the localized brief existed as a validator-enforced artifact (scripts/validate-executive-brief-translations.ts) but no render-lib code consumed it at HTML-render time. This continuation makes the documented cascade real, with a bounded-context implementation, tight tests, and doc/runtime parity.What changed
scripts/render-lib/aggregator/seo/localized-brief.ts(~180 lines) implementing cascade chain step Sync styles.css from Hack23/homepage #2:extractLocalizedBriefSeo({ briefMarkdown, subfolder })returns{ title, description }candidates fromexecutive-brief_<lang>.mdreadFirstHeading,cleanArticleTitle,readBlufParagraph,readFirstParagraph,truncateToSentenceBoundary) so per-language extraction shares the English-side cleaning logic verbatim (no duplicated tradecraft)isBannedLocalizedBriefH1()enforces parity withscripts/agentic/analysis-gate.ts § checkExecutiveBrief— rejectsREPLACE THIS H1,Executive Brief Template,AI_MUST_REPLACE,AI-generated political intelligence, and bare boilerplateExecutive Brief(case- and emoji-tolerant)aggregator/index.tsand the top-levelrender-lib/index.tsbarrelscripts/render-lib/article-merge.ts:MergeLocalizedInputgains optionallocalizedBriefMarkdown+subfolderfieldsarticle.<lang>.mdfront-mattertitle:/description:(independent fields)scripts/render-articles.tsrenderOne()readsexecutive-brief_<lang>.mdnext toarticle.mdand forwards it to the mergertests/localized-brief-seo.test.ts(18 cases): banned H1 variants, bare boilerplate, empty BLUF, first-paragraph fallback, sentence-boundary truncation, slug collision, all-empty inputstests/article-merge.test.ts(+5 cases): brief overrides FM, banned brief preserves FM title but localizes description, empty brief preserves both FM fields, omitted/emptylocalizedBriefMarkdownis a no-opArticle-Generation.mdcascade rules now point at the new bounded-context module so the document and runtime agree; per-type workflow rule downgraded fromMUST translate from briefto defence-in-depthruntime merger does it anywaylocalized-brief-seo,article-merge,render-lib*,chrome-seo-features,jsonld-and-markdown-pipeline,structured-data,sitemap-generation,hreflang-validation,validate-article);tsc -p tsconfig.scripts.json --noEmitclean; ESLint clean on all touched filesFiles
Stacks cleanly on PR #2518 (executive-brief tradecraft) and PR #2519 (per-type workflow ownership): the cascade documented in PR #2521 now has a runtime implementation that defends against translator stubs leaking into SERP titles.