Skip to content

feat: Enhance week-ahead cross-referencing with policy context, questions, and interpellation spotlight#609

Merged
pethers merged 4 commits intomainfrom
copilot/enhance-cross-referencing-tools
Feb 27, 2026
Merged

feat: Enhance week-ahead cross-referencing with policy context, questions, and interpellation spotlight#609
pethers merged 4 commits intomainfrom
copilot/enhance-cross-referencing-tools

Conversation

Copy link
Contributor

Copilot AI commented Feb 26, 2026

  • Add extractKeywords helper (min-length 2 for EU/KU/AI acronyms, DRY shared by both matchers)
  • Add helper functions findRelatedDocuments, findRelatedQuestions, extractMinister in content-generators.ts
  • Render per-event "Policy Context" box (all 14 languages) inside high-priority events loop
  • Rename "Parliamentary Questions to Ministers" → "Questions to Watch" (all 14 languages)
  • Rename "Interpellations Pending" → "Interpellation Spotlight" (all 14 languages) + show extracted minister name with minister-target CSS class
  • Remove TODO comment in week-ahead.ts (lines 50-53), replaced with implementation note
  • PR review fixes:
    • Add organ?: string to RawCalendarEvent interface (type safety — no more cast to Record<string, string>)
    • extractKeywords now splits on [\s,–-]+ (hyphens, commas, en-dashes) so hyphenated terms like "EU-riktlinjer" are tokenized to "eu" + "riktlinjer"
    • extractMinister uses [^\S\n]+ (non-newline whitespace) to prevent crossing into the next line; strips trailing "om/angående/rörande/beträffande" clauses and punctuation
    • All non-null assertions result.articles[0]! replaced with explicit length checks + descriptive throw new Error()
    • Added 9 edge-case / cross-referencing tests: no-minister-line, empty-name "till", multiple "till" patterns, no-match returns no box, 3-item limit enforcement, high-priority event with unrelated interpellation
Original prompt

This section details on the original issue you should resolve

<issue_title>Improve Week Ahead: Enhance cross-referencing with policy documents, questions, and interpellations</issue_title>
<issue_description>## 📋 Issue Type
Enhancement — Extend Week Ahead articles with document context, written questions, and interpellations

🎯 Objective

Implement the TODO additional MCP tools in scripts/news-types/week-ahead.ts to provide richer forward-looking parliamentary previews: search_dokument (related policy documents), get_fragor (written questions for upcoming debates), and get_interpellationer (interpellations scheduled for the week).

📊 Current State

  • File: scripts/news-types/week-ahead.ts (488 lines)
  • Implemented tools: get_calendar_events ✅, search_dokument ✅, search_anforanden ✅, get_fragor ✅, get_interpellationer
  • REQUIRED_TOOLS: 5 tools all declared
  • Gap: While tools are listed in REQUIRED_TOOLS, the TODO at line 50 indicates the analysis depth is shallow — calendar events drive the article but related policy documents, questions, and interpellations aren't deeply cross-referenced with calendar items
  • Impact: Week ahead articles list what's happening but miss the policy context (why it matters, what questions are pending, which interpellations will be debated)

🚀 Desired State

  • Calendar events are enriched with related policy document context
  • Written questions (get_fragor) are linked to specific upcoming debates
  • Interpellations are highlighted with their urgency and minister responses
  • Articles provide "why this matters" context for each major calendar item
  • Cross-referencing between calendar events and pending parliamentary questions
  • Enhanced prospective analysis connecting upcoming events to ongoing policy threads

🔧 Implementation Approach

1. Deep document cross-referencing (~80 lines)

For each calendar event, search for related documents:

// For each calendar event, find related policy context
const relatedDocs = await mcpClient.call('search_dokument', {
  titel: calendarEvent.summary,
  from_date: weekStartDate,
  to_date: weekEndDate,
  limit: 3
});
  • Connect committee meetings to their pending betänkanden
  • Link plenary sessions to propositions being debated

2. Question-debate linkage (~60 lines)

Match written questions to upcoming committee/plenary sessions:

  • Identify which ministers will face questions
  • Highlight overdue responses
  • Flag questions connected to upcoming votes

3. Interpellation enrichment (~60 lines)

  • Link interpellations to specific debate slots
  • Add minister response status
  • Highlight high-profile confrontations

