Skip to content

Fix 2026-04-18 weekly-review economic context violations and auto-emit Sankey for future review articles#1837

Merged
pethers merged 6 commits intomainfrom
copilot/fix-economic-context-violations
Apr 19, 2026
Merged

Fix 2026-04-18 weekly-review economic context violations and auto-emit Sankey for future review articles#1837
pethers merged 6 commits intomainfrom
copilot/fix-economic-context-violations

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 19, 2026

  • Fix 2026-04-18 weekly-review commentary (132 → 215 words) and add Sankey section
  • Auto-emit Sankey for weekly-review/monthly-review in buildArticleVisualizationSections
  • Update workflow markdowns (Step 3d) with exact word counts + pre-PR grep gate
  • Reviewer feedback (4135671420) — DRY helpers, alwaysEmit, localized labels/titles
  • Reviewer feedback (4135688442) — docstrings, dead placeholder removed, sparse-week reachability
  • Reviewer feedback (4135734237):
    • Stale commentbuildDeepInspectionSections no longer bucketizes locally; replaced the misleading "Single-pass classification … merge fpm/eu … kept the counts local for classifiedTypes" comment block with an accurate 4-line pointer stating that Sankey bucketing is delegated to buildLegislativeSankeySection via SANKEY_DOC_TYPE_SPEC.
    • Weekly workflow doc_counteconomic-data.json has no .docs field (only dataPoints, commentary, charts, source), so the jq expression always returned 0 and could falsely mark a missing Sankey as acceptable. Swapped to find "analysis/daily/$ARTICLE_DATE/weekly-review/documents" -maxdepth 1 -name '*.json' | wc -l — the actual source of truth for weekly-review documents.
    • Monthly workflow doc_count — same fix under analysis/daily/$ARTICLE_DATE/monthly-review/documents.
  • Typecheck clean
  • 6 targeted vitest suites pass (deep-analysis-section, sankey-section, generate-news-enhanced 1–2, weekly-review, workflow-architecture)

Copilot AI linked an issue Apr 19, 2026 that may be closed by this pull request
@github-actions github-actions bot added the size-xs Extra small change (< 10 lines) label Apr 19, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🏷️ Automatic Labeling Summary

This PR has been automatically labeled based on the files changed and PR metadata.

Applied Labels: size-xs

Label Categories

  • 🗳️ Content: news, dashboard, visualization, intelligence
  • 💻 Technology: html-css, javascript, workflow, security
  • 📊 Data: cia-data, riksdag-data, data-pipeline, schema
  • 🌍 I18n: i18n, translation, rtl
  • 🔒 ISMS: isms, iso-27001, nist-csf, cis-controls
  • 🏗️ Infrastructure: ci-cd, deployment, performance, monitoring
  • 🔄 Quality: testing, accessibility, documentation, refactor
  • 🤖 AI: agent, skill, agentic-workflow

For more information, see .github/labeler.yml.

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

@github-actions github-actions bot added html-css HTML/CSS changes news News articles and content generation size-m Medium change (50-250 lines) labels Apr 19, 2026
Copilot AI changed the title [WIP] Fix economic context audit violations detected on 2026-04-19 Fix 2026-04-18 weekly-review economic context violations Apr 19, 2026
Copilot AI requested a review from pethers April 19, 2026 07:34
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

@pethers
Copy link
Copy Markdown
Member

pethers commented Apr 19, 2026

@copilot and make sure all future articles for all types also would generate expected content

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 19, 2026

@copilot and make sure all future articles for all types also would generate expected content

Extended the fix so future generated articles of every type satisfy the validator automatically (8531257):

  • scripts/generate-news-enhanced/generators.tsbuildArticleVisualizationSections now auto-emits an inline class="sankey-section" SVG via generateSankeySection whenever articleType is one of the D3-required types (weekly-review, monthly-review) and ≥2 distinct document-type flows exist. Non-review types (committee-reports, motions, etc.) are unaffected — verified by smoketest.
  • .github/workflows/news-weekly-review.md + news-monthly-review.md Step 3d now state the exact minimum word counts (150 / 200) and include a grep -l 'class="sankey-section"' pre-PR gate.
  • 175 existing vitest tests still pass; validate-economic-context --date 2026-04-18 is green.

