Improve political analysis methods, AI prompts, and news quality#930
Improve political analysis methods, AI prompts, and news quality#930
Conversation
- Filter placeholder/fallback text from article meta keywords in motions, weekly-review, and monthly-review strategies to prevent contamination - Render [AI_ANALYSIS_REQUIRED] markers as user-friendly "analysis pending" notices in deep-analysis-content.ts instead of raw marker strings - Add isAiMarker() helper and aiPendingNotice() to analysis-constants.ts - Add AI_PENDING_CLASS CSS class for styling pending analysis sections Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/2ba3bb30-556b-4af8-974b-df5237aeb020 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
… gates - Add 5 new quality issues to Top 15 in ai-driven-analysis-guide.md - Add visualization data generation rules (Chart.js, D3, SWOT completeness) - Add AI-first content generation protocol with field-by-field requirements - Add article-analysis cross-reference requirements - Add placeholder keyword and zero metrics gates to all 9 workflow files - Add visualization completeness gates to all 9 workflow files - Update guide version to 4.0 Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/2ba3bb30-556b-4af8-974b-df5237aeb020 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
…prove SWOT empty states - Add CSS for .ai-analysis-pending indicators with orange border and clock icon - Add CSS for D3 treemap, network, and SWOT chart wrappers with dark mode - Add dashboard chart hover effects and styling - Improve SWOT empty quadrant messages with informative AI-pending notice - All 2724 tests passing Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/2ba3bb30-556b-4af8-974b-df5237aeb020 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
…rove JSDoc - Move isPlaceholderText() from 3 strategy files to shared analysis-constants.ts - Improve isAiMarker() JSDoc with legacy marker format documentation - Inline AI_PENDING_OPEN constant into aiPendingNotice() function - All 2724 tests passing, 0 lint errors Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/2ba3bb30-556b-4af8-974b-df5237aeb020 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
…terns - Use character class [\\s._-] instead of literal dot for matching "data unavailable" variants in TypeScript and shell grep patterns - Update JSDoc to document all matched separator patterns - All 2724 tests passing Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/2ba3bb30-556b-4af8-974b-df5237aeb020 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Replace [AI_ANALYSIS_REQUIRED] placeholder markers with factual, data-driven content generation across all 5 analysis builders: - buildVotingAnalysis: why, impact assessment (5 dimensions), outlook now generate contextual text from voting patterns, polarization, anomalies - buildProspectiveAnalysis: why, impact, consequences, mistakes, outlook all generate from event/committee/pipeline/bottleneck data - buildBreakingAnalysis: why, impact, consequences, mistakes, outlook generate from feed data (adopted texts, events, procedures, MEPs) - buildPropositionsAnalysis: why, impact, consequences, outlook, mistakes generate from pipeline health score and throughput metrics - buildCommitteeAnalysis: why, impact, consequences, mistakes, outlook generate from committee activity rates, document counts, member data - buildDefaultStakeholderPerspectives: reasoning now generates per-group factual descriptions instead of AI_MARKER (6 stakeholders × all articles) This eliminates ~52 [AI_ANALYSIS_REQUIRED] placeholders per article run, producing articles with complete analytical content from the start. Updated 10 test assertions from AI_MARKER expectations to data-driven content expectations. All 2724 tests passing. Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/bcd58788-c318-483d-9cec-161b7b21f8d4 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR replaces several [AI_ANALYSIS_REQUIRED] placeholders with deterministic, data-driven political analysis text, improves placeholder handling in rendered HTML/CSS, and tightens workflow quality gates to prevent placeholder metadata and silent zero-metrics from shipping.
Changes:
- Replaced AI marker placeholders in analysis builders and stakeholder perspectives with data-derived narrative text (why/impact/outlook/consequences/mistakes).
- Added
isAiMarker()+AI_PENDING_CLASSrendering path to show “analysis pending” notices instead of raw markers, plus corresponding CSS styling. - Filtered placeholder/fallback strings out of SEO keywords and expanded workflow methodology + checklists/quality gates.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/deep-analysis.test.js | Updates assertions to expect data-driven analysis text instead of AI markers. |
| styles.css | Adds .ai-analysis-pending styling and additional visualization container styles. |
| src/utils/intelligence-analysis.ts | Replaces default stakeholder reasoning markers with deterministic reasoning text. |
| src/generators/swot-content.ts | Replaces empty SWOT fallback “—” with an editorial/AI pending notice. |
| src/generators/strategies/weekly-review-strategy.ts | Filters placeholder titles/types/topics out of generated meta keywords. |
| src/generators/strategies/motions-strategy.ts | Filters placeholder titles/types/topics out of generated meta keywords. |
| src/generators/strategies/monthly-review-strategy.ts | Filters placeholder titles/types/topics out of generated meta keywords. |
| src/generators/deep-analysis-content.ts | Adds AI-marker detection to render “pending” notices instead of raw marker text. |
| src/generators/analysis-builders.ts | Replaces AI markers with computed “why/impact/outlook/consequence/mistake” text across strategies. |
| src/constants/analysis-constants.ts | Adds isAiMarker(), AI_PENDING_CLASS, and isPlaceholderText() helpers. |
| scripts/utils/intelligence-analysis.js | Compiled output reflecting TS stakeholder reasoning changes. |
| scripts/generators/swot-content.js | Compiled output reflecting SWOT empty-state text change. |
| scripts/generators/strategies/weekly-review-strategy.js | Compiled output reflecting keyword placeholder filtering. |
| scripts/generators/strategies/motions-strategy.js | Compiled output reflecting keyword placeholder filtering. |
| scripts/generators/strategies/monthly-review-strategy.js | Compiled output reflecting keyword placeholder filtering. |
| scripts/generators/deep-analysis-content.js | Compiled output reflecting AI-pending rendering changes. |
| scripts/generators/analysis-builders.js | Compiled output reflecting data-driven analysis builder changes. |
| scripts/constants/analysis-constants.js | Compiled output reflecting new marker/placeholder helpers. |
| analysis/methodologies/ai-driven-analysis-guide.md | Updates methodology doc to v4.0, expands quality issues and adds new quality gates. |
| .github/workflows/news-weekly-review.md | Adds checklist items for placeholder keywords and silent zero metrics + viz completeness requirements. |
| .github/workflows/news-week-ahead.md | Adds checklist items for placeholder keywords and silent zero metrics + viz completeness requirements. |
| .github/workflows/news-propositions.md | Adds checklist items for placeholder keywords and silent zero metrics + viz completeness requirements. |
| .github/workflows/news-motions.md | Adds checklist items for placeholder keywords and silent zero metrics + viz completeness requirements. |
| .github/workflows/news-monthly-review.md | Adds checklist items for placeholder keywords and silent zero metrics + viz completeness requirements. |
| .github/workflows/news-month-ahead.md | Adds checklist items for placeholder keywords and silent zero metrics + viz completeness requirements. |
| .github/workflows/news-committee-reports.md | Adds checklist items for placeholder keywords and silent zero metrics + viz completeness requirements. |
| .github/workflows/news-breaking.md | Adds checklist items for placeholder keywords and silent zero metrics + viz completeness requirements. |
| .github/workflows/news-article-generator.md | Adds checklist items for placeholder keywords and silent zero metrics + viz completeness requirements. |
| function aiPendingNotice(message: string): string { | ||
| return `<em class="${AI_PENDING_CLASS}">${escapeHTML(message)}</em>`; | ||
| } |
There was a problem hiding this comment.
aiPendingNotice() returns an that already has AI_PENDING_CLASS, but callers also add AI_PENDING_CLASS to parent
/. Because .ai-analysis-pending adds border-left + ::before hourglass, this results in duplicated icons/borders (nested pending markers) and also applies block-style UI chrome to inline inside tables/lists. Consider returning a plain without the pending class (or use a separate inline-only class), and keep AI_PENDING_CLASS only on the outer block-level container that should show the indicator.
src/generators/analysis-builders.ts
Outdated
| const margin = record.votes.for - record.votes.against; | ||
| const marginDesc = margin > 100 ? 'strong majority' : margin > 30 ? 'moderate majority' : 'narrow margin'; | ||
| consequences.push({ | ||
| action: `Vote on "${record.title}"`, | ||
| consequence: AI_MARKER, | ||
| consequence: `${record.result} by ${marginDesc} (${record.votes.for} for, ${record.votes.against} against, ${record.votes.abstain} abstentions). This outcome shapes the legislative trajectory of this policy area.`, | ||
| severity: record.votes.for > record.votes.against * 2 ? 'high' : 'medium', |
There was a problem hiding this comment.
deriveConsequencesFromVoting() computes marginDesc from votes.for - votes.against but doesn't handle rejected votes: negative margins will always fall into the “narrow margin” bucket, and the wording always implies a majority in favour. Consider using Math.abs(margin) for the size bucket and include the direction (e.g., majority for vs majority against) so consequences read correctly for both adopted and rejected results.
| outcomes.push({ | ||
| actor: pattern.group, | ||
| outcome: 'winner', | ||
| reason: AI_MARKER, | ||
| reason: `${pattern.group} demonstrated strong internal cohesion (${(pattern.cohesion * 100).toFixed(0)}%) with high participation (${(pattern.participation * 100).toFixed(0)}%), enabling effective bloc voting.`, | ||
| }); |
There was a problem hiding this comment.
The JSDoc for deriveStakeholderOutcomesFromVoting() still says “Reasoning text is deferred to the AI agent”, but the function now generates concrete reason strings. Please update the comment to reflect the current behavior so future readers don’t assume outcomes contain AI markers.
|
@copilot apply changes based on the comments in this thread and failing checks |
…ss, Prettier - Fix deriveConsequencesFromVoting(): use Math.abs(margin) for size bucket and include direction (majority in favour vs majority against) so consequences read correctly for both adopted and rejected votes - Fix JSDoc for deriveStakeholderOutcomesFromVoting(): update comment to reflect that function now generates concrete reason strings - Fix double AI_PENDING_CLASS: remove redundant class from <p> tags that already wrap <em class="ai-analysis-pending"> via aiPendingNotice() - Fix Prettier formatting in analysis-builders.ts Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/2367b903-7e00-4047-8365-0095f3c4dc42 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
…bash escaping, SWOT a11y Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/73e967f4-82c8-45a2-8367-cfddf0d20ada Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
All 4 review comments addressed in 045de5f:
Build ✅, lint ✅ (0 errors), 2724 tests passing ✅, Prettier ✅, CodeQL ✅. |
| /* RTL overrides for AI pending indicators */ | ||
| [dir='rtl'] div.ai-analysis-pending, | ||
| [dir='rtl'] section.ai-analysis-pending { | ||
| border-left: none; | ||
| border-right-width: 3px; | ||
| border-right-style: solid; | ||
| border-right-color: var(--accent-warning, #fd7e14); | ||
| padding-left: 0; | ||
| padding-right: 0.75rem; |
There was a problem hiding this comment.
In RTL mode the border-right-color here is always set to --accent-warning (light-mode value). Because this rule comes after the dark-mode @media (prefers-color-scheme: dark) override, it will override the intended --accent-warning-dark color and render the pending indicator border with the wrong color in dark+RTL. Consider overriding border-right-color inside the dark-mode media query after this rule (or moving the dark RTL override below), so dark+RTL uses --accent-warning-dark while still reusing the shared width/style properties.
|
@copilot apply changes based on the comments in this thread |
…er color Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/64c50c3d-bae4-4029-84ff-52a365749737 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Fixed in a511bc1. Added a Build ✅, lint ✅ (0 errors), 2724 tests ✅. |
| /* Dark mode overrides */ | ||
| @media (prefers-color-scheme: dark) { | ||
| div.ai-analysis-pending, | ||
| section.ai-analysis-pending { | ||
| border-left-color: var(--accent-warning-dark, #e08c00); | ||
| } | ||
|
|
||
| [dir='rtl'] div.ai-analysis-pending, | ||
| [dir='rtl'] section.ai-analysis-pending { | ||
| border-left-color: initial; | ||
| border-right-color: var(--accent-warning-dark, #e08c00); | ||
| } |
There was a problem hiding this comment.
AI pending styles only switch to dark colors inside @media (prefers-color-scheme: dark). When the user explicitly toggles dark theme via html[data-theme='dark'] (with a light OS preference), these rules won’t apply and the pending UI falls back to light-mode colors (e.g. --surface-muted → #f8f9fa). Add explicit html[data-theme='dark'] overrides (and RTL equivalents) mirroring the media-query block so manual dark mode renders correctly.
| @media (prefers-color-scheme: dark) { | ||
| .d3-treemap-wrapper svg, | ||
| .d3-network-wrapper svg, | ||
| .d3-swot-chart-wrapper svg { | ||
| background: var(--surface-muted-dark, #161b22); | ||
| } | ||
|
|
||
| .d3-swot-chart-wrapper text, | ||
| .d3-network-wrapper text { | ||
| fill: #c9d1d9 !important; | ||
| } | ||
| } |
There was a problem hiding this comment.
The new D3 visualization background overrides are only applied via @media (prefers-color-scheme: dark). If the site is in explicit dark mode (html[data-theme='dark']) while the OS preference is light, the SVG background will remain the light fallback (#f8f9fa). Add html[data-theme='dark'] overrides for these selectors (and consider RTL if relevant) to keep theme-toggle behavior consistent.
|
@copilot apply changes based on the comments in this thread |
…styles Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/a28deb2e-e3f1-45fc-aefb-56cc42aabfc3 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Both review comments addressed in 0f57e05:
Build ✅, lint ✅ (0 errors), 2724 tests ✅. |
| if (isAiMarker(why)) { | ||
| return ` | ||
| <div class="analysis-why ${AI_PENDING_CLASS}"> | ||
| <h3>${escapeHTML(heading)}</h3> | ||
| <p${langAttr}>${aiPendingNotice('AI political analysis pending — this section will be completed by the editorial intelligence workflow.')}</p> | ||
| </div>`; |
There was a problem hiding this comment.
This AI-pending message is hard-coded in English. The rest of this module localizes user-visible strings via getLocalizedString(DEEP_ANALYSIS_STRINGS, lang), so this notice should be moved into the localized strings table (or otherwise localized) to avoid English-only UI in non-English articles.
| .map((s) => { | ||
| const reasonText = isAiMarker(s.reason) | ||
| ? aiPendingNotice('AI stakeholder analysis pending.') | ||
| : escapeHTML(s.reason); | ||
| return ( | ||
| `<li class="stakeholder-item ${outcomeClass(s.outcome)}">` + | ||
| `<span class="stakeholder-badge">${escapeHTML(outcomeLabel(s.outcome, strings))}</span> ` + | ||
| `<span${contentLangAttr}><strong>${escapeHTML(s.actor)}</strong>: ${escapeHTML(s.reason)}</span>` + | ||
| `<span${contentLangAttr}><strong>${escapeHTML(s.actor)}</strong>: ${reasonText}</span>` + |
There was a problem hiding this comment.
This AI-pending message ("AI stakeholder analysis pending.") is hard-coded in English. Please localize it similarly to other deep-analysis UI strings (getLocalizedString(DEEP_ANALYSIS_STRINGS, lang)) so non-English pages don’t have English-only notices.
| .map((p) => { | ||
| if (isAiMarker(p.text)) { | ||
| return ( | ||
| `<div class="impact-card ${p.css} ${AI_PENDING_CLASS}">` + | ||
| `<h4>${escapeHTML(p.label)}</h4>` + | ||
| `<p${langAttr}>${aiPendingNotice('AI impact analysis pending.')}</p>` + | ||
| `</div>` | ||
| ); |
There was a problem hiding this comment.
The impact-card AI-pending notice text is hard-coded in English. Since deep-analysis headings/labels are localized, this pending notice should also be localized (added to DEEP_ANALYSIS_STRINGS) to keep UI language consistent.
| .map((item) => { | ||
| const consequenceText = isAiMarker(item.consequence) | ||
| ? aiPendingNotice('AI consequence analysis pending.') | ||
| : escapeHTML(item.consequence); | ||
| return ( | ||
| `<tr class="consequence-row severity-${escapeHTML(item.severity)}">` + | ||
| `<td class="action-cell"${langAttr}>${escapeHTML(item.action)}</td>` + | ||
| `<td class="arrow-cell">→</td>` + | ||
| `<td class="consequence-cell"${langAttr}>${escapeHTML(item.consequence)}</td>` + | ||
| `<td class="consequence-cell"${langAttr}>${consequenceText}</td>` + |
There was a problem hiding this comment.
This AI-pending consequence notice text is hard-coded in English. Please localize it alongside the other DEEP_ANALYSIS_STRINGS content so non-English articles remain fully localized when placeholders slip through.
| .map((m) => { | ||
| const altText = isAiMarker(m.alternative) | ||
| ? aiPendingNotice('AI alternative analysis pending.') | ||
| : escapeHTML(m.alternative); | ||
| return ( | ||
| `<div class="mistake-card">` + | ||
| `<p class="mistake-actor"><strong>${escapeHTML(m.actor)}</strong></p>` + | ||
| `<p class="mistake-description"${langAttr}>${escapeHTML(m.description)}</p>` + | ||
| `<p class="mistake-alternative"><em>${escapeHTML(alternativeLabel)}:</em> <span${langAttr}>${escapeHTML(m.alternative)}</span></p>` + | ||
| `<p class="mistake-alternative"><em>${escapeHTML(alternativeLabel)}:</em> <span${langAttr}>${altText}</span></p>` + |
There was a problem hiding this comment.
This AI-pending alternative notice text is hard-coded in English. Please localize it using the same pattern as other deep-analysis strings so non-English pages don’t render English-only fallback notices.
| if (isAiMarker(outlook)) { | ||
| return ` | ||
| <div class="analysis-outlook ${AI_PENDING_CLASS}"> | ||
| <h3>${escapeHTML(heading)}</h3> | ||
| <p${langAttr}>${aiPendingNotice('AI strategic outlook pending — this section will be completed by the editorial intelligence workflow.')}</p> | ||
| </div>`; |
There was a problem hiding this comment.
The outlook AI-pending message is hard-coded in English. This should be localized (e.g., via DEEP_ANALYSIS_STRINGS) to keep the deep-analysis section consistent with the selected UI language.
| `<span class="perspective-impact-badge perspective-impact-${escapeHTML(p.impact)}">${escapeHTML(localizedImpactLabel(p.impact, strings))}</span>` + | ||
| `<span class="perspective-severity-badge severity-${escapeHTML(p.severity)}">${escapeHTML(localizedSeverityLabel(p.severity, strings))}</span>` + | ||
| `</div>` + | ||
| `<p class="perspective-reasoning"${langAttr}>${escapeHTML(p.reasoning)}</p>` + | ||
| `<p class="perspective-reasoning"${langAttr}>${isAiMarker(p.reasoning) ? aiPendingNotice('AI stakeholder perspective analysis pending.') : escapeHTML(p.reasoning)}</p>` + | ||
| evidenceHtml + |
There was a problem hiding this comment.
The stakeholder perspective AI-pending notice is hard-coded in English. Please move this string into the localized deep-analysis strings so non-English pages don’t display English-only fallback messages.
|
@copilot apply changes based on the comments in this thread |
…t.ts Add pendingNotice field to DeepAnalysisStrings interface with translations for all 14 languages. Replace 7 hard-coded English AI-pending messages in buildWhySection, buildStakeholderSection, buildImpactSection, buildConsequencesSection, buildMistakesSection, buildOutlookSection, and buildStakeholderPerspectivesSection with localized strings from DEEP_ANALYSIS_STRINGS. Agent-Logs-Url: https://github.com/Hack23/euparliamentmonitor/sessions/18aad81f-ec92-4225-bbde-e50bedfd975e Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
All 7 review comments addressed in eefad97: Added Build ✅, lint ✅ (0 errors), 2724 tests ✅, Prettier ✅, CodeQL ✅. |
Replaces
[AI_ANALYSIS_REQUIRED]placeholders with deterministic, data-driven political analysis text, improves placeholder handling in rendered HTML/CSS, tightens workflow quality gates, and fixes i18n/a11y issues for non-English articles.Changes Made
[AI_ANALYSIS_REQUIRED]markers.isAiMarker()+AI_PENDING_CLASSrendering path to show styled "analysis pending" notices instead of raw markers, with corresponding CSS styling that correctly distinguishes inline (em.ai-analysis-pending) from block-level container styles to avoid duplicated hourglass icons.isPlaceholderText(). All keyword builders (weekly-review, motions, monthly-review) now filter-then-slice (instead of slice-then-filter) to reliably include up to N real keywords.deriveConsequencesFromVoting()usesMath.abs(margin)for size buckets and includes direction ("majority in favour" vs "majority against") so consequences read correctly for both adopted and rejected results. ExtracteddescribeVoteMargin()helper that handles tied votes (margin === 0) explicitly as "tied vote with no clear majority" and includes direction for all margin buckets including narrow. Severity classification is now bidirectional, usingMath.abs(votesFor - votesAgainst) > totalVotes / 2so strong majorities against are also classified ashigh.contentLang='en'tobuildDeepAnalysisSection()so English analysis text gets correctlangattributes in non-English articles. SWOT empty quadrant fallback uses language-neutral ellipsis instead of hard-coded English.deep-analysis-content.ts(why, stakeholder, impact, consequences, mistakes/alternatives, outlook, and stakeholder perspectives sections) are now localized via a newpendingNoticefield inDEEP_ANALYSIS_STRINGSwith translations for all 14 supported languages. Non-English articles no longer display English-only fallback notices when AI markers slip through.aria-label="No items available"so screen readers announce the empty state meaningfully instead of reading punctuation.[dir='rtl']CSS overrides for.ai-analysis-pendingblock containers, swappingborder-left→border-right,padding-left→padding-right, and repositioning the hourglass icon. Uses individual border properties (width/style/color) instead of shorthand to preserve dark-mode RTLborder-right-coloroverride specificity.html[data-theme='dark']overrides for AI pending indicators (border color, text muted color, impact-card background, and RTL equivalents) and D3 visualization backgrounds/text fills, so manual dark-mode toggle works correctly regardless of OS color scheme preference..dashboard-panel/.dashboard-panel:hoverrules that were overriding the main dashboard styles, unintentionally removing thetransformtransition and changing hover shadow values.<meta name="keywords">content only (not the full HTML body) to avoid false positives on legitimate body text. Fixed over-escaped bash quoting in the meta-keywords extraction snippet.Testing