Conversation
🏷️ Automatic Labeling SummaryThis PR has been automatically labeled based on the files changed and PR metadata. Applied Labels: size-xs Label Categories
For more information, see |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/2fc5abca-4e43-44ac-bc43-605c2539a244 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
|
@copilot and make sure all future articles for all types also would generate expected content |
Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/9e683f78-aa54-48cb-9965-d67cc7af21fb Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Extended the fix so future generated articles of every type satisfy the validator automatically (8531257):
|
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
There was a problem hiding this comment.
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.jsoncommentary 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. |
| /** | ||
| * 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); |
There was a problem hiding this comment.
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.
| sankeyFlows.push({ source: 'pvt', target: 'other', value: otherDocs.length, label: `${otherDocs.length}` }); | ||
| } | ||
|
|
||
| if (sankeyFlows.length < 2) return null; |
There was a problem hiding this comment.
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.
| 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}`, | |
| }); | |
| } |
| 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}` }); |
There was a problem hiding this comment.
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.
| title: topic ? `Legislative Flow — ${topic}` : 'Legislative Flow', | ||
| summary: `Flow of ${docs.length} parliamentary documents from initiating actors to document types`, |
There was a problem hiding this comment.
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.
| 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 apply changes based on the comments in this thread |
…els/titles Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/72741fb8-dfca-4092-8fe3-19eafacbfa02 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Applied all four reviewer threads in a45c322:
Typecheck clean, 256 existing vitest tests pass, |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
| > **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; } |
There was a problem hiding this comment.
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.
| > **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 |
| > **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; } |
There was a problem hiding this comment.
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.
| > **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 |
| 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); | ||
| } |
There was a problem hiding this comment.
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).
| 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}`, | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| 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; |
| * - 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) |
There was a problem hiding this comment.
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).
| * 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 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>
Applied all 5 reviewer threads in a46b537:
Smoketest confirms |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
| // 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.) |
There was a problem hiding this comment.
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).
| > **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 |
There was a problem hiding this comment.
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).
| > **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 | ||
| > ``` |
There was a problem hiding this comment.
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 apply changes based on the comments in this thread |
…count source Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/24a4729e-6291-48c4-939d-b5f1bddef873 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Applied all 3 reviewer threads in 07094df:
Typecheck clean; 6 targeted vitest suites pass (deep-analysis-section, sankey-section, generate-news-enhanced 1–2, weekly-review, workflow-architecture). |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
buildArticleVisualizationSectionsbuildDeepInspectionSectionsno 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 tobuildLegislativeSankeySectionviaSANKEY_DOC_TYPE_SPEC.doc_count—economic-data.jsonhas no.docsfield (onlydataPoints,commentary,charts,source), so the jq expression always returned 0 and could falsely mark a missing Sankey as acceptable. Swapped tofind "analysis/daily/$ARTICLE_DATE/weekly-review/documents" -maxdepth 1 -name '*.json' | wc -l— the actual source of truth for weekly-review documents.doc_count— same fix underanalysis/daily/$ARTICLE_DATE/monthly-review/documents.