@github-actions github-actions bot added documentation Documentation updates workflow GitHub Actions workflows ci-cd CI/CD pipeline changes refactor Code refactoring agentic-workflow Agentic workflow changes labels Apr 19, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

Copilot AI changed the title Fix 2026-04-18 weekly-review economic context violations Fix 2026-04-18 weekly-review economic context violations and auto-emit Sankey for future review articles Apr 19, 2026
@pethers pethers marked this pull request as ready for review April 19, 2026 08:06
Copilot AI review requested due to automatic review settings April 19, 2026 08:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Addresses Economic Data Contract violations for the first 2026-04-18 weekly review and updates the generator/workflow guidance so future weekly-review / monthly-review articles automatically include the required Sankey section.

Changes:

  • Expands economic-data.json commentary and syncs the updated text into the published weekly-review HTML.
  • Adds a Sankey section to the 2026-04-18 EN weekly review article.
  • Introduces a generator helper to auto-append a Sankey section for review article types and updates the weekly/monthly workflow instructions accordingly.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
scripts/generate-news-enhanced/generators.ts Adds review-type Sankey gating + helper to generate an inline Sankey TemplateSection from document buckets.
news/2026-04-18-weekly-review-en.html Updates economic dashboard summary and appends an inline Sankey section + fallback table.
analysis/daily/2026-04-18/weekly-review/economic-data.json Expands AI economic commentary to meet minimum word-count expectations.
.github/workflows/news-weekly-review.md Tightens Step 3d guidance (min words/numerics) and adds a pre-PR grep check for class="sankey-section".
.github/workflows/news-monthly-review.md Same as weekly-review workflow guidance, with monthly thresholds and check.

