Skip to content

Fix empty analysis fallback with lookback retry in pre-article-analysis pipeline#1562

Merged
pethers merged 6 commits into
mainfrom
copilot/fix-empty-analysis-fallback
Apr 5, 2026
Merged

Fix empty analysis fallback with lookback retry in pre-article-analysis pipeline#1562
pethers merged 6 commits into
mainfrom
copilot/fix-empty-analysis-fallback

Conversation

Copilot AI commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

Improves resilience of the pre-article-analysis pipeline and downstream consumers by avoiding "empty analysis" outputs when a target date has no matching documents, and by annotating synthesis with data freshness when lookback is used.

Changes Made

  • Add subtractBusinessDays() utility to data-downloader.ts for lookback date calculation, with strict YYYY-MM-DD input validation (RangeError on malformed dates)
  • Add lookback retry logic in pre-article-analysis.ts when date-filtered documents are 0, expanding to previous business days up to MAX_LOOKBACK_BUSINESS_DAYS (5)
  • Add dataFreshness metadata to synthesis output indicating actual data date vs article date
  • Add readLatestNonEmptyAnalysis() in analysis-reader.ts with DATE_FORMAT_RE guard and fallback to previous day's analysis
  • Clamp excludedDocsCount with Math.max(0, …) to prevent negative log output when by-ID fetches grow allDocs beyond flattenedDocs

Testing

  • 11 tests for subtractBusinessDays() utility (including input validation and edge cases)
  • 13 tests for lookback behavior, isNonEmptyAnalysis(), and readLatestNonEmptyAnalysis() (including date format guard)
  • All 4023+ tests pass across 98 files
  • 0 CodeQL security alerts

Copilot AI linked an issue Apr 5, 2026 that may be closed by this pull request
7 tasks
@github-actions github-actions Bot added the size-xs Extra small change (< 10 lines) label Apr 5, 2026
@github-actions

github-actions Bot commented Apr 5, 2026

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

github-actions Bot commented Apr 5, 2026

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

…cle-analysis pipeline

- Add subtractBusinessDays() and MAX_LOOKBACK_BUSINESS_DAYS to data-downloader.ts
- Add lookback retry logic in pre-article-analysis.ts when date-filtered docs are 0
- Add dataFreshness field to SynthesisSummary and serialize it in synthesis output
- Add isNonEmptyAnalysis() and readLatestNonEmptyAnalysis() to analysis-reader.ts
- Parse dataFreshness from synthesis markdown in parseSynthesisSummary()
- Add 19 new tests covering all new functionality
- All 4020 tests pass, build:lib succeeds

Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/7890551d-fe7d-453b-bc6a-2b1cc092100d

Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
@github-actions github-actions Bot added testing Test coverage refactor Code refactoring size-l Large change (250-1000 lines) labels Apr 5, 2026
@github-actions

github-actions Bot commented Apr 5, 2026

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

…dd comment

- Simplify dataFreshness type from optional+null to required string|null
- Fix isNonEmptyAnalysis JSDoc to accurately describe theme/narrative check
- Add comment explaining requestedIdSet guard prevents lookback in deep-inspection mode

Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/7890551d-fe7d-453b-bc6a-2b1cc092100d

Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix empty analysis fallback with MCP lookback retry Fix empty analysis fallback with lookback retry in pre-article-analysis pipeline Apr 5, 2026
Copilot AI requested a review from pethers April 5, 2026 22:10
@github-actions

github-actions Bot commented Apr 5, 2026

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 commented Apr 5, 2026

Copy link
Copy Markdown
Member

@copilot fix failing checks
TypeScript & JavaScript Testing / Unit Tests (Vitest) (pull_request)
TypeScript & JavaScript Testing / Unit Tests (Vitest) (pull_request)Failing after 37s

@pethers

pethers commented Apr 5, 2026

Copy link
Copy Markdown
Member

@copilot fix failing checks
TypeScript & JavaScript Testing / Unit Tests (Vitest) (pull_request)
TypeScript & JavaScript Testing / Unit Tests (Vitest) (pull_request)Failing after 37s

…rals

The dataFreshness field was made required (not optional) in the refactor
commit, but three test synthesis objects in isNonEmptyAnalysis tests were
missing the property, causing tsc --noEmit to fail in CI.

Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/6a93378c-5a83-4668-8928-58f0ccc8b8f3

Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
@github-actions

