Integrate document analysis framework with all content generators#1238
Integrate document analysis framework with all content generators#1238
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 |
- Extended DeepAnalysisOptions with optional frameworkAnalysis parameter - Added analyzeDocumentsForContent() bridge function in shared.ts - Added PESTLE, stakeholder impact, risk, and implementation section renderers when framework analysis is available - Updated 6 content generators to pass framework analysis through - Added 4 new localization keys across all 14 languages - Fixed null safety in calculateCoalitionRiskIndex for partyPerformance - All 3711 existing tests pass Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
- Updated analyzeDocumentsForContent() to return both framework and perspective analyses (6 lenses: government, opposition, citizen, economic, international, media) - Extended DeepAnalysisOptions with perspectiveAnalysis field - Added perspective insights rendering to generateDeepAnalysisSection() - Fixed null safety for partyPerformance in government and opposition lenses - Updated all 6 content generators to pass both analysis results through - Added 19 comprehensive integration tests covering: - Bridge function returns both analysis types - PESTLE, stakeholder, risk, and implementation section rendering - 6-lens perspective insights rendering - Multi-language label support (en, sv, de, ja, ar) - HTML escaping / XSS prevention - Graceful degradation without framework analysis - Confidence scoring and iteration metadata - All 3730 tests pass (3711 existing + 19 new) Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
- Extracted 6 named constants (MAX_PESTLE_ITEMS, MAX_STAKEHOLDER_IMPACTS, MAX_RISK_ITEMS, MAX_PERSPECTIVE_INSIGHTS, MAX_IMPLEMENTATION_OBSTACLES, MAX_AGENCIES_DISPLAYED) for improved code maintainability - Fixed test description to match actual validation (≥3 groups) - Fixed CIAContext fixture to satisfy strict type checking - All 3730 tests pass, TypeScript compiles cleanly Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
There was a problem hiding this comment.
Pull request overview
This PR connects the existing AI document analysis framework and 6-lens perspective analysis framework to all article content generators, so deep analysis sections can be enriched with structured PESTLE, stakeholder impact, risk, implementation, and perspective insights.
Changes:
- Added a shared bridge (
analyzeDocumentsForContent()) and extended deep-analysis rendering to accept optional framework + perspective analysis inputs. - Updated all relevant content generators to call the bridge and pass analyses through to
generateDeepAnalysisSection(). - Expanded localization labels and added integration tests; also fixed null-safety around
cia.partyPerformance.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/analysis-integration.test.ts | New integration coverage for bridge + enriched deep analysis rendering and localization. |
| scripts/types/content.ts | Extends ContentLabelSet with new deep-analysis framework label keys. |
| scripts/data-transformers/risk-analysis.ts | Null-safe guard for partyPerformance in coalition risk calculation. |
| scripts/data-transformers/content-generators/week-ahead.ts | Uses bridge + passes framework/perspective analyses into deep analysis section. |
| scripts/data-transformers/content-generators/shared.ts | Introduces bridge + extends generateDeepAnalysisSection() to render framework and 6-lens insights. |
| scripts/data-transformers/content-generators/propositions.ts | Uses bridge + passes analyses into deep analysis rendering. |
| scripts/data-transformers/content-generators/motions.ts | Uses bridge + passes analyses into deep analysis rendering. |
| scripts/data-transformers/content-generators/interpellations.ts | Uses bridge + passes analyses into deep analysis rendering. |
| scripts/data-transformers/content-generators/index.ts | Re-exports analyzeDocumentsForContent() from the generator barrel. |
| scripts/data-transformers/content-generators/generic.ts | Uses bridge + passes analyses into deep analysis rendering. |
| scripts/data-transformers/content-generators/committee.ts | Uses bridge + passes analyses into deep analysis rendering. |
| scripts/data-transformers/constants/content-labels-part1.ts | Adds new framework label keys for part1 languages. |
| scripts/data-transformers/constants/content-labels-part2.ts | Adds new framework label keys for part2 languages. |
| scripts/analysis-framework/lenses/opposition.ts | Null-safe guard for cia.partyPerformance usage. |
| scripts/analysis-framework/lenses/government.ts | Null-safe guard for cia.partyPerformance usage. |
| const { frameworkAnalysis, perspectiveAnalysis } = analyzeDocumentsForContent(allWeekDocs, lang, weekCia); | ||
| content += generateDeepAnalysisSection({ | ||
| documents: allWeekDocs, | ||
| lang, | ||
| cia: weekCia, | ||
| articleType: 'week-ahead', | ||
| frameworkAnalysis, | ||
| perspectiveAnalysis, | ||
| }); | ||
|
|
| const top = [...byType.values()].slice(0, MAX_RISK_ITEMS); | ||
| const rows = top.map(r => { | ||
| const icon = r.severity === 'high' ? '🔴' : r.severity === 'medium' ? '🟡' : '🟢'; | ||
| return ` <li>${icon} <strong>${escapeHtml(r.type)}</strong> (${escapeHtml(r.severity)}): ${escapeHtml(r.description)}</li>`; | ||
| }); |
| * Render a summary of stakeholder impacts across all analysed documents. | ||
| * Shows up to 7 stakeholder groups with their impact direction and burden. | ||
| */ | ||
| function renderStakeholderImpactSummary(analyses: DocumentAnalysis[]): string { | ||
| // Collect all stakeholder impacts, deduplicated by stakeholder name | ||
| const impactMap = new Map<string, StakeholderImpact>(); | ||
| for (const a of analyses) { | ||
| for (const impact of a.stakeholderImpacts) { | ||
| // Keep the first (or higher-magnitude) impact per stakeholder | ||
| const existing = impactMap.get(impact.stakeholder); | ||
| if (!existing) { | ||
| impactMap.set(impact.stakeholder, impact); | ||
| } | ||
| } |
| const parts: string[] = []; | ||
| const fIcon = highestFeasibility === 'high' ? '🟢' : highestFeasibility === 'medium' ? '🟡' : '🔴'; | ||
| parts.push(` <p>${fIcon} <strong>Feasibility:</strong> ${escapeHtml(highestFeasibility)}. ${escapeHtml(assessments[0].estimatedTimeline)}</p>`); | ||
|
|
||
| if (allObstacles.size > 0) { | ||
| const obstacleList = [...allObstacles].slice(0, MAX_IMPLEMENTATION_OBSTACLES).map(o => `<li>${escapeHtml(o)}</li>`).join(''); | ||
| parts.push(` <p><strong>Key obstacles:</strong></p>\n <ul>${obstacleList}</ul>`); | ||
| } | ||
|
|
||
| if (allAgencies.size > 0) { | ||
| parts.push(` <p><strong>Agencies involved:</strong> ${[...allAgencies].slice(0, MAX_AGENCIES_DISPLAYED).map(a => escapeHtml(a)).join(', ')}</p>`); | ||
| } |
| const { frameworkAnalysis, perspectiveAnalysis } = analyzeDocumentsForContent(propositions, lang, data.ciaContext); | ||
| content += generateDeepAnalysisSection({ | ||
| documents: propositions, | ||
| lang, | ||
| cia: data.ciaContext, | ||
| articleType: 'propositions', | ||
| frameworkAnalysis, | ||
| perspectiveAnalysis, | ||
| }); |
| const impacts = [...impactMap.values()].slice(0, MAX_STAKEHOLDER_IMPACTS); | ||
| if (impacts.length === 0) return ' <p>No stakeholder impact data available.</p>'; | ||
|
|
| /** | ||
| * Render a risk assessment summary. Groups risks by type and shows up to 5. | ||
| */ | ||
| function renderRiskAssessment(risks: RiskAssessment[]): string { | ||
| // Deduplicate by type+severity, preferring higher severity | ||
| const byType = new Map<string, RiskAssessment>(); | ||
| for (const r of risks) { | ||
| const key = r.type; | ||
| const existing = byType.get(key); | ||
| if (!existing || severityRank(r.severity) > severityRank(existing.severity)) { | ||
| byType.set(key, r); | ||
| } | ||
| } |
|
|
||
| for (const ia of assessments) { | ||
| ia.keyObstacles.forEach(o => allObstacles.add(o)); | ||
| ia.agenciesInvolved.forEach(a => allAgencies.add(a)); | ||
| if (feasibilityRank(ia.feasibility) < feasibilityRank(highestFeasibility)) { | ||
| highestFeasibility = ia.feasibility; | ||
| } | ||
| } | ||
|
|
||
| const parts: string[] = []; | ||
| const fIcon = highestFeasibility === 'high' ? '🟢' : highestFeasibility === 'medium' ? '🟡' : '🔴'; | ||
| parts.push(` <p>${fIcon} <strong>Feasibility:</strong> ${escapeHtml(highestFeasibility)}. ${escapeHtml(assessments[0].estimatedTimeline)}</p>`); |
| const { frameworkAnalysis, perspectiveAnalysis } = analyzeDocumentsForContent(motions, lang, data.ciaContext); | ||
| content += generateDeepAnalysisSection({ | ||
| documents: motions, | ||
| lang, | ||
| cia: data.ciaContext, | ||
| articleType: 'motions', | ||
| frameworkAnalysis, | ||
| perspectiveAnalysis, | ||
| }); |
| const { frameworkAnalysis, perspectiveAnalysis } = analyzeDocumentsForContent(interpellations, lang, data.ciaContext); | ||
| content += generateDeepAnalysisSection({ | ||
| documents: interpellations, | ||
| lang, | ||
| cia: data.ciaContext, | ||
| articleType: 'interpellations', | ||
| frameworkAnalysis, | ||
| perspectiveAnalysis, | ||
| }); |
|
@copilot apply changes based on the comments in this thread |
…nderers Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Implemented the review-thread fixes in 881b7ff:
Screenshot of updated deep-analysis UI: |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
analyzeDocumentsForContent()when deep analysis will not render (doc count < 2 for standard types)analysis-integration,data-transformers) and full suite (3730/3730pass)tsconfig.scripts.json) with no errorsOriginal prompt
This section details on the original issue you should resolve
<issue_title>AI-Powered Comprehensive Document Analysis Framework with Multi-Stakeholder Perspectives</issue_title>
<issue_description>## 📋 Issue Type
Feature / Architecture Enhancement
🎯 Objective
Create an AI-powered Comprehensive Document Analysis Framework that ensures every parliamentary document, government proposition, and policy paper is systematically analyzed from all stakeholders' perspectives using structured analytical techniques (SWOT, PESTLE, stakeholder mapping, impact assessment) with multiple AI iterations for depth and accuracy. This framework becomes the shared analytical backbone that all content generators and agentic workflows call for consistent, high-quality political analysis.
📊 Current State
generateArticleContent()formats document metadata (title, type, date) — not the document's actual policy contentpolicy-analysis.ts: ContainsdetectPolicyDomains()(keyword matching),assessConfidenceLevel()(heuristic),detectNarrativeFrames()(pattern matching) — useful utilities but not AI-powered analysisrisk-analysis.ts: ContainscalculateCoalitionRiskIndex()anddetectAnomalousPatterns()— formula-based, not AI-analyzed🚀 Desired State
Comprehensive Analysis Framework
Every document processed through the framework receives:
Multi-Iteration Analysis Protocol
Stakeholder Universe (Dynamic Selection)
AI selects relevant stakeholders per document from this universe:
🔧 Implementation Approach
Step 1: Create Core Analysis Framework