Comment on lines +424 to +447
/**
* Build a legislative-flow Sankey TemplateSection from a set of documents.
* Returns null when fewer than two non-trivial flows can be derived, so
* callers can decide whether to append the section.
*
* Shares the actor → document-type semantics used by the deep-inspection
* Sankey: government coalition initiates propositions / laws / gov.
* communications / EU positions / press releases; opposition initiates
* committee reports and motions; private sector / external actors carry
* external references and other document types.
*/
function buildLegislativeSankeySection(
docs: RawDocument[],
topic: string | null,
lang: Language,
): TemplateSection | null {
if (docs.length < 2) return null;

const buckets = new Map<string, RawDocument[]>();
for (const d of docs) {
const t = effectiveType(d);
let arr = buckets.get(t);
if (!arr) { arr = []; buckets.set(t, arr); }
arr.push(d);
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper duplicates the document bucketing + Sankey node/flow construction logic that already exists in buildDeepInspectionSections later in the file. Keeping two near-identical implementations increases the risk of future drift (e.g., new doc types or actor semantics only being updated in one place). Consider extracting the shared "docs → Sankey options" logic once and reusing it from both builders.

Copilot uses AI. Check for mistakes.
sankeyFlows.push({ source: 'pvt', target: 'other', value: otherDocs.length, label: `${otherDocs.length}` });
}

if (sankeyFlows.length < 2) return null;
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For weekly-review / monthly-review the validator requires a Sankey/flow diagram unconditionally (requiresD3: true), but this helper returns null when it derives fewer than 2 flows. That can reintroduce contract failures on weeks/months where all documents fall into a single type bucket (e.g., only propositions), because no sankey-section will be emitted at all. Consider always emitting a (possibly single-flow) Sankey for review article types, or emitting a minimal placeholder Sankey section when diversity is insufficient, rather than returning null here.

Suggested change
if (sankeyFlows.length < 2) return null;
if (sankeyFlows.length === 0) {
sankeyNodes.push({ id: 'review', label: 'Review Period', color: 'blue' });
sankeyNodes.push({ id: 'documents', label: 'Documents', color: 'purple' });
sankeyFlows.push({
source: 'review',
target: 'documents',
value: Math.max(docs.length, 1),
label: `${docs.length}`,
});
}

Copilot uses AI. Check for mistakes.
Comment on lines +473 to +483
if (propDocs.length > 0) {
sankeyNodes.push({ id: 'prop', label: 'Propositions', color: 'orange' });
sankeyFlows.push({ source: 'gov', target: 'prop', value: propDocs.length, label: `${propDocs.length}` });
}
if (betDocs.length > 0) {
sankeyNodes.push({ id: 'bet', label: 'Committee Reports', color: 'blue' });
sankeyFlows.push({ source: 'opp', target: 'bet', value: betDocs.length, label: `${betDocs.length}` });
}
if (motDocs.length > 0) {
sankeyNodes.push({ id: 'mot', label: 'Motions', color: 'yellow' });
sankeyFlows.push({ source: 'opp', target: 'mot', value: motDocs.length, label: `${motDocs.length}` });
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sankey node labels are hard-coded English strings (e.g. "Propositions", "Committee Reports", "Motions", etc.). Because buildArticleVisualizationSections is used for weekly/monthly review generation across 14 languages, these labels will leak English into non-English articles. Use the existing doc-type localization helper (localizeDocType via docTypeLabel(...)) for document-type nodes (and map the merged EU bucket to an existing doc-type like fpm), so the Sankey renders in the target language.

Copilot uses AI. Check for mistakes.
Comment on lines +516 to +517
title: topic ? `Legislative Flow — ${topic}` : 'Legislative Flow',
summary: `Flow of ${docs.length} parliamentary documents from initiating actors to document types`,
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generateSankeySection already has localized default section titles (SECTION_TITLES) per language, but this code always overrides title and summary with English strings. For non-English weekly/monthly review pages this forces an English heading/summary even when localized defaults are available. Consider either omitting title/summary for non-EN languages (letting the generator localize), or providing localized variants here.

Suggested change
title: topic ? `Legislative Flow — ${topic}` : 'Legislative Flow',
summary: `Flow of ${docs.length} parliamentary documents from initiating actors to document types`,
...(lang === 'en'
? {
title: topic ? `Legislative Flow — ${topic}` : 'Legislative Flow',
summary: `Flow of ${docs.length} parliamentary documents from initiating actors to document types`,
}
: {}),

Copilot uses AI. Check for mistakes.
@pethers
Copy link
Copy Markdown
Member

pethers commented Apr 19, 2026

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 19, 2026

@copilot apply changes based on the comments in this thread

Applied all four reviewer threads in a45c322:

  1. DRY — Extracted SANKEY_DOC_TYPE_SPEC + bucketDocsForSankey + buildLegislativeSankeyNodesAndFlows. buildDeepInspectionSections now delegates its ~60-line Sankey block to buildLegislativeSankeySection, so actor → doc-type semantics live in one place.
  2. Always emit for review types — Added BuildLegislativeSankeyOptions.alwaysEmit; the review call site passes alwaysEmit: true, so single-type weeks/months still ship class="sankey-section" (with a placeholder Review Period → Documents flow as last-ditch fallback). Deep-inspection keeps the legacy flows >= 2 guard.
  3. Localized doc-type labels — All Sankey node labels now go through docTypeLabel(key, lang, count). EU bucket merges raw fpm + eu and localizes via the existing fpm entry.
  4. Localized title/summary — English + topic-bearing Sankeys keep the explicit Legislative Flow — {topic} copy; non-English topic-less reviews omit the overrides so generateSankeySection's SECTION_TITLES[lang] renders the target language.

Typecheck clean, 256 existing vitest tests pass, validate-economic-context --date 2026-04-18 green, and the multi-language smoketest confirms sv/de/fr/en all get localized titles and doc-type labels.

@github-actions github-actions bot added the size-l Large change (250-1000 lines) label Apr 19, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Comment thread .github/workflows/news-weekly-review.md Outdated
Comment on lines +748 to +751
> **Sankey / flow diagram** (required for `weekly-review` and `monthly-review`): `scripts/generate-news-enhanced/generators.ts` now auto-appends a `class="sankey-section"` SVG via `buildArticleVisualizationSections` whenever ≥2 distinct document-type flows exist. The AI writer does not need to emit Sankey HTML directly — just confirm the generated HTML contains `class="sankey-section"` before opening the PR:
> ```bash
> grep -l 'class="sankey-section"' news/$ARTICLE_DATE-weekly-review-*.html || {
> echo "❌ Sankey section missing — the validator will block the PR"; exit 1; }
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow step says the generator appends a class="sankey-section" only when “≥2 distinct document-type flows exist”, but the generator is called with alwaysEmit: true for weekly-review and will emit even for single doc-type weeks (and can still skip everything if docs.length < 2 due to the early return in buildArticleVisualizationSections). Update this instruction to match the generator’s actual emission rules so the pre-PR gate is accurate.

Suggested change
> **Sankey / flow diagram** (required for `weekly-review` and `monthly-review`): `scripts/generate-news-enhanced/generators.ts` now auto-appends a `class="sankey-section"` SVG via `buildArticleVisualizationSections` whenever ≥2 distinct document-type flows exist. The AI writer does not need to emit Sankey HTML directly — just confirm the generated HTML contains `class="sankey-section"` before opening the PR:
> ```bash
> grep -l 'class="sankey-section"' news/$ARTICLE_DATE-weekly-review-*.html || {
> echo "❌ Sankey section missing — the validator will block the PR"; exit 1; }
> **Sankey / flow diagram** (required for `weekly-review` and `monthly-review` when the generator can emit it): for `weekly-review`, `scripts/generate-news-enhanced/generators.ts` calls `buildArticleVisualizationSections` with `alwaysEmit: true`, so `class="sankey-section"` may be auto-appended even when only **one** document type is present. However, `buildArticleVisualizationSections` can still return early and emit no Sankey section when fewer than **2 total documents** are available. The AI writer does not need to emit Sankey HTML directly — just verify the generated HTML contains `class="sankey-section"` when the article has enough source documents for the visualization builder to run:
> ```bash
> if grep -l 'class="sankey-section"' news/$ARTICLE_DATE-weekly-review-*.html; then
> echo "✅ Sankey section present";
> else
> echo "ℹ️ No sankey-section found. This is expected when the weekly review has fewer than 2 source documents, but otherwise the generated HTML should include it."
> fi

Copilot uses AI. Check for mistakes.
Comment on lines +757 to +760
> **Sankey / flow diagram** (required for `monthly-review`): `scripts/generate-news-enhanced/generators.ts` now auto-appends a `class="sankey-section"` SVG via `buildArticleVisualizationSections` whenever ≥2 distinct document-type flows exist. The AI writer does not need to emit Sankey HTML directly — just confirm the generated HTML contains `class="sankey-section"` before opening the PR:
> ```bash
> grep -l 'class="sankey-section"' news/$ARTICLE_DATE-monthly-review-*.html || {
> echo "❌ Sankey section missing — the validator will block the PR"; exit 1; }
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow step says the generator appends a class="sankey-section" only when “≥2 distinct document-type flows exist”, but the generator is called with alwaysEmit: true for monthly-review and will emit even for single doc-type months (and can still skip everything if docs.length < 2 due to the early return in buildArticleVisualizationSections). Update this instruction to match the generator’s actual emission rules so the pre-PR gate is accurate.

Suggested change
> **Sankey / flow diagram** (required for `monthly-review`): `scripts/generate-news-enhanced/generators.ts` now auto-appends a `class="sankey-section"` SVG via `buildArticleVisualizationSections` whenever ≥2 distinct document-type flows exist. The AI writer does not need to emit Sankey HTML directly — just confirm the generated HTML contains `class="sankey-section"` before opening the PR:
> ```bash
> grep -l 'class="sankey-section"' news/$ARTICLE_DATE-monthly-review-*.html || {
> echo "❌ Sankey section missing — the validator will block the PR"; exit 1; }
> **Sankey / flow diagram** (`monthly-review`): `scripts/generate-news-enhanced/generators.ts` calls `buildArticleVisualizationSections` with `alwaysEmit: true` for this article type, so the generated HTML should normally include a `class="sankey-section"` SVG even for single document-type months. However, `buildArticleVisualizationSections` can still return early and emit no visualization sections when there are fewer than 2 documents for the month. The AI writer does not need to emit Sankey HTML directly — just verify the output follows those generator rules before opening the PR:
> ```bash
> if grep -l 'class="sankey-section"' news/$ARTICLE_DATE-monthly-review-*.html; then
> echo "✅ Sankey section present";
> else
> doc_count=$(jq '.docs | length' data.json)
> if [ "$doc_count" -lt 2 ]; then
> echo "ℹ️ Sankey section not emitted because the month has fewer than 2 documents";
> else
> echo "❌ Sankey section missing — unexpected for monthly-review when at least 2 documents exist"; exit 1
> fi
> fi

Copilot uses AI. Check for mistakes.
Comment on lines +661 to +674
try {
// ── 2b. Legislative-flow Sankey for retrospective reviews ─────────────
// The economic-data contract marks `weekly-review` and `monthly-review`
// as `requiresD3: true`, so emit an inline SVG Sankey whenever the
// article belongs to one of those types and enough distinct flows
// exist. Deep-inspection has its own richer Sankey inside
// `buildDeepInspectionSections` so we skip it here to avoid duplicates.
if (
context?.articleType &&
REVIEW_ARTICLE_TYPES_REQUIRING_SANKEY.has(context.articleType)
) {
const sankeySection = buildLegislativeSankeySection(docs, topic, lang, { alwaysEmit: true });
if (sankeySection) sections.push(sankeySection);
}
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new weekly/monthly-review Sankey block is unreachable when docs.length < 2 because buildArticleVisualizationSections returns early before this REVIEW_ARTICLE_TYPES_REQUIRING_SANKEY check. If the economic-context contract requires a Sankey even for sparse weeks/months, consider restructuring the function so the Sankey/economic sections can still emit when docs.length >= 1 (while keeping SWOT/dashboard gated by higher thresholds).

Copilot uses AI. Check for mistakes.
Comment on lines +569 to +590
if (flows.length < 2) {
if (!opts.alwaysEmit) return null;
// Placeholder one-flow Sankey so review articles always ship a
// `class="sankey-section"` element. Uses the localized "documents"
// label shared with the deep-inspection generator.
if (flows.length === 0) {
const reviewLabel = ENRICHMENT_LABELS.reviewPeriod?.[lang]
?? ENRICHMENT_LABELS.reviewPeriod?.en
?? 'Review Period';
const documentsLabel = deepLabel('documents', lang);
nodes.push(
{ id: 'review', label: reviewLabel, color: 'blue' },
{ id: 'documents', label: documentsLabel, color: 'purple' },
);
flows.push({
source: 'review',
target: 'documents',
value: Math.max(docs.length, 1),
label: `${docs.length}`,
});
}
}
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alwaysEmit placeholder branch is effectively dead: bucketDocsForSankey() always puts documents into a known bucket (at minimum other), so buildLegislativeSankeyNodesAndFlows() will produce at least one flow whenever docs.length > 0. As a result flows.length === 0 never happens, ENRICHMENT_LABELS.reviewPeriod is unused, and the docstring about emitting a Review Period → Documents placeholder is misleading. Either remove this placeholder logic/label, or change the bucketing/flow rules so a true “no doc-type flows” state can occur.

Suggested change
if (flows.length < 2) {
if (!opts.alwaysEmit) return null;
// Placeholder one-flow Sankey so review articles always ship a
// `class="sankey-section"` element. Uses the localized "documents"
// label shared with the deep-inspection generator.
if (flows.length === 0) {
const reviewLabel = ENRICHMENT_LABELS.reviewPeriod?.[lang]
?? ENRICHMENT_LABELS.reviewPeriod?.en
?? 'Review Period';
const documentsLabel = deepLabel('documents', lang);
nodes.push(
{ id: 'review', label: reviewLabel, color: 'blue' },
{ id: 'documents', label: documentsLabel, color: 'purple' },
);
flows.push({
source: 'review',
target: 'documents',
value: Math.max(docs.length, 1),
label: `${docs.length}`,
});
}
}
if (flows.length < 2 && !opts.alwaysEmit) return null;

Copilot uses AI. Check for mistakes.
* - SWOT stakeholder analysis (always, when docs.length >= 2)
* - Chart.js dashboard with document type breakdown (when docs.length >= 3)
* - Legislative-flow Sankey (when `articleType` requires D3 in the economic
* data contract — `weekly-review`, `monthly-review` — and >=2 flows exist)
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function docstring says the Sankey is emitted for weekly-review/monthly-review only when “>=2 flows exist”, but the implementation calls buildLegislativeSankeySection(..., { alwaysEmit: true }) and will emit even with a single flow. Please align the comment/docs with the actual behavior (or adjust the logic if the 2-flow guard is intended here).

Suggested change
* data contract `weekly-review`, `monthly-review` and >=2 flows exist)
* data contract `weekly-review`, `monthly-review`; these article types
* may emit a minimal placeholder Sankey via `alwaysEmit` when fewer than
* two meaningful flows can be derived)

Copilot uses AI. Check for mistakes.
@pethers
Copy link
Copy Markdown
Member

pethers commented Apr 19, 2026

@copilot apply changes based on the comments in this thread

…holder, allow Sankey for sparse weeks

Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/5016ea4a-9656-41db-8936-c79f9bfa3fec

Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 19, 2026

@copilot apply changes based on the comments in this thread

Applied all 5 reviewer threads in a46b537:

  1. Dead placeholder removed (generators.ts:569-590) — bucketDocsForSankey always yields ≥1 flow (unknown raw types fall into other), so flows.length === 0 was unreachable. Simplified to if (flows.length < 2 && !opts.alwaysEmit) return null; per the review suggestion, and deleted the unused Review Period → Documents branch + ENRICHMENT_LABELS.reviewPeriod entry.

  2. Docstring accuracy (generators.ts:619) — buildLegislativeSankeySection, BuildLegislativeSankeyOptions, and buildArticleVisualizationSections docstrings now correctly state that review article types emit whenever docs.length >= 1 (no "2 flows" guard in that path).

  3. Sparse-week reachability (generators.ts:661-674) — restructured buildArticleVisualizationSections: replaced the blanket docs.length < 2 early-return with per-section gates (enoughForSwot = docs.length >= 2, enoughForDashboard = docs.length >= 3, Sankey + economic sections emit for docs.length >= 1). Empty weeks (docs.length === 0) still short-circuit.

  4. Weekly workflow (news-weekly-review.md:748-751) — Step 3d now describes the actual alwaysEmit: true rule: Sankey is auto-appended whenever the week has ≥1 document, and the grep gate only permits a missing Sankey when docs.length === 0.

  5. Monthly workflow (news-monthly-review.md:757-760) — same alignment for monthly-review.

Smoketest confirms docs=1 weekly-review now emits Sankey in en/sv/de, docs=0 returns empty sections cleanly, and committee-reports still has no Sankey. Typecheck clean, 268 existing vitest tests pass, validate-economic-context --date 2026-04-18 green.

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment on lines +2180 to +2189
// Single-pass classification: bucket docs by effectiveType() to avoid N×filter passes.
// EU docs use both 'fpm' and 'eu' raw types; effectiveType() preserves the raw value,
// so we merge both into the euDocs bucket below.
const buckets = new Map<string, RawDocument[]>();
for (const d of docs) {
const t = effectiveType(d);
let arr = buckets.get(t);
if (!arr) { arr = []; buckets.set(t, arr); }
arr.push(d);
}
const propDocs = buckets.get('prop') ?? [];
const betDocs = buckets.get('bet') ?? [];
const motDocs = buckets.get('mot') ?? [];
const skrDocs = buckets.get('skr') ?? [];
const sfsDocs = buckets.get('sfs') ?? [];
const euDocs = [...(buckets.get('fpm') ?? []), ...(buckets.get('eu') ?? [])];
const pressmDocs = buckets.get('pressm') ?? [];
const extDocs = buckets.get('ext') ?? [];
// classifiedTypes must mirror every bucket key consumed above (including both EU keys)
const classifiedTypes = new Set(['prop','bet','mot','skr','sfs','fpm','eu','pressm','ext']);
const otherDocs = [...buckets.entries()]
.filter(([k]) => !classifiedTypes.has(k))
.flatMap(([, v]) => v);
// Single-pass classification: bucket docs by effectiveType() to detect
// whether the deep-inspection Sankey is worth rendering. The actual
// Sankey nodes/flows are built by `buildLegislativeSankeySection`
// which re-buckets internally using the shared helper.
// (Previously this function duplicated the bucket + node/flow logic;
// that duplication is gone — kept the counts local for the
// `classifiedTypes` comment and future debugging only.)
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In buildDeepInspectionSections, the “Single-pass classification” comment block no longer matches the implementation: the bucket/merge logic was removed, but the comment still references merging fpm/eu and “kept the counts local for the classifiedTypes comment”, yet no such counts/classifiedTypes exist anymore. Please update or remove this stale comment so it reflects the current code path (Sankey bucketing now happens inside buildLegislativeSankeySection).

Copilot uses AI. Check for mistakes.
Comment on lines +748 to +759
> **Sankey / flow diagram** (required for `weekly-review`): `scripts/generate-news-enhanced/generators.ts` calls `buildArticleVisualizationSections` with `alwaysEmit: true` for this article type, so `class="sankey-section"` is auto-appended whenever the week has at least **one** document — even when every document collapses into a single doc-type bucket. The only case where no Sankey is emitted is an empty week (`docs.length === 0`); in that edge case the visualization builder returns an empty section list. The AI writer does not need to emit Sankey HTML directly — just verify the generated HTML contains `class="sankey-section"` before opening the PR:
> ```bash
> if grep -l 'class="sankey-section"' news/$ARTICLE_DATE-weekly-review-*.html; then
> echo "✅ Sankey section present"
> else
> doc_count=$(jq '[.docs // []] | first | length' analysis/daily/$ARTICLE_DATE/weekly-review/economic-data.json 2>/dev/null || echo 0)
> if [ "$doc_count" = "0" ]; then
> echo "ℹ️ Sankey section not emitted — the week has 0 documents (validator allows this)"
> else
> echo "❌ Sankey section missing — the validator will block the PR"; exit 1
> fi
> fi
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Step 3d Sankey verification snippet computes doc_count from analysis/daily/$ARTICLE_DATE/weekly-review/economic-data.json using .docs, but economic-data.json does not contain a docs field (it has dataPoints, commentary, charts, source, etc.). As written, this jq expression will always yield 0, so it can incorrectly “allow” a missing class="sankey-section" even when the week had documents. Please derive the document count from an actual source of truth (e.g., the number of JSON files under analysis/daily/$ARTICLE_DATE/weekly-review/documents/, or another manifest used by the weekly-review pipeline).

Copilot uses AI. Check for mistakes.
Comment on lines +757 to +769
> **Sankey / flow diagram** (required for `monthly-review`): `scripts/generate-news-enhanced/generators.ts` calls `buildArticleVisualizationSections` with `alwaysEmit: true` for this article type, so `class="sankey-section"` is auto-appended whenever the month has at least **one** document — even when every document collapses into a single doc-type bucket. The only case where no Sankey is emitted is an empty month (`docs.length === 0`); in that edge case the visualization builder returns an empty section list. The AI writer does not need to emit Sankey HTML directly — just verify the generated HTML contains `class="sankey-section"` before opening the PR:
> ```bash
> if grep -l 'class="sankey-section"' news/$ARTICLE_DATE-monthly-review-*.html; then
> echo "✅ Sankey section present"
> else
> doc_count=$(jq '[.docs // []] | first | length' analysis/daily/$ARTICLE_DATE/monthly-review/economic-data.json 2>/dev/null || echo 0)
> if [ "$doc_count" = "0" ]; then
> echo "ℹ️ Sankey section not emitted — the month has 0 documents (validator allows this)"
> else
> echo "❌ Sankey section missing — the validator will block the PR"; exit 1
> fi
> fi
> ```
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Step 3d Sankey verification snippet computes doc_count from analysis/daily/$ARTICLE_DATE/monthly-review/economic-data.json using .docs, but economic-data.json doesn’t include a docs field (it has dataPoints, commentary, charts, source, etc.). This makes the jq expression effectively always return 0, which can incorrectly treat a missing class="sankey-section" as acceptable. Please base doc_count on the monthly-review documents source of truth (e.g., count JSONs under analysis/daily/$ARTICLE_DATE/monthly-review/documents/ if that directory is produced, or use the manifest the generator writes).

Copilot uses AI. Check for mistakes.
@pethers
Copy link
Copy Markdown
Member

pethers commented Apr 19, 2026

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 19, 2026

@copilot apply changes based on the comments in this thread

Applied all 3 reviewer threads in 07094df:

  1. Stale comment (generators.ts:2180-2189) — buildDeepInspectionSections no longer does local bucketing/merging, so the duplicated "Single-pass classification … merge fpm/eu into euDocs … kept the counts local for classifiedTypes" comment block was removed and replaced with an accurate 4-line pointer stating that Sankey bucketing is delegated to buildLegislativeSankeySection via SANKEY_DOC_TYPE_SPEC.

  2. Weekly workflow doc_count (news-weekly-review.md) — economic-data.json has no .docs field (only dataPoints, commentary, charts, source), so the old jq expression always returned 0 and could falsely mark a missing Sankey as acceptable. Swapped to find "analysis/daily/$ARTICLE_DATE/weekly-review/documents" -maxdepth 1 -name '*.json' | wc -l, which is the actual source of truth (confirmed by inspecting the 2026-04-18 tree — the documents/ subdirectory contains one JSON per ingested Riksdag document).

  3. Monthly workflow doc_count (news-monthly-review.md) — same fix under analysis/daily/$ARTICLE_DATE/monthly-review/documents.

Typecheck clean; 6 targeted vitest suites pass (deep-analysis-section, sankey-section, generate-news-enhanced 1–2, weekly-review, workflow-architecture).

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

@pethers pethers merged commit f9f0cb6 into main Apr 19, 2026
22 checks passed
@pethers pethers deleted the copilot/fix-economic-context-violations branch April 19, 2026 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agentic-workflow Agentic workflow changes ci-cd CI/CD pipeline changes documentation Documentation updates html-css HTML/CSS changes news News articles and content generation refactor Code refactoring size-l Large change (250-1000 lines) size-m Medium change (50-250 lines) size-xs Extra small change (< 10 lines) workflow GitHub Actions workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Economic Context Audit — violations detected on 2026-04-19

3 participants