github-actions Bot commented Apr 5, 2026

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 commented Apr 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot fix failing checks
[TypeScript & JavaScript Testing / Unit Tests (Vitest) (pull_request)](https://github.com/Hack23/riksdagsmonitor/actions/runs/24011574628/job/70023930403?p...

Fixed in cbae1dd — added the missing dataFreshness: null property to 3 test synthesis object literals in tests/analysis-reader.test.ts. The SynthesisSummaryResult type was changed from optional to required for dataFreshness in the refactor commit, but the test objects weren't updated. CI is now green.

@copilot fix failing checks
[TypeScript & JavaScript Testing / Unit Tests (Vitest) (pull_request)](https://github.com/Hack23/riksdagsmonitor/actions/runs/24011574628/job/70023930403?...

Already fixed in cbae1dd. The TypeScript & JavaScript Testing workflow is now passing on this commit.

@pethers
pethers marked this pull request as ready for review April 5, 2026 23:34
Copilot AI review requested due to automatic review settings April 5, 2026 23:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves resilience of the pre-article-analysis pipeline and downstream consumers by avoiding “empty analysis” outputs when a target date has no matching documents, and by annotating synthesis with data freshness when lookback is used.

Changes:

  • Add business-day lookback support (subtractBusinessDays, MAX_LOOKBACK_BUSINESS_DAYS) and apply it when date-filtered documents are zero.
  • Add dataFreshness metadata to synthesis output and parsing to record when the analysis data date differs from the article date.
  • Add isNonEmptyAnalysis + readLatestNonEmptyAnalysis to support fallback to the most recent non-empty daily analysis, with tests.

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
tests/analysis-reader.test.ts Adds test coverage for business-day subtraction, dataFreshness parsing, non-empty analysis detection, and non-empty lookback reading.
scripts/pre-article-analysis/markdown-serializer.ts Extends synthesis summary schema/serialization to include dataFreshness in “Data Quality Notes”.
scripts/pre-article-analysis/data-downloader.ts Introduces business-day date arithmetic utility and lookback constant used by the pipeline.
scripts/pre-article-analysis.ts Implements lookback retry when no documents match the requested date and propagates dataFreshness into synthesis.
scripts/analysis-reader.ts Parses dataFreshness, adds “non-empty analysis” detection, and adds a lookback reader for non-empty daily analysis.

Comment on lines +78 to +80
*
* @param dateStr - ISO date string (YYYY-MM-DD)
* @param days - Number of business days to subtract (must be >= 0)

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

The JSDoc says days must be >= 0, but the implementation clamps negative values to 0 (and tests rely on that). Update the JSDoc to reflect the actual behavior, or enforce the contract by throwing on negative input.

Suggested change
*
* @param dateStr - ISO date string (YYYY-MM-DD)
* @param days - Number of business days to subtract (must be >= 0)
* Fractional values are rounded down, and negative values are treated as 0.
*
* @param dateStr - ISO date string (YYYY-MM-DD)
* @param days - Number of business days to subtract; fractional values are floored and values < 0 are clamped to 0

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +84
export function subtractBusinessDays(dateStr: string, days: number): string {
const d = new Date(`${dateStr}T00:00:00Z`);
let remaining = Math.max(0, Math.floor(days));

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

subtractBusinessDays() will throw a RangeError on malformed dateStr because new Date(...).toISOString() fails for Invalid Date. Since this is an exported utility, consider validating dateStr (e.g., strict YYYY-MM-DD) and either throw a clearer error or return a safe fallback.

Copilot uses AI. Check for mistakes.
Comment thread scripts/pre-article-analysis.ts Outdated
Comment on lines 671 to 675
@@ -634,6 +674,9 @@ async function runPreArticleAnalysis(opts: {
console.log(
` Selected ${allDocs.length} documents for analysis for ${date} (${excludedDocsCount} with missing or non-matching dates excluded)`,

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

excludedDocsCount can become negative when --document-ids causes additional documents to be fetched and appended to allDocs (making allDocs.length > flattenedDocs.length). This will produce misleading logs like "(-2 excluded)". Consider computing excluded counts before adding fetched-by-id docs, or clamp at 0 and log fetched-by-id count separately.

Copilot uses AI. Check for mistakes.
Comment on lines +795 to +799
for (let i = 1; i <= maxDaysBack; i++) {
const d = new Date(`${date}T00:00:00Z`);
d.setUTCDate(d.getUTCDate() - i);
const prevDate = d.toISOString().slice(0, 10);
const prev = await readDailyAnalysis(prevDate, basePath);

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

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

readLatestNonEmptyAnalysis() constructs a Date from the date string without validating format. If date is not a valid YYYY-MM-DD, toISOString() will throw (whereas readDailyAnalysis safely returns an empty result due to DATE_FORMAT_RE). Add the same DATE_FORMAT_RE guard here before doing Date arithmetic.

Copilot uses AI. Check for mistakes.
@pethers

pethers commented Apr 5, 2026

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

@github-actions

github-actions Bot commented Apr 5, 2026

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 commented Apr 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

Applied all 4 review comments in 680d62a and e32d9aa:

  1. data-downloader.ts:78-80 — Updated JSDoc to document clamping behavior, removed duplicate @param description
  2. data-downloader.ts:82-84 — Added YYYY-MM-DD regex + isNaN guard with clear RangeError messages (4 new tests)
  3. pre-article-analysis.ts:671-675 — Clamped excludedDocsCount with Math.max(0, …) to prevent negative log output
  4. analysis-reader.ts:795-799 — Added DATE_FORMAT_RE guard before Date arithmetic in readLatestNonEmptyAnalysis() (1 new test)

All 91 analysis-reader tests pass, 4023+ total tests pass, 0 CodeQL alerts.

@github-actions

github-actions Bot commented Apr 5, 2026

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 8b83f82 into main Apr 5, 2026
16 checks passed
@pethers
pethers deleted the copilot/fix-empty-analysis-fallback branch April 5, 2026 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Code refactoring size-l Large change (250-1000 lines) size-xs Extra small change (< 10 lines) testing Test coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix empty analysis fallback with MCP lookback retry in pre-article-analysis pipeline

3 participants