4. Enhanced article templates

  • Add "Policy Context" subsection per day
  • Add "Questions to Watch" sidebar content
  • Add "Interpellation Spotlight" for major confrontations
  • Update all 14 language templates

🤖 Recommended Agent

code-quality-engineer — TypeScript enhancement with MCP tool orchestration patterns

✅ Acceptance Criteria

  • Calendar events cross-referenced with related policy documents
  • Written questions linked to upcoming debate topics
  • Interpellations enriched with minister response context
  • Article templates include new "Policy Context" sections
  • All 14 language templates updated
  • Prospective tone maintained (forward-looking, not retrospective)
  • Article quality validation passes with enhanced content
  • No regression in existing week-ahead generation
  • Performance within 15-second batch target for all languages

📚 References

  • File: scripts/news-types/week-ahead.ts:50-53 (TODO comments)
  • MCP Tools: search_dokument, get_fragor, get_interpellationer
  • Content generators: scripts/data-transformers/content-generators.ts
  • Calendar transformer: scripts/data-transformers/calendar.ts
    </issue_description>

<agent_instructions>Enhance week-ahead article cross-referencing in scripts/news-types/week-ahead.ts. Deepen integration of search_dokument, get_fragor, and get_interpellationer to cross-reference calendar events with policy context. Add "Policy Context" and "Questions to Watch" template sections. Update all 14 language templates. Run tests with npx vitest run.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…ions to watch, and interpellation spotlight

Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Copilot AI changed the title [WIP] Enhance Week Ahead with additional MCP tools feat: Enhance week-ahead cross-referencing with policy context, questions, and interpellation spotlight Feb 26, 2026
@pethers pethers requested a review from Copilot February 27, 2026 02:14
@github-actions github-actions bot added testing Test coverage refactor Code refactoring size-l Large change (250-1000 lines) labels Feb 27, 2026
@github-actions
Copy link
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
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

This PR enhances week-ahead articles by implementing deep cross-referencing between calendar events and parliamentary documents/questions. Previously, week-ahead articles displayed calendar events, documents, questions, and interpellations as disconnected lists. Now, high-priority calendar events are enriched with inline "Policy Context" boxes showing up to 3 related documents/questions per event, questions are relabeled as "Questions to Watch," and interpellations gain a "Interpellation Spotlight" section with minister attribution extracted from summaries.

Changes:

  • Added 4 new helper functions (extractKeywords, findRelatedDocuments, findRelatedQuestions, extractMinister) to enable keyword-based cross-referencing between events, documents, and questions
  • Replaced generic section labels ("Parliamentary Questions to Ministers" → "Questions to Watch", "Interpellations Pending" → "Interpellation Spotlight") with more engaging titles in all 14 languages
  • Generated inline "Policy Context" boxes for high-priority events showing related documents and questions with proper HTML escaping and URL sanitization

Reviewed changes

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

File Description
scripts/news-types/week-ahead.ts Removed TODO comments and updated documentation to reflect that all 5 MCP tools are now actively used with cross-referencing implementation
scripts/data-transformers/content-generators.ts Added cross-referencing logic with 4 helper functions, inline Policy Context rendering, updated section labels for Questions/Interpellations in 14 languages, and minister extraction from interpellation summaries
tests/news-types/week-ahead.test.ts Added 6 new tests in "Enhanced Cross-Referencing" suite covering Policy Context rendering, multilingual section labels (EN/SV), and minister name extraction

Comment on lines 59 to 62
function findRelatedDocuments(event: RawCalendarEvent, documents: RawDocument[]): RawDocument[] {
const rec = event as Record<string, string>;
const eventOrgan = rec['organ'] ?? '';
const keywords = extractKeywords(rec['rubrik'] ?? rec['titel'] ?? rec['title'] ?? '');
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The function accesses the organ property via type casting to Record<string, string>, but RawCalendarEvent doesn't define an organ field in scripts/data-transformers/types.ts (lines 12-24). This creates a type safety gap.

Consider either:

  1. Adding organ?: string; to the RawCalendarEvent interface if the MCP server actually returns this field
  2. Or documenting that this field may not exist in production data, which would cause the organ-matching logic to silently fail

The test mocks provide this field, but if production data doesn't include it, the cross-referencing feature will only work via keyword matching, not organ matching.

Copilot uses AI. Check for mistakes.

/** Extract meaningful keywords from text for cross-reference matching (min 2 chars, captures EU, KU, etc.) */
function extractKeywords(text: string): string[] {
return text.toLowerCase().split(/\s+/).filter(w => w.length >= 2);
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The extractKeywords function filters words by minimum length of 2 characters, which correctly captures acronyms like 'EU', 'KU', and 'AI'. However, it splits on whitespace only and doesn't handle common Swedish compound word patterns or punctuation-separated terms.

For example, if an event title is "Budget-diskussion EU-riktlinjer" (Budget discussion EU guidelines), splitting on \s+ will produce "Budget-diskussion" and "EU-riktlinjer" as single keywords, potentially missing matches against documents that use "Budget" or "EU" alone.

Consider splitting on [\s\-,]+ to also break on hyphens and commas, or using a more sophisticated tokenization approach for better cross-referencing accuracy.

Suggested change
return text.toLowerCase().split(/\s+/).filter(w => w.length >= 2);
return text.toLowerCase().split(/[\s,-]+/u).filter(w => w.length >= 2);

Copilot uses AI. Check for mistakes.
/** Extract targeted minister name from interpellation summary "till MINISTER" header line */
function extractMinister(summary: string): string {
const m = summary.match(/\btill\s+([^\n]+)/i);
return m ? m[1].trim() : '';
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The regex pattern /\btill\s+([^\n]+)/i captures everything after "till " until the first newline. This assumes the minister name doesn't contain newlines, but the captured group could include trailing content if the format is "till MINISTER\nExtra text".

For robustness, consider trimming or more specifically matching common minister title patterns. Swedish minister names typically follow patterns like "Statsminister NAME" or "Minister för AREA NAME". A more specific pattern could avoid capturing unwanted trailing text.

Suggested change
return m ? m[1].trim() : '';
if (!m) return '';
const raw = m[1].trim();
// Remove common trailing topic clauses (e.g. "om X", "angående Y") and punctuation
const lowerRaw = raw.toLowerCase();
const stopPhrases = [' om ', ' angående ', ' rörande ', ' beträffande '];
let end = raw.length;
for (const phrase of stopPhrases) {
const idx = lowerRaw.indexOf(phrase);
if (idx !== -1 && idx < end) {
end = idx;
}
}
// Also cut at common terminating punctuation if it comes earlier
const punctIdx = raw.search(/[?:;.,]/);
if (punctIdx !== -1 && punctIdx < end) {
end = punctIdx;
}
return raw.slice(0, end).trim();

Copilot uses AI. Check for mistakes.
Comment on lines 452 to 454
const result = await weekAheadModule.generateWeekAhead({ languages: ['en'] });
expect(result.success).toBe(true);
const article = result.articles[0]!;
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The tests use result.articles[0]! (non-null assertion) to access the first article. While the test expects result.success to be true, if the article generation silently produces zero articles, this would cause a test crash rather than a clear failure message.

Consider using optional chaining result.articles[0]?.html or explicit length checks with better error messages to make test failures more debuggable when articles aren't generated as expected.

Copilot uses AI. Check for mistakes.
expect(article.html).toContain('minister-target');
expect(article.html).toContain('Statsminister Ulf Kristersson');
});

Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The test verifies that minister names are extracted and displayed, but doesn't test edge cases where:

  1. The summary has no "till" line (extractMinister returns empty string)
  2. The summary has malformed minister lines
  3. Multiple "till" patterns appear

Consider adding test cases for these edge cases to ensure the feature degrades gracefully when interpellation summaries don't follow the expected format.

Suggested change
it('should handle interpellation summary without minister line gracefully', async () => {
mockClientInstance.fetchInterpellations.mockResolvedValue([{
titel: 'Question about transport policy',
dok_id: 'H901ip4',
parti: 'M',
summary: 'Interpellation 2025/26:4\nav Alex Example\nDetta är en fråga om transportpolitiken utan specifik ministerrad.',
}]);
const result = await weekAheadModule.generateWeekAhead({ languages: ['en'] });
expect(result.success).toBe(true);
const article = result.articles[0]!;
// When no minister can be extracted, component should degrade gracefully and not render a minister-target
expect(article.html).not.toContain('minister-target');
});
it('should handle malformed minister line without breaking rendering', async () => {
mockClientInstance.fetchInterpellations.mockResolvedValue([{
titel: 'Question about education policy',
dok_id: 'H901ip5',
parti: 'L',
// "till" appears but no valid minister name follows
summary: 'Interpellation 2025/26:5\nav Chris Example\ntill \nDetta är en fråga om utbildningspolitiken.',
}]);
const result = await weekAheadModule.generateWeekAhead({ languages: ['en'] });
expect(result.success).toBe(true);
const article = result.articles[0]!;
// Malformed minister line should not cause a crash or render an empty minister-target
expect(article.html).not.toContain('minister-target');
});
it('should handle multiple "till" patterns in summary without failing', async () => {
mockClientInstance.fetchInterpellations.mockResolvedValue([{
titel: 'Question about climate and finance policy',
dok_id: 'H901ip6',
parti: 'C',
summary: 'Interpellation 2025/26:6\nav Pat Example\ntill Klimatminister Romina Pourmokhtari\noch till Finansminister Elisabeth Svantesson\nDetta är en fråga om klimat- och finanspolitiken.',
}]);
const result = await weekAheadModule.generateWeekAhead({ languages: ['en'] });
expect(result.success).toBe(true);
const article = result.articles[0]!;
// The spotlight section should still render; parsing ambiguities must not break generation
expect(article.html).toContain('Interpellation Spotlight');
});

Copilot uses AI. Check for mistakes.
Comment on lines 440 to 458
it('should show Policy Context box when event organ matches document organ', async () => {
// Provide a high-priority event (contains 'EU' to pass isHighPriority) with organ matching the doc
mockClientInstance.fetchCalendarEvents.mockResolvedValue([{
id: '1', title: 'EU budget vote', date: '2026-02-16', type: 'chamber', organ: 'Kammaren',
}]);
mockClientInstance.searchDocuments.mockResolvedValue([{
titel: 'Budget Proposition 2026',
dok_id: 'H901prop1',
doktyp: 'prop',
organ: 'Kammaren',
}]);

const result = await weekAheadModule.generateWeekAhead({ languages: ['en'] });
expect(result.success).toBe(true);
const article = result.articles[0]!;
// 'EU budget vote' event has organ 'Kammaren', matching the document's organ
expect(article.html).toContain('Policy Context');
expect(article.html).toContain('policy-context-box');
});
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The test only verifies that keyword-overlap matching works when an event title contains "budget" and a question title also contains "budget". However, it doesn't test:

  1. The organ-matching code path (which has a type safety issue)
  2. Cases where no documents/questions match any events
  3. Cases where more than 3 matches exist (to verify the slice(0,3) limit works)
  4. Events that are high-priority but have no related content

Consider adding test cases to exercise all code paths in the cross-referencing logic.

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

pethers commented Feb 27, 2026

@copilot apply changes based on the comments in this thread

@github-actions
Copy link
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

… robustness, and edge-case tests

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

Copilot AI commented Feb 27, 2026

@copilot apply changes based on the comments in this thread

Applied all changes from the review thread in commit 15fbb2a:

  • RawCalendarEvent.organ — added organ?: string to the interface in types.ts; removed the Record<string, string> cast in findRelatedDocuments/findRelatedQuestions
  • extractKeywords — now splits on [\s,–-]+ (whitespace, hyphens, en-dashes, commas) so "EU-riktlinjer" tokenises to "eu" + "riktlinjer"
  • extractMinister — regex changed to [^\S\n]+ (non-newline whitespace) to prevent crossing onto the next line; added stop-phrase stripping (om/angående/rörande/beträffande) and punctuation cutoff
  • Tests — replaced all result.articles[0]! with explicit length checks + throw new Error(); added 9 new edge-case tests: no-"till"-line, empty "till" name, multiple "till" patterns, no-match renders no policy-context-box, slice(0,3) limit, and high-priority event with unrelated interpellation

@github-actions
Copy link
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
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 4 out of 4 changed files in this pull request and generated no new comments.

@pethers pethers marked this pull request as ready for review February 27, 2026 07:14
@pethers pethers merged commit 83d25d7 into main Feb 27, 2026
20 checks passed
@pethers pethers deleted the copilot/enhance-cross-referencing-tools branch February 27, 2026 07:14
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) testing Test coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve Week Ahead: Enhance cross-referencing with policy documents, questions, and interpellations

3 participants