ARCHITECTURE.md

June 13, 2026 ยท View on GitHub

Hack23 Logo

๐Ÿ›๏ธ EU Parliament Monitor โ€” Architecture

C4 Model Architecture for European Parliament Intelligence Platform
๐Ÿ“ System Context โ€ข ๐Ÿ“ฆ Container View โ€ข ๐Ÿ”ง Component Design

Owner Version Effective Date Review Cycle OpenSSF Best Practices

๐Ÿ“‹ Document Owner: CEO | ๐Ÿ“„ Version: 1.5 | ๐Ÿ“… Last Updated: 2026-05-28 (UTC) | ๐Ÿ“ฆ Release: v0.9.26
๐Ÿ”„ Review Cycle: Quarterly | โฐ Next Review: 2026-08-28


โœ… April-2026 Aggregator-Pipeline Migration โ€” Complete

The April-2026 release migrated from an AI-authored-HTML pipeline to a deterministic aggregator pipeline. Article HTML is now rendered by src/aggregator/** from committed Stage-B analysis artifacts โ€” there is no AI-authored HTML step, no per-article-type strategies, no AI_MARKER/FALLBACK_TEMPLATE sentinel contract, and no src/utils/content-validator.ts / validate-articles.ts runtime validators. The repository still keeps scripts/validate-analysis-completeness.js as the Stage-C artifact-floor checker invoked by prompts and tests.

Canonical references for the current release:

  • ๐ŸŸข Render entry point: src/aggregator/article-generator.ts (CLI: npm run generate-article -- --run <analysis-run-dir>)
  • ๐Ÿ“ฆ Aggregator modules: artifact-order.ts, clean-artifact.ts, analysis-aggregator.ts, markdown-renderer.ts, article-html.ts, article-metadata.ts (5-tier editorial-highlight resolver for <title> / <meta description> โ€” manifest override โ†’ first artefact H1 โ†’ aggregated H1 โ†’ first strong prose โ†’ localized template)
  • ๐Ÿค– Agentic workflows: 14 unified news-<type>.md files (Stages A โ†’ B โ†’ C โ†’ D โ†’ E in one session, covering 14 article types โ€” including the long-horizon quarter-ahead/year-ahead and electoral term-outlook/ election-cycle set added in 2026-Q2) + news-translate.md; the split-family workflows (news-<type>-analysis.md + news-<type>-article.md) and the manual news-article-generator.md helper were deleted
  • ๐Ÿ’ฐ Economic-context enforcement: editorial Stage-C agent-side review over intelligence/economic-context.md (the Wave-2 OR-gate and Wave-3/Wave-4 strict runtime gates in src/utils/content-validator.ts were purged with the rest of the validator layer; enforcement moved to the Stage-C completeness review protocol in .github/prompts/03-analysis-completeness-gate.md and the depth floors in analysis/methodologies/reference-quality-thresholds.json)

The C4 Container and Component diagrams in this document have been rewritten against the post-migration aggregator stack โ€” they no longer reference deleted strategies/builders/content-validator.ts.

This document serves as the primary entry point for the EU Parliament Monitor's architectural documentation. It provides a comprehensive view of the system's design using the C4 model approach, starting from a high-level system context and drilling down to component interactions.

๐Ÿ“š Architecture Documentation Map

DocumentFocusDescriptionDocumentation Link
Architecture๐Ÿ›๏ธ ArchitectureC4 model showing current system structureView Source
Future Architecture๐Ÿ›๏ธ ArchitectureC4 model showing future system structureView Source
Mindmaps๐Ÿง  ConceptCurrent system component relationshipsView Source
Future Mindmaps๐Ÿง  ConceptFuture capability evolutionView Source
SWOT Analysis๐Ÿ’ผ BusinessCurrent strategic assessmentView Source
Future SWOT Analysis๐Ÿ’ผ BusinessFuture strategic opportunitiesView Source
Data Model๐Ÿ“Š DataCurrent data structures and relationshipsView Source
Future Data Model๐Ÿ“Š DataEnhanced European Parliament data architectureView Source
Flowcharts๐Ÿ”„ ProcessCurrent data processing workflowsView Source
Future Flowcharts๐Ÿ”„ ProcessEnhanced AI-driven workflowsView Source
State Diagrams๐Ÿ”„ BehaviorCurrent system state transitionsView Source
Future State Diagrams๐Ÿ”„ BehaviorEnhanced adaptive state transitionsView Source
Security Architecture๐Ÿ›ก๏ธ SecurityCurrent security implementationView Source
Future Security Architecture๐Ÿ›ก๏ธ SecuritySecurity enhancement roadmapView Source
Threat Model๐ŸŽฏ SecuritySTRIDE threat analysisView Source
Classification๐Ÿท๏ธ GovernanceCIA classification & BCPView Source
CRA Assessment๐Ÿ›ก๏ธ ComplianceCyber Resilience ActView Source
Workflowsโš™๏ธ DevOpsCI/CD documentationView Source
Future Workflows๐Ÿš€ DevOpsPlanned CI/CD enhancementsView Source
Business Continuity Plan๐Ÿ”„ ResilienceRecovery planningView Source
Financial Security Plan๐Ÿ’ฐ FinancialCost & security analysisView Source
End-of-Life Strategy๐Ÿ“ฆ LifecycleTechnology EOL planningView Source
Unit Test Plan๐Ÿงช TestingUnit testing strategyView Source
E2E Test Plan๐Ÿ” TestingEnd-to-end testingView Source
Performance Testingโšก PerformancePerformance benchmarksView Source
Security Policy๐Ÿ”’ SecurityVulnerability reporting & security policyView Source

๐Ÿ›ก๏ธ ISMS Policy Alignment

EU Parliament Monitor is developed and maintained in accordance with Hack23 AB's Information Security Management System (ISMS), which is aligned with ISO 27001:2022, NIST CSF 2.0, and CIS Controls v8.1.

Applicable ISMS Policies

PolicyDescriptionRelevance to EU Parliament Monitor
Information Security PolicyEstablishes organization-wide security governance and risk management frameworkDefines overall security posture, risk assessment methodology, and management responsibilities for the project
Secure Development PolicyDefines secure coding standards, code review requirements, and SDLC security gatesMandates security-first development practices: input validation, dependency scanning, SAST/DAST integration, secure CI/CD pipelines
Open Source PolicyGoverns use, contribution, and licensing of open source softwareEnsures compliance with Apache-2.0 License, dependency license compatibility, and transparent open source contribution practices
Classification PolicyDefines data classification scheme (Public, Internal, Confidential, Restricted) and handling requirementsAll project content classified as PUBLIC; establishes data handling controls for any future sensitive data integration
AI PolicyGoverns responsible AI usage, transparency, and human oversight requirementsGoverns LLM usage for content generation: transparency requirements, human review workflows, bias mitigation, prompt injection protection
Access Control PolicyDefines authentication, authorization, least privilege, and privileged access managementControls GitHub repository access, branch protection rules, secret management, and deployment permissions
Cryptography PolicyEstablishes cryptographic standards for data protection (algorithms, key management, TLS)Mandates HTTPS-only content delivery, TLS 1.2+ (TLS 1.3 where supported) for outbound HTTPS API communications; EP MCP integration uses a local stdio JSON-RPC channel (no TLS); ensures secure secret storage for LLM API keys

ISMS Compliance Implementation

ISO 27001:2022 Controls Implemented:

  • A.5.10 - Information Security Policy (documented and reviewed quarterly)
  • A.8.3 - Secure Coding (ESLint security rules, CodeQL SAST scanning)
  • A.8.23 - Web Filtering (planned CSP headers via CloudFront, XSS prevention)
  • A.8.24 - Cryptography (HTTPS-only, TLS 1.2+ / TLS 1.3 where supported, site delivery via CloudFront)
  • A.8.28 - Secure Coding (input validation, dependency scanning)

NIST CSF 2.0 Functions Addressed:

  • Identify (ID): Asset inventory, risk assessment, vulnerability management
  • Protect (PR): Access control, data security, secure development
  • Detect (DE): Security monitoring, vulnerability scanning, anomaly detection
  • Respond (RS): Incident response procedures, GitHub Security Advisories
  • Recover (RC): Business Continuity Plan, backup/restore procedures

CIS Controls v8.1 Implemented:

  • Control 1: Inventory and Control of Enterprise Assets (documented in repo)
  • Control 4: Secure Configuration (branch protection, security policies)
  • Control 6: Access Control Management (GitHub RBAC, least privilege)
  • Control 8: Audit Log Management (GitHub audit logs, workflow logs)
  • Control 10: Malware Defenses (Dependabot, npm audit, CodeQL)
  • Control 16: Application Software Security (SAST, dependency scanning, secure coding)

Compliance Evidence

Evidence of ISMS compliance is maintained through:

  • Policy Documents: All policies stored in Hack23/ISMS-PUBLIC
  • Security Architecture: SECURITY_ARCHITECTURE.md maps controls to implementations
  • Threat Model: THREAT_MODEL.md documents STRIDE analysis and mitigations
  • Classification: CLASSIFICATION.md defines data classification and handling
  • Audit Trail: GitHub audit logs, workflow execution logs, dependency scan reports
  • Security Scanning: CodeQL results, Dependabot alerts, npm audit reports

๐ŸŽฏ System Overview

EU Parliament Monitor is a TypeScript-first static site generator and political intelligence platform that creates multi-language news articles about European Parliament activities. Content is produced by a fleet of 15 agentic GitHub Workflows (gh-aw โ€” 14 unified news-<type>.md covering 14 article types + news-translate.md) that drive AI agents (Claude Opus 4.8 for the 14 article workflows; Claude Sonnet 4.6 for news-translate.md, via GitHub Copilot) through the Stage Aโ†’E protocol, consuming structured data from three data surfaces:

  • European Parliament MCP Server v1.3.20+ (primary โ€” 60+ tools including plenary, MEPs, votes, committees, procedures, adopted texts, sliding-window + fixed-window feeds, analytical tools, and a three-state voting fallback to the EP Open Data Portal)
  • World Bank Open Data MCP (non-economic only โ€” WDI social/health/education/environment/governance indicators)
  • IMF REST (SDMX 3.0) native TypeScript fetch client โ€” primary economic source: WEO + Fiscal Monitor + IFS + BOP + ER + PCPS + GFSR + EREO + FSI + GFS + DOT

TypeScript code handles data acquisition, analysis orchestration, HTML structure, and validation; AI agents author all narrative content under a strict two-pass AI-First Quality regime.

Mission Statement

Enable democratic transparency by providing automated, multilingual coverage of European Parliament activities through a secure, maintainable static site architecture.

Key Characteristics

  • Minimal Runtime Dependencies: Pure static HTML/CSS output with no server-side execution; one pinned production dependency (european-parliament-mcp-server@1.3.20) plus one optional dependency (worldbank-mcp@1.0.1) used only at build time; markdown-it + plugins (markdown-it-anchor, markdown-it-footnote, markdown-it-attrs, markdown-it-deflist) vendored in the aggregator for deterministic artifact rendering
  • TypeScript Source: All source in src/ written in TypeScript 6.0.3 (strict, ESM, "type": "module"), compiled via tsc โ€” rootDir: ./src, outDir: ./scripts, target: ES2025, module: NodeNext
  • Multi-Language Support: Generates content in 14 languages (en, sv, da, no, fi, de, fr, es, nl, ar, he, ja, ko, zh), defined in src/constants/language-core.ts::ALL_LANGUAGES
  • Article Types: 15 registered content types in src/config/article-horizons.ts (ADR-007) โ€” 14 scheduled production types (breaking, committee-reports, election-cycle, month-ahead, month-in-review, motions, propositions, quarter-ahead, quarter-in-review, term-outlook, week-ahead, week-in-review, year-ahead, year-in-review) + deep-analysis (manual/workflow_dispatch only) โ€” each type is a slug, not a strategy module; the aggregator renders the same canonical artifact order for every type and per-type content differences are carried by the Stage-B artifacts themselves
  • Agentic Workflows: 15 unified gh-aw markdown workflows โ€” 14 news-<type>.md article types (Stages A โ†’ B โ†’ C โ†’ D โ†’ E in one session, active-work budget 22โ€“28 min before the single safe-outputs create_pull_request call, 60-min timeout-minutes cap; engine.mcp.session-timeout is intentionally not set โ€” the bundled MCP gateway v0.3.20 rejects the field) + news-translate.md (14-language flush translation, exempt from the single-PR rule) โ€” compiled to .lock.yml via gh aw compile --validate (pinned GH_AW_VERSION: v0.77.0)
  • Analysis-Artifact-Driven Article Pipeline: Agents author the full Stage-B artifact set under analysis/daily/<date>/<slug>/ (or <slug>-run<NN>/ when multiple runs occur on the same date) and commit it. The deterministic aggregator (src/aggregator/**, invoked via npm run generate-article -- --run <analysis-run-dir> for a single run or npm run generate-article:all for batch regen) walks manifest.json, cleans each artifact, and emits the final HTML with the shared site chrome (stacked header + embedded 14-language switcher + TOC sidebar + footer stats) and 14-language hreflang entries. There is no AI-authored HTML step, no strategies, no builders, no section-builders
  • Economic Data (IMF-primary, Wave-4 strict default editorial): IMF REST is the primary source for every economic claim in intelligence/economic-context.md; World Bank MCP provides complementary non-economic context only. Enforcement is editorial at the Stage-C completeness review โ€” the legacy runtime gates (articlePolicyHasEconomicContext, articlePolicyHasIMFEconomicEvidence, isWave3IMFStrictEnabled) in src/utils/content-validator.ts were purged in April-2026; the Stage-C reviewer applies the IMF-required-for-policy rule directly over the committed artifact
  • Quality-Through-Artifact Principle: Mandatory 2-pass iterative improvement during Stage B (~60% pass 1, ~40% pass 2); โ‰ฅ 80 words/SWOT item, โ‰ฅ 150 words/stakeholder perspective, โ‰ฅ 1 Mermaid or Chart.js visualisation per core artifact, 0 [AI_ANALYSIS_REQUIRED] sentinel markers in any committed file (enforced at Stage-C agent-side review against reference-quality-thresholds.json)
  • MCP Integration: Spawned as local child processes via stdio JSON-RPC at build time; inside agentic workflows via the awmg gateway at http://host.docker.internal:8080/mcp/european-parliament
  • Security by Design: Minimal attack surface through static architecture; 5-layer gh-aw security (AWF Squid firewall allowlist, sandboxed Docker, safe-output constraints, JSONL audit trail, lock file compilation); agent prose-injection class of defects eliminated at the root by the aggregator migration (no AI-authored HTML step means no template-prose leak vector)
  • AWS Hosted: AWS S3 + CloudFront (primary, via deploy-s3.yml with OIDC auth); GitHub Pages retained as documented fallback; npm package published to registry.npmjs.org/euparliamentmonitor with SLSA Level 3 provenance

๐Ÿ“Š C4 Model Level 1: System Context Diagram

๐Ÿ‘ค User Focus: Shows how different user types interact with the EU Parliament Monitor system and what external systems it depends on.

๐ŸŒ Integration Focus: Illustrates the relationships with GitHub infrastructure, European Parliament APIs, and LLM services.

C4Context
    title EU Parliament Monitor - System Context Diagram

    Person(citizen, "European Citizen", "Reads news about European Parliament activities in their native language")
    Person(journalist, "Journalist", "Uses site as research source for European political coverage")
    Person(researcher, "Political Researcher", "Analyzes EP activities and trends")
    Person(contributor, "Developer/Contributor", "Maintains and improves the news generation system")

    System(epmonitor, "EU Parliament Monitor", "Static site with multilingual news about European Parliament activities")

    System_Ext(github, "GitHub", "Hosts repository, runs CI/CD (GitHub Actions)")
    System_Ext(aws, "AWS (S3 + CloudFront)", "Serves static site globally via CDN")
    System_Ext(ep_mcp, "European Parliament MCP Server", "Provides structured access to EP data")
    System_Ext(ep_api, "European Parliament APIs", "Official EP data sources (plenary, committees, documents)")
    System_Ext(llm, "LLM Service", "Generates article content from structured EP data")

    Rel(citizen, epmonitor, "Reads news", "HTTPS")
    Rel(journalist, epmonitor, "Researches stories", "HTTPS")
    Rel(researcher, epmonitor, "Analyzes data", "HTTPS")
    Rel(contributor, github, "Contributes code", "Git/HTTPS")

    Rel(epmonitor, github, "Built and deployed via", "GitHub Actions")
    Rel(epmonitor, aws, "Hosted on", "S3 + CloudFront")
    Rel(github, epmonitor, "Generates site via", "GitHub Actions")
    Rel(epmonitor, ep_mcp, "Fetches EP data via", "MCP Protocol")
    Rel(ep_mcp, ep_api, "Queries EP data", "HTTPS/JSON")
    Rel(epmonitor, llm, "Generates content via", "API/SDK")

    UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2")

Context Diagram - Key Elements

ElementTypeDescriptionTechnology
European CitizenUserPrimary audience seeking EP news in native languageWeb Browser
JournalistUserProfessional using site for research and story developmentWeb Browser
Political ResearcherUserAcademic or analyst studying EP activitiesWeb Browser
Developer/ContributorUserMaintainer improving systemGit, Node.js, VS Code
EU Parliament MonitorSystemCore static site generatorNode.js, TypeScript
GitHubExternal SystemSource control, CI/CDGitHub Actions
EP MCP ServerExternal SystemStructured EP data accessMCP Protocol, TypeScript
EP APIsExternal SystemOfficial data sourcesREST APIs, JSON
LLM ServiceExternal SystemContent generationAPI (OpenAI/Anthropic/etc.)

Trust Boundaries and Security Zones

graph TB
    subgraph "Public Internet - Untrusted Zone"
        Users["Web Users\nCitizens, Journalists, Researchers"]
    end
    
    subgraph "GitHub Infrastructure - Trusted Zone"
        subgraph "Build Environment"
            Actions["GitHub Actions Runner\nGitHub-hosted Ubuntu runner\nubuntu-latest + Node.js 26"]
            EPServer["European Parliament\nMCP Server\nLocal process, stdio JSON-RPC"]
        end
        
        subgraph "Source Control"
            Repo["Git Repository\nVersion Control"]
        end
    end
    
    subgraph "AWS Hosting - Cloud Infrastructure Zone"
        Pages["AWS S3 + CloudFront CDN\nHTTPS via ACM"]
    end
    
    subgraph "External Services - Partially Trusted Zone"
        EPAPI["European Parliament\nOfficial APIs"]
        LLM["LLM Service\nOpenAI/Anthropic"]
    end
    
    Users -->|"HTTPS GET\nRead-Only"| Pages
    Actions -->|"Spawns locally\nstdio JSON-RPC"| EPServer
    EPServer -->|"HTTPS/JSON\nData Queries"| EPAPI
    Actions -->|"API Calls\nContent Gen"| LLM
    Actions -->|"Git Push\nAuthenticated"| Repo
    Actions -->|"S3 Sync + CF Invalidation\nAuthenticated via OIDC"| Pages
    
    classDef users fill:#CE93D8,stroke:#6A1B9A,stroke-width:2px,color:#000000
    classDef hosting fill:#A5D6A7,stroke:#2E7D32,stroke-width:2px,color:#000000
    classDef actions fill:#90CAF9,stroke:#1565C0,stroke-width:2px,color:#000000
    classDef external fill:#FFE082,stroke:#F57C00,stroke-width:2px,color:#000000

    class Users users
    class Pages hosting
    class Actions actions
    class EPServer,EPAPI,LLM external

Trust Boundary Analysis:

ZoneTrust LevelSecurity ControlsThreat Model
Public InternetUntrustedHTTPS-only, planned CSP headers, static content onlyDDoS, XSS attempts (mitigated by static architecture)
GitHub InfrastructureTrustedGitHub authentication, branch protection, optional signed commits, secret scanningSupply chain attacks (mitigated by Dependabot, CodeQL)
AWS HostingTrustedACM certificate, HTTPS redirect, DDoS protection via CloudFrontHosting infrastructure compromise (mitigated by AWS security controls, OIDC deploy auth)
External ServicesPartially TrustedAPI authentication, basic input parsing/shape validation; planned systematic sanitization/escaping and rate limitingData poisoning, API compromise (mitigated by validation, monitoring, planned hardening)

Key Security Boundaries:

  1. User โ†’ CloudFront: Read-only HTTPS access, no authentication required (public content)
  2. GitHub Actions โ†’ External APIs: Authenticated API calls, input validation, error handling
  3. GitHub Actions โ†’ AWS S3: Authenticated S3 sync + CloudFront invalidation, only static files deployed
  4. External Services โ†’ System: Data parsed and basic shape-validated before use; comprehensive sanitization/escaping and rate limiting are planned controls

๐Ÿ“ฆ C4 Model Level 2: Container Diagram

๐Ÿ“ฆ Container Focus: Major containers (applications, data stores, MCP clients) of the post-April-2026 aggregator pipeline.

๐Ÿ”„ Data Flow Focus: How agentic workflows produce analysis artifacts and how the deterministic aggregator renders them into 14-language HTML.

%%{init: {"theme":"dark","themeVariables":{"primaryColor":"#1565C0","primaryTextColor":"#fff","lineColor":"#90CAF9","fontFamily":"Inter, Helvetica, Arial, sans-serif"}}}%%
C4Container
    title EU Parliament Monitor โ€” Container Diagram (April-2026 aggregator pipeline)

    Person(user, "Reader", "Reads multilingual EP news at euparliamentmonitor.com")
    Person(contributor, "Contributor", "Maintains code, methodologies, translations")
    Person(researcher, "Researcher / Journalist", "Audits analysis artifacts via the Political Intelligence Hub")

    Container_Boundary(epmonitor, "EU Parliament Monitor") {
        Container(aw_orchestrator, "gh-aw Orchestrator", "Agentic Workflows (Claude Opus 4.8 articles / Claude Sonnet 4.6 translate)", "15 agentic workflows: 14 unified news-<type>.md + news-translate.md")
        Container(prompt_lib, "Prompt Library", "10 bounded contexts", ".github/prompts/00-scope โ€ฆ 09-troubleshooting; lint:prompts drift-guard")
        Container(methodology_lib, "Methodology Library", "Markdown methodologies + JSON thresholds", "19 methodologies + reference-quality-thresholds.json (analysis/methodologies/)")
        Container(template_lib, "Template Library", "60 Markdown templates", "60 top-level content templates + 6 partial fragments under _partials/ (analysis/templates/)")
        ContainerDb(analysis_runs, "Analysis Runs", "Markdown + JSON", "analysis/daily/YYYY-MM-DD/<type>/{manifest.json,intelligence/,classification/,risk-scoring/,threat-assessment/,documents/,extended/}")
        Container(aggregator, "Aggregator (5 modules)", "TypeScript", "src/aggregator/**: artifact-order ยท clean-artifact ยท analysis-aggregator ยท markdown-renderer ยท article-html ยท article-metadata ยท article-generator (CLI)")
        Container(ep_client, "EP MCP Client", "TypeScript", "Stdio JSON-RPC to european-parliament-mcp-server@1.3.20+; 60+ tools; getVotingRecordsWithFallback() to EP Open Data Portal (src/mcp/ep-mcp-client.ts)")
        Container(wb_client, "World Bank MCP Client", "TypeScript", "WORLD_BANK_MCP_TOOLS โ€” non-economic indicators only (src/mcp/wb-mcp-client.ts)")
        Container(imf_client, "IMF REST Client", "TypeScript", "IMF_MCP_TOOLS โ€” primary economic source: WEO/Fiscal Monitor/IFS/BOP/ER/PCPS (src/mcp/imf-mcp-client.ts)")
        Container(stage_c_review, "Stage-C Review", "Editorial agent + thresholds", "Reads .github/prompts/03-analysis-completeness-gate.md + reference-quality-thresholds.json โ€” replaces purged content-validator.ts")
        Container(news_indexes, "News Indexes & Sitemap", "TypeScript", "Per-language index pages + sitemap.xml + sitemap_<lang>.html (src/generators/news-indexes.ts, sitemap.ts)")
        ContainerDb(static_files, "Static Site Output", "HTML/CSS/JS/JSON", "news/<slug>-<lang>.html (14 langs) ยท news/<slug>.en.md ยท article.md per run ยท sitemap.xml ยท articles-metadata.json")
    }

    Container_Boundary(github_infra, "GitHub Infrastructure") {
        Container(actions, "GitHub Actions", "CI/CD + gh-aw runtime", "15 news + ~15 standard workflows; SHA-pinned actions; OpenSSF Scorecard")
        ContainerDb(repo, "Git Repository", "Version control", "Source + analysis runs + generated content; SLSA L3 provenance")
    }

    Container_Boundary(aws_infra, "AWS Infrastructure") {
        Container(cf_s3, "CloudFront + S3", "CDN / object storage", "Primary hosting ยท ACM HTTPS ยท OIDC GithubWorkFlowRole ยท cache: HTML 1h, immutable assets 1y")
    }

    System_Ext(ep_mcp, "European Parliament MCP Server v1.3.20+", "60+ tools โ€” plenary, voting, motions, committee, MEPs, declarations, procedures, analytical (voting-anomaly, coalition, MEP-influence)")
    System_Ext(ep_open_data, "EP Open Data Portal", "https://data.europarl.europa.eu โ€” voting-records fallback (/api/v2/decision)")
    System_Ext(wb_mcp, "World Bank Open Data MCP", "Non-economic WDI indicators (health, education, environment, governance)")
    System_Ext(imf_api, "IMF SDMX 3.0 REST", "https://dataservices.imf.org/REST/SDMX_3.0/")
    System_Ext(copilot, "GitHub Copilot / Claude Opus 4.8 (articles) ยท Claude Sonnet 4.6 (translate)", "Authors analysis Markdown under 2-pass AI-First Quality regime โ€” never authors HTML")

    Rel(user, cf_s3, "Reads HTML in 14 langs", "HTTPS")
    Rel(researcher, repo, "Audits analysis/daily/", "Git/HTTPS")
    Rel(contributor, repo, "Commits code + methodologies", "Git/HTTPS")
    Rel(actions, aw_orchestrator, "Triggers on schedule / manual", "gh-aw engine")
    Rel(aw_orchestrator, copilot, "Delegates analysis authoring", "Copilot CLI")
    Rel(aw_orchestrator, prompt_lib, "Imports prompts", "Markdown")
    Rel(aw_orchestrator, methodology_lib, "Reads methodologies", "Markdown")
    Rel(aw_orchestrator, template_lib, "Fills templates", "Markdown")
    Rel(aw_orchestrator, ep_client, "Stage A โ€” fetch", "fn")
    Rel(aw_orchestrator, wb_client, "Stage A โ€” context (optional)", "fn")
    Rel(aw_orchestrator, imf_client, "Stage A โ€” economic context", "fn")
    Rel(aw_orchestrator, analysis_runs, "Stage B โ€” write artifacts", "fs.write")
    Rel(aw_orchestrator, stage_c_review, "Stage C โ€” completeness gate", "agent review")
    Rel(stage_c_review, analysis_runs, "Reads + grades", "fn")
    Rel(aw_orchestrator, aggregator, "Stage D โ€” npm run generate-article", "CLI")
    Rel(aggregator, analysis_runs, "Reads manifest.json + artifacts", "fs.read")
    Rel(aggregator, static_files, "Writes 14 HTML + Markdown", "fs.write")
    Rel(news_indexes, static_files, "Writes index pages", "fs.write")
    Rel(ep_client, ep_mcp, "stdio JSON-RPC", "MCP")
    Rel(ep_client, ep_open_data, "Voting fallback", "HTTPS REST")
    Rel(wb_client, wb_mcp, "stdio JSON-RPC", "MCP")
    Rel(imf_client, imf_api, "HTTPS / SDMX", "REST")
    Rel(static_files, repo, "Stage E โ€” single PR", "Git")
    Rel(actions, cf_s3, "Deploy via OIDC", "S3 sync + CloudFront invalidation")

    UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2")

Container Diagram โ€” Key Elements

๐Ÿงฑ Containerโš™๏ธ Technology๐ŸŽฏ Purpose๐Ÿ”„ Data flow
๐Ÿค– gh-aw OrchestratorClaude Opus 4.8 (articles) / Claude Sonnet 4.6 (translate) + gh-aw v0.77.0Runs 15 agentic workflows (14 article + 1 translate); produces analysis artifactsTriggers via cron / manual; commits one PR per article run
๐Ÿ“š Prompt / Methodology / Template librariesMarkdown + JSONBounded-context prompts (10), methodologies (19), templates (60 top-level + 6 _partials/)Read by every agentic workflow at start-of-session
๐Ÿง  Analysis RunsMarkdown + JSONPer-run intelligence tree under analysis/daily/<date>/<type>/Written by Stage B agents; consumed by Stage C and aggregator
๐ŸŸข Aggregator (5 modules)TypeScriptReads manifest.json and Markdown artifacts; renders 14-language HTML deterministicallynpm run generate-article -- --run <dir>
๐Ÿ”Œ EP MCP ClientTypeScript60+ EP tools + voting fallback to EP Open Data Portal /api/v2/decisionStage A data collection
๐Ÿ’ฐ IMF / ๐ŸŒฑ World Bank ClientsTypeScriptEconomic context (IMF) + non-economic indicators (WB)Stage A wave-2 context
โš–๏ธ Stage-C ReviewEditorial agent + JSON thresholdsPer-artifact line floors + tradecraft signals (Admiralty / WEP / ICD-203)Replaces the purged content-validator.ts runtime gate
๐ŸŒ News Indexes & SitemapTypeScript14-language index pages, sitemap.xml, hreflang alternatesnpm run prebuild
๐Ÿ“ฆ Static Site OutputHTML / CSS / JS / JSON / MarkdownPublic deliverable: news pages + article.md source per runCommitted to main, deployed to S3
๐Ÿš€ GitHub ActionsCI/CD + gh-aw15 news + ~15 standard workflowsDaily news + on-PR validation
โ˜๏ธ CloudFront + S3CDN / object storagePrimary hosting via OIDC GithubWorkFlowRoleHTTPS + immutable asset cache

Security Responsibilities per Container

ContainerSecurity responsibilityImplementationControls
๐Ÿค– gh-aw OrchestratorSandboxed AWF runtime, Squid egress allowlist, capability-bounded safe outputsRuns in GitHub-hosted ephemeral VMs; safe-outputs.create-pull-request.max: 1; step-security/harden-runner egress blockA.5.10, A.8.28 (ISO 27001), CIS 16
๐ŸŸข AggregatorDeterministic Markdownโ†’HTML; explicit markdown-it plugin allowlist; clean-artifact.ts strips SPDX/banners; script-src 'self' CSPNo AI-authored HTML; vendored Mermaid/Chart.js/D3 under js/vendor/A.8.23, A.8.28 (ISO 27001), OWASP A03
๐Ÿ”Œ EP MCP ClientLocal stdio JSON-RPC; per-request timeout + retry backoff; envelope validation; voting-records three-state fallbacksafeCallTool() + callToolWithRetry() wrappers in ep-mcp-client.tsA.8.24 (ISO 27001), CIS 16
๐Ÿง  Analysis ArtifactsTradecraft grading (Admiralty A1โ€“F6 + WEP) + provenance manifestmanifest.json cross-reference map; methodology-reflection.md auditA.5.12 (ISO 27001)
๐Ÿ“ฆ Static FilesPublic-data only; integrity via Git + SLSA L3 attestationAll EP/IMF/WB content is public; SBOM, REUSE licence headersA.5.10 (ISO 27001)
๐Ÿš€ GitHub ActionsOIDC for AWS deploy; Secrets at job-scope; SHA-pinned third-party actionsGithubWorkFlowRole IAM with least privilege; harden-runner egress allowlistA.8.3, CIS 6
โ˜๏ธ CloudFront + S3HTTPS-only via ACM; bucket policy denies public ACLs; CloudFront cache-control by file classLong-cache immutable assets, short-cache HTMLA.13.1, A.5.23 (ISO 27001)
Amazon CloudFront + S3HTTPS-only, CDN security, DDoS protectionForces HTTPS redirect via ACM certificate, CloudFront with DDoS mitigation, HSTS headers (configured externally in CloudFront distribution)A.8.24 (ISO 27001)
Git RepositoryAccess control, branch protection, signed commitsRBAC with least privilege, protected main branch, optional signed commitsCIS Control 6, A.8.3

Container Security Architecture

graph TB
    subgraph "Generation Layer - Build Time Security"
        NewsGen["News Generator\nInput Validation\nData Sanitization"]
        MCPClient["MCP Client\nLocal stdio JSON-RPC\nConnection Retry\nRequest Timeout"]
        Template["Template Engine\nXSS Prevention\nCSP Generation\nHTML Sanitization"]
    end
    
    subgraph "Storage Layer - Version Control Security"
        GitRepo["Git Repository\nBranch Protection\nCode Review\nAudit Logs"]
        Secrets["GitHub Secrets\nEncrypted Storage\nLeast Privilege"]
    end
    
    subgraph "Delivery Layer - Runtime Security"
        Pages["Amazon CloudFront + S3\nHTTPS-Only\nHSTS Headers\nDDoS Protection"]
        CDN["CloudFront Edge\nTLS Termination\nEdge Caching\nGeographic Distribution"]
    end
    
    subgraph "External Layer - Third-Party Security"
        EPMCP["EP MCP Server\nMCP Protocol\nData Validation"]
        LLM["LLM Service\nAPI Key Auth\nPrompt Injection Prevention"]
    end
    
    NewsGen -->|Validated Data| Template
    NewsGen -->|"Spawns locally via stdio"| MCPClient
    MCPClient -->|JSON-RPC| EPMCP
    NewsGen -->|Secured API Calls| LLM
    Template -->|Safe HTML| GitRepo
    Secrets -->|Inject at Runtime| NewsGen
    GitRepo -->|Deploy to S3| Pages
    Pages -->|Cached Content| CDN
    
    classDef generation fill:#90CAF9,stroke:#1565C0,stroke-width:2px,color:#000000
    classDef storage fill:#A5D6A7,stroke:#2E7D32,stroke-width:2px,color:#000000
    classDef delivery fill:#FFCC80,stroke:#F57C00,stroke-width:2px,color:#000000
    classDef external fill:#CE93D8,stroke:#6A1B9A,stroke-width:2px,color:#000000

    class NewsGen,Template,MCPClient generation
    class GitRepo,Secrets storage
    class Pages,CDN delivery
    class EPMCP,LLM external

๐Ÿงฉ C4 Model Level 3: Component Diagram โ€” Aggregator Pipeline

๐Ÿ”ง Component Focus: Internal components of the deterministic aggregator (src/aggregator/**) and supporting MCP / methodology modules.

๐ŸŽฏ Responsibility Focus: How analysis Markdown artifacts produced by the agentic workflows become 14-language HTML deliverables.

%%{init: {"theme":"dark","themeVariables":{"primaryColor":"#1565C0","primaryTextColor":"#fff","lineColor":"#90CAF9","fontFamily":"Inter, Helvetica, Arial, sans-serif"}}}%%
C4Component
    title EU Parliament Monitor โ€” Aggregator Components (post-April-2026)

    Container_Boundary(aggregator_c, "Aggregator (src/aggregator/)") {
        Component(article_generator, "article-generator.ts", "TypeScript CLI", "Entry point: npm run generate-article -- --run <dir>; walks manifest.json")
        Component(artifact_order, "artifact-order.ts", "TypeScript", "ARTIFACT_SECTIONS โ€” canonical 19-section order")
        Component(clean_artifact, "clean-artifact.ts", "TypeScript", "Strips SPDX/banner/provenance front matter from each artifact before merge")
        Component(analysis_aggregator, "analysis-aggregator.ts", "TypeScript", "aggregateAnalysisRun() โ€” filters manifestFiles to .md only excluding data/runs/pass1; emits Provenance & Audit block at END")
        Component(markdown_renderer, "markdown-renderer.ts", "TypeScript", "markdown-it + plugins (anchor, footnote, attrs, deflist); explicit allowlist; renderMarkdown()")
        Component(article_html, "article-html.ts", "TypeScript", "HTML5 wrapper: stacked header, language switcher, TOC sidebar, JSON-LD NewsArticle, isBasedOn provenance, hreflang alternates, footer")
        Component(article_metadata, "article-metadata.ts", "TypeScript", "5-tier editorial-highlight resolver for <title>/<meta description>: manifest override โ†’ first-artifact H1 โ†’ aggregated H1 โ†’ first strong prose โ†’ localized template")
    }

    Container_Boundary(mcp_c, "MCP & Data Clients (src/mcp/)") {
        Component(ep_client, "ep-mcp-client.ts", "TypeScript", "60+ tools; safeCallTool + callToolWithRetry; recess-mode detection; slow-feed warnings")
        Component(ep_open_data, "ep-open-data-client.ts", "TypeScript", "EPOpenDataClient + getVotingRecordsWithFallback() three-state fallback")
        Component(wb_client, "wb-mcp-client.ts", "TypeScript", "WORLD_BANK_MCP_TOOLS โ€” non-economic indicators")
        Component(imf_client, "imf-mcp-client.ts", "TypeScript", "class IMFMCPClient + IMF_MCP_TOOLS; native fetch SDMX 3.0; primary economic source")
        Component(mcp_health, "mcp-health.ts / mcp-retry.ts / mcp-connection.ts", "TypeScript", "Health probes, retry backoff, connection lifecycle")
    }

    Container_Boundary(intel_c, "Intelligence Utilities (src/utils/, src/generators/)") {
        Component(political_classification, "political-classification.ts", "TypeScript", "7-dimension EP event classification")
        Component(political_threat, "political-threat-assessment.ts", "TypeScript", "5-framework political threat (Landscape 6D + Attack Trees + Kill Chain + Diamond + ICO)")
        Component(political_risk, "political-risk-assessment.ts", "TypeScript", "5ร—5 Likelihood ร— Impact scoring")
        Component(significance, "significance-scoring.ts", "TypeScript", "Publication priority score per artifact")
        Component(quality_scorer, "article-quality-scorer.ts", "TypeScript", "Editorial quality signals")
        Component(news_indexes, "news-indexes.ts + sitemap.ts", "TypeScript", "14-language indexes + sitemap.xml + per-language sitemap_<lang>.html")
    }

    Container_Boundary(scripts_c, "Workflow Scripts (scripts/aggregator/)") {
        Component(prior_run_diff, "prior-run-diff.js", "Node.js", "Re-run improve/extend helper; classifies prior-run artifacts as must-extend (carryForward[]) or rewrite; always-on (no env flag); emits priorRunDiff JSON with priorLines+extendFloor")
        Component(forward_statements, "forward-statements-registry.js", "Node.js", "Forward-looking-statement JSONL registry; week/month-ahead seeding")
        Component(checkpoint, "checkpoint-analysis-to-memory.sh", "Bash", "Pre-audited helper; replaces inline expansion-heavy bash in workflows (shell-safety)")
    }

    System_Ext(ep_mcp, "EP MCP Server v1.3.20+", "60+ tools โ€” plenary, voting, motions, committee, MEPs, declarations, procedures, analytical")
    System_Ext(ep_portal, "EP Open Data Portal", "/api/v2/decision โ€” voting fallback")
    System_Ext(wb_mcp, "World Bank Open Data MCP", "Non-economic WDI")
    System_Ext(imf_api, "IMF SDMX 3.0", "WEO / FM / IFS / BOP / ER / PCPS")
    ContainerDb(analysis_dir, "analysis/daily/<date>/<type>/", "Markdown + JSON", "manifest.json + intelligence/ + classification/ + risk-scoring/ + threat-assessment/ + extended/")
    ContainerDb(news_dir, "news/<slug>(-<lang>).{md,html}", "Markdown + HTML", "Per-language deliverables")

    Rel(article_generator, analysis_dir, "reads manifest.json", "fs.readFileSync")
    Rel(article_generator, artifact_order, "uses ARTIFACT_SECTIONS", "import")
    Rel(article_generator, clean_artifact, "cleans each artifact", "fn")
    Rel(article_generator, analysis_aggregator, "aggregateAnalysisRun()", "fn")
    Rel(analysis_aggregator, markdown_renderer, "renderMarkdown()", "fn")
    Rel(markdown_renderer, article_html, "wraps in HTML5 chrome", "fn")
    Rel(article_html, article_metadata, "5-tier metadata resolver", "fn")
    Rel(article_html, news_dir, "writes 14 HTML + 1 .md", "fs.writeFileSync")
    Rel(news_indexes, news_dir, "writes index pages + sitemaps", "fs.writeFileSync")

    Rel(ep_client, ep_mcp, "stdio JSON-RPC", "MCP")
    Rel(ep_open_data, ep_portal, "voting fallback", "HTTPS")
    Rel(wb_client, wb_mcp, "stdio JSON-RPC", "MCP")
    Rel(imf_client, imf_api, "HTTPS/SDMX", "REST")
    Rel(ep_client, mcp_health, "health + retry", "fn")

    Rel(prior_run_diff, analysis_dir, "carry-forward plan", "JSON")
    Rel(forward_statements, analysis_dir, "JSONL registry seeding", "fs")

    UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2")

Component Diagram โ€” Key Elements

๐Ÿงฉ Component๐ŸŽฏ Responsibility๐Ÿ”— Dependencies๐Ÿ“‚ File location
๐ŸŸข Aggregator pipelineDiscover manifest.json โ†’ clean artifacts โ†’ aggregate (19-section canonical order, Provenance & Audit at end, .md only excluding data/runs/pass1/) โ†’ render Markdown โ†’ wrap HTML with TOC sidebar + shared chrome + progressive disclosure layers (quick/analysis/intelligence) + per-layer reading-time estimates + anchor-aware auto-expansion runtime โ†’ write <slug>.en.md + 14 <slug>-<lang>.htmlmarkdown-it + markdown-it-anchor/-footnote/-attrs/-deflistsrc/aggregator/{article-generator,analysis-aggregator,markdown-renderer,article-html,artifact-order,clean-artifact,article-metadata,progressive-disclosure}.ts
๐Ÿง  Analysis artifacts60 top-level content templates under analysis/daily/<date>/<type>/ with manifest.json declaring articleType + files map. 3-variant manifest schema (articleType / articleTypes[] / legacy runType) handled by resolveArticleTypeFromManifest(). Optional dataMode (full/title-only/degraded-imf/degraded-voting/minimal) applies depth-floor reduction factors for structurally constrained runs (see DATA_MODEL.md ยง Reference Quality Thresholds)19 methodologies (10-step protocol, Rules 1โ€“22)analysis/methodologies/*.md, analysis/templates/**, analysis/daily/**
๐Ÿ”Œ EP MCP Client60+ EP tools via stdio JSON-RPC; safeCallTool() + callToolWithRetry() wrappers; recess-mode detection ([1952,2100] year window); slow-feed warning downgrade for get_events_feedeuropean-parliament-mcp-server@1.3.20+ (PR #405 normalises political-group codes)src/mcp/ep-mcp-client.ts
๐Ÿ—ณ๏ธ EP Open Data fallbackThree-state voting fallback: (a) MCP has data โ†’ use it ยท (b) MCP empty โ†’ query /api/v2/decision ยท (c) both empty โ†’ ๐Ÿ”ด unavailability marker via virtual tool name ep-get-voting-recordsEP Open Data Portalsrc/mcp/ep-open-data-client.ts (see getVotingRecordsWithFallback())
๐Ÿ’ฐ IMF Clientclass IMFMCPClient + IMF_MCP_TOOLS; primary economic source per IMF Indicator Mapping; native Node 26 fetch SDMX 3.0; env IMF_API_BASE_URL, IMF_API_TIMEOUT_MSNone (REST)src/mcp/imf-mcp-client.ts
๐ŸŒฑ World Bank ClientWORLD_BANK_MCP_TOOLS; non-economic WDI indicators only (health, education, environment, governance, innovation)worldbank-mcp (optional)src/mcp/wb-mcp-client.ts
โš–๏ธ Stage-C completeness gateEditorial agent-side review against .github/prompts/03-analysis-completeness-gate.md and analysis/methodologies/reference-quality-thresholds.json line floors. Replaces the purged runtime content-validator.tsMethodology library + per-artifact thresholds.github/prompts/03-โ€ฆ, analysis/methodologies/reference-quality-thresholds.json
๐Ÿ” Prior-Run DiffRe-run improve/extend helper; classifies prior-run artifacts as must-extend (carryForward[]) or below-floor rewrite; always-on (no env flag); emits priorRunDiff JSON with priorLines+extendFloor consumed by Stage B and Stage Cโ€”scripts/aggregator/prior-run-diff.js
๐Ÿ“œ Forward-statements registryCanonical last-occurrence-per-id JSONL registry; week/month-ahead seeds data/forward-statements-open.json; Stage C enforces a "carried-forward forward statements" section when open items existJSONL registryscripts/aggregator/forward-statements-registry.js, analysis/forward-statements/
๐Ÿ›ก๏ธ Shell-safety helperPre-audited bash helper for checkpoint-to-memory; replaces expansion-heavy inline workflow bash that the sandbox shell-safety filter would blockBashscripts/checkpoint-analysis-to-memory.sh
๐Ÿง  Intelligence utilitiespolitical-classification (7D), political-threat-assessment (5-framework), political-risk-assessment$ (5 \times 5 \text{L} \times \text{I}), $significance-scoring, article-quality-scorerTypessrc/utils/*.ts
๐ŸŒ News Indexes & SitemapPer-language news index pages, sitemap.xml, per-language sitemap_<lang>.html, hreflang alternatesMetadata, file-utilssrc/generators/news-indexes.ts, src/generators/sitemap.ts
๐Ÿ”ข ConstantsALL_LANGUAGES (14), LANGUAGE_PRESETS (all, eu-core, nordic), article-type slugs, committee indicator mapโ€”src/constants/*.ts

Component Interaction Patterns

sequenceDiagram
    autonumber
    participant CLI as article-generator CLI
    participant AGG as analysis-aggregator
    participant AO as artifact-order
    participant CA as clean-artifact
    participant MR as markdown-renderer
    participant AH as article-html
    participant AM as article-metadata
    participant FS as File System

    CLI->>AGG: aggregateAnalysisRun(runDir)
    AGG->>AGG: read manifest.json โ†’ resolveArticleType()
    AGG->>AO: get ARTIFACT_SECTIONS (19 canonical sections)
    loop For each section in ARTIFACT_SECTIONS
        AGG->>CA: cleanArtifact(rawMd) โ€” strip SPDX/banners
        CA-->>AGG: cleaned Markdown
    end
    AGG-->>CLI: AggregatedRun { markdown, sectionToc, includedArtifacts }

    CLI->>AM: resolveArticleMetadata(manifest) โ€” 5-tier resolver
    AM-->>CLI: ResolvedMetadata { title, description }

    CLI->>MR: renderMarkdown(aggregatedMarkdown)
    MR-->>CLI: RenderedMarkdown { html, toc, mermaidCount }

    loop For each language (14 languages)
        CLI->>AH: wrapArticleHtml(body, lang, toc, metadata)
        AH-->>CLI: complete HTML5 document
        CLI->>FS: writeFileSync(news/<slug>-<lang>.html)
    end

    CLI->>FS: writeFileSync(news/<slug>.en.md)
    CLI->>FS: writeFileSync(analysis/daily/<date>/<type>/article.md)
    CLI->>FS: writeFileSync(analysis/daily/<date>/<type>/article-meta.json)
    CLI-->>CLI: GenerateResult

Component Collaboration Patterns

PatternComponents InvolvedPurposeImplementation
Manifest-Driven Aggregationarticle-generator.ts โ†’ manifest/reader.ts โ†’ analysis-aggregator.tsDiscover and aggregate analysis artifacts in canonical orderReads manifest.json from run dir; resolves articleType (3-variant schema); filters to .md artifacts; emits Provenance block
19-Section Canonical Orderartifact-order.ts โ†’ analysis-aggregator.tsDeterministic article structure matching intelligence-product layoutARTIFACT_SECTIONS defines section IDs, titles, and artifact paths; unmatched artifacts go to "Supplementary Intelligence" bucket
5-Tier Metadata Resolutionarticle-metadata.tsResolve <title> and <meta description> from multiple sourcesmanifest override โ†’ first-artifact H1 โ†’ aggregated H1 โ†’ first strong prose line โ†’ localized template fallback, then hard reject of pipeline-jargon tokens (Stage Aโ€ฆStage E, scripts/, prefetch/timestamps/run markers) with synthesized reader-facing fallback
Key Takeaways Extractionkey-takeaways.ts โ†’ analysis-aggregator.tsAuto-extract 3โ€“7 bullet key takeawaysHarvests from intelligence/synthesis-summary.md + intelligence/intelligence-assessment.md; inserted after Executive Brief
MCP Connection Retry with Backoffmcp-retry.ts โ†’ mcp-connection.ts โ†’ ep-mcp-client.tsHandle transient MCP connection failurescallToolWithRetry() with exponential backoff; safeCallTool() catches errors and returns typed fallback payloads
Three-State Voting Fallbackep-mcp-client.ts โ†’ ep-open-data-client.tsEnsure voting data availability(a) MCP has data โ†’ use it ยท (b) MCP empty โ†’ query EP Open Data /api/v2/decision ยท (c) both empty โ†’ unavailability marker
Reader Intelligence Guidereader-intelligence-guide.ts โ†’ article-generator.tsPrepend methodology transparency panelCollapsible HTML panel prepended to article body; provides tradecraft context to readers
Run Discovery & Collision Groupingruns/discover.ts โ†’ runs/grouping.ts โ†’ article-generator.tsBatch mode (--all) discovers all valid runsWalks analysis/daily/**\/manifest.json; groups by date+type for collision detection; renders each independently

๐Ÿ”„ Deployment Diagram

โ˜๏ธ Infrastructure Focus: Shows how the system is deployed on GitHub infrastructure.

๐Ÿš€ CI/CD Focus: Illustrates the automated deployment pipeline.

C4Deployment
    title EU Parliament Monitor - Deployment Diagram

    Deployment_Node(github_cloud, "GitHub Cloud", "GitHub Infrastructure") {
        Deployment_Node(actions_runner, "GitHub Actions Runner", "Ubuntu 24.04") {
            Container(workflow, "News Generation Workflow", "GitHub Actions YAML", "Daily scheduled workflow")
            Container(node_runtime, "Node.js Runtime", "Node.js 26", "Executes generation scripts")
        }

        Deployment_Node(pages_cdn, "AWS Infrastructure", "S3 + CloudFront") {
            Container(web_server, "Amazon CloudFront", "CDN / HTTPS", "Serves HTTPS content globally")
            ContainerDb(static_content, "Amazon S3 Bucket", "Object Storage", "Generated articles and pages")
        }

        Deployment_Node(repo_storage, "GitHub Repository", "Git Storage") {
            ContainerDb(git_repo, "Git Repository", "Version Control", "Source code and generated content")
        }
    }

    Deployment_Node(user_device, "User Device", "Desktop/Mobile") {
        Container(browser, "Web Browser", "Chrome/Firefox/Safari", "Renders news articles")
    }

    Deployment_Node(external_services, "External Services", "Cloud") {
        System_Ext(ep_mcp, "EP MCP Server", "EP data access")
        System_Ext(llm, "LLM Service", "Content generation")
    }

    Rel(workflow, node_runtime, "Executes", "Process")
    Rel(node_runtime, ep_mcp, "Fetches data", "stdio/JSON-RPC")
    Rel(node_runtime, llm, "Generates content", "HTTPS/API")
    Rel(node_runtime, git_repo, "Commits files", "Git")
    Rel(git_repo, static_content, "Deploys via", "S3 sync + CloudFront invalidation")
    Rel(browser, web_server, "Requests pages", "HTTPS")
    Rel(web_server, static_content, "Serves", "HTTP/2")

    UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="1")

Deployment - Key Infrastructure

Infrastructure ComponentTechnologyPurposeConfiguration
GitHub Actions Runnerubuntu-latest, Node.js 26Execute generation workflow.github/workflows/news-*.lock.yml
Amazon CloudFrontAWS CDNServe static content globallyCloudFront distribution (deploy-s3.yml)
Amazon S3AWS Object StorageHost static site filesS3 bucket (deploy-s3.yml)
Git RepositoryGitHub StorageVersion control + content storagepublic repository
Web BrowserModern browsersRender news articlesHTML5, CSS3, ES6+
EP MCP ServerLocal Node processEP data accessSpawned locally via stdio JSON-RPC
LLM ServiceExternal APIContent generationAPI key authentication

Article Types & Workflows

14 scheduled production article types are driven by 14 unified news-<type>.md workflows (Stage Aโ†’E in one ~60-min session, single PR per run). A 15th type (deep-analysis) is manual/workflow_dispatch only. Article HTML is rendered deterministically by src/aggregator/article-generator.ts from committed Stage-B analysis artifacts โ€” there are no per-type strategy modules in the post-April-2026 pipeline. Every horizon's data window, cadence, mandatory artifacts and stage budgets are defined centrally in src/config/article-horizons.ts (see ADR-007).

๐Ÿท๏ธ Article Type๐Ÿค– gh-aw Workflow๐Ÿ“… Cadence
๐Ÿšจ breakingnews-breaking.mdEvery 6 hours
๐Ÿ”ฎ week-aheadnews-week-ahead.mdFri 07:00 UTC
๐Ÿ“Š month-aheadnews-month-ahead.md1st of month 08:00 UTC
๐ŸŒ quarter-aheadnews-quarter-ahead.md1st of month 08:00 UTC
๐Ÿ›ฐ๏ธ year-aheadnews-year-ahead.mdQuarterly (2 Jan/Apr/Jul/Oct)
๐Ÿ—“๏ธ term-outlooknews-term-outlook.mdSemi-annual (1 Jan & 1 Jul)
๐Ÿ—ณ๏ธ election-cyclenews-election-cycle.mdAnnual (1 Dec) + T-180/T-90/T-30 imminent triggers
๐Ÿ“‹ week-in-reviewnews-week-in-review.mdSat 09:00 UTC
๐Ÿ“ˆ month-in-reviewnews-month-in-review.md28th of month 10:00 UTC
๐Ÿ“š quarter-in-reviewnews-quarter-in-review.md5th of month 08:00 UTC
๐Ÿ“œ year-in-reviewnews-year-in-review.mdAnnual (15 Jan)
๐Ÿ›๏ธ committee-reportsnews-committee-reports.mdMonโ€“Fri 04:00 UTC
๐Ÿ—ณ๏ธ motionsnews-motions.mdMonโ€“Fri 06:00 UTC
โš–๏ธ propositionsnews-propositions.mdMonโ€“Fri 05:00 UTC

Plus: news-translate.md (14-language translation helper, manual dispatch only).

Agentic Workflows (gh-aw)

All 15 news workflows are markdown source files compiled to YAML (.md โ†’ .lock.yml) via the GitHub Agentic Workflows CLI (gh aw compile --validate) with pinned GH_AW_VERSION: v0.77.0 in .github/workflows/compile-agentic-workflows.yml. See WORKFLOWS.md for the full surface.

5-layer security model:

  1. AWF Squid firewall allowlist โ€” egress HTTP allowlist per workflow
  2. Sandboxed Docker with restricted shell โ€” bash tool-call contract (every call requires command + description); shell expansion restrictions
  3. Safe-output constraints โ€” create-pull-request with max-patch-size (default 1024 KB; news-translate.md sets 10240 KB at top level for 14-language fan-out)
  4. JSONL audit trail โ€” per-step structured logs
  5. Lock file compilation โ€” .lock.yml is the immutable executed artifact; .md is the human source under review

MCP gateway (containerised): EP_MCP_GATEWAY_URL=http://host.docker.internal:80/mcp/european-parliament, provisioned by scripts/mcp-setup.sh.

Validator gates (Stage-C completeness review, agent-side โ€” replaces purged runtime validators):


๐Ÿ“Š Technology Stack

Core Technologies

LayerTechnologyVersionPurposeRationale
RuntimeNode.js26.x (engines: >=26); Node.js 26 nightly tested in CIJavaScript execution environmentLTS release for stability and long-term support; ESM-native ("type": "module")
LanguageTypeScript6.0.3Primary development languageStrict type safety; compiles from src/ โ†’ scripts/ targeting ES2025, module: NodeNext
Package Managernpm10.xDependency managementNative Node.js package manager, security audit integration
TestingVitest4.1.4Unit and integration testingFast, ESM-native; happy-dom env; happy-dom@20.9.0
E2E TestingPlaywright1.59.1End-to-end browser testing@axe-core/playwright@4.11.3 for WCAG 2.1 AA
LintingESLint10.2.1Code quality and securityFlat config; plugins: eslint-plugin-sonarjs@4.0.3, eslint-plugin-security@4.0.0, eslint-plugin-jsdoc@62.9.0
FormattingPrettier3.8.3Code formattingOpinionated formatter, consistent code style
VisualizationChart.js4.5.1Dashboard charts in articlesVendored into js/vendor/ via npm run copy-vendor
VisualizationD37.9.0Advanced visualizationsUsed for specific intelligence views
DocumentationTypeDoc0.28.19API documentation generationGenerates docs/ pages from TypeScript sources
HTML ValidationHTMLHint1.9.2HTML5 validationPre-commit + CI
Duplicate Checkjscpd4.0.9Copy-paste detectionScheduled quality audits

Technology Version Matrix

TechnologyCurrent VersionMinimum VersionEnd-of-LifeUpdate Policy
Node.js26.x (LTS)26.0.0 (engines: >=26)~Apr 2029Running on Node.js 26 LTS; Node.js 26 nightly tested in CI (test-and-report.yml)
npm10.x (latest)10.0.0Follows Node.js lifecycleAuto-updated with Node.js
TypeScript6.0.36.0.0N/AUpdate to latest minor within 14 days, major within 90 days
Vitest4.1.44.0.0N/AUpdate to latest minor within 14 days, major within 60 days
Playwright1.59.11.55.0N/AUpdate to latest minor within 14 days, major within 60 days
ESLint10.2.110.0.0N/AUpdate to latest minor within 14 days, major within 90 days
Prettier3.8.33.0.0N/AUpdate to latest minor within 14 days, major within 90 days
Chart.js4.5.14.0.0N/AVendored; update with copy-vendor script
D37.9.07.0.0N/AVendored; update with copy-vendor script
TypeDoc0.28.190.28.0N/AMajor within 60 days
european-parliament-mcp-server1.3.20 (pinned)1.3.20Per upstreamTrack releases; repository workflows and package dependency pin european-parliament-mcp-server@1.3.20 for EP MCP data access
worldbank-mcp1.0.1 (optional)1.0.0Per upstreamBiannual WDI refresh cadence
gh-aw CLIv0.77.0 (pinned GH_AW_VERSION)v0.77.0Per upstreamWorkflow-level pin in compile-agentic-workflows.yml; .md workflows compile to committed .lock.yml files

Dependency Management

Production Dependencies (1 required + 1 optional):

  • european-parliament-mcp-server@1.3.20 โ€” Primary data surface; 6 sliding-window feed tools (timeframe + startDate when custom) and 7 fixed-window feed tools (limit/offset only โ€” documents, plenary_documents, committee_documents, plenary_session_documents, parliamentary_questions, corporate_bodies, controlled_vocabularies); returns uniform {status:"unavailable", items:[]} envelope on upstream failure.
  • worldbank-mcp@1.0.1 (optionalDependencies) โ€” WDI macro/social/environment/health indicators.

IMF REST is integrated via native TypeScript fetch in src/mcp/imf-mcp-client.ts (class IMFMCPClient) โ€” this is NOT an MCP server; calls go directly to https://dataservices.imf.org/REST/SDMX_3.0/. Env: IMF_API_BASE_URL, IMF_API_TIMEOUT_MS. Supplies WEO + FM monthly forecasts up to five years ahead.

Dev dependencies (notable): vitest@4.1.5, @vitest/ui, @vitest/coverage-v8, happy-dom@20.9.0, @playwright/test@1.60.0, @axe-core/playwright@4.11.3, typescript@6.0.3, eslint@10.3.0, eslint-plugin-sonarjs@4.0.3, eslint-plugin-security@4.0.0, eslint-plugin-jsdoc@62.9.0, prettier@3.8.3, htmlhint@1.9.2, typedoc@0.28.19, chart.js@4.5.1, d3@7.9.0, papaparse@5.5.3, husky@9.1.7, jscpd@4.0.9.

Security & Quality

ToolPurposeIntegrationConfiguration
CodeQLSAST scanningGitHub Actions (weekly + PR).github/workflows/codeql.yml
DependabotDependency vulnerability scanningGitHub native (daily).github/dependabot.yml
npm auditDependency security checkPre-commit + CIpackage.json scripts
ESLint SecuritySecurity-focused lintingPre-commit + CIeslint.config.js (security plugin)
HTMLHintHTML validationCI pipeline.htmlhintrc
HuskyGit hooksPre-commit, pre-push.husky/ directory
PlaywrightAccessibility testingE2E test suiteplaywright.config.js (axe integration)

Infrastructure

ServicePurposeConfigurationCost
GitHub ActionsCI/CD automation.github/workflows/Free (public repo)
AWS S3Static site hostingS3 bucket + static websitePay-per-use (storage, requests)
Amazon CloudFrontContent deliveryCloudFront distribution (S3)Pay-per-use (data transfer, requests)
GitVersion controlRepositoryFree (public repo)

External Services

ServicePurposeProtocolAuthenticationRate LimitsCost Model
European Parliament MCP ServerEP data accessLocal process (stdio JSON-RPC)None (local process)N/A (handled by MCP server / EP APIs)Free (EP open data via MCP server)
LLM Service (OpenAI/Anthropic)Content generationHTTPS/JSONAPI key (required)Varies by providerPay-per-token
GitHub APIRepository operationsREST/GraphQLGitHub token5000 req/hrFree (authenticated)

Browser Support

BrowserMinimum VersionFeatures RequiredTesting Coverage
Chrome/Edge90+ES2020, CSS Grid, Flexboxโœ… Playwright E2E (Chromium in CI)
Firefox88+ES2020, CSS Grid, Flexbox๐Ÿงช Manual regression (no Playwright CI)
Safari14+ES2020, CSS Grid, Flexbox๐Ÿงช Manual regression (no Playwright CI)
Mobile Chrome90+ES2020, Responsive Design๐Ÿงช Manual responsive testing
Mobile Safari14+ES2020, Responsive Design๐Ÿงช Manual responsive testing

No support for:

  • Internet Explorer (EOL June 2022)
  • Legacy Edge (Chromium-based only)

Build Process

TypeScript source in src/ is compiled to JavaScript in scripts/ via tsc. The generated JavaScript files are executed by Node.js during news generation. The public npm entry point is src/index.ts (published as euparliamentmonitor with SLSA Level 3 provenance attestations).

src/                                   โ†’ scripts/                          (tsc compilation)
โ”œโ”€โ”€ index.ts                           โ†’ index.js                          npm package entry point
โ”œโ”€โ”€ constants/                         โ†’ constants/
โ”‚   โ”œโ”€โ”€ config.ts                      Project paths, BASE_URL, filename patterns
โ”‚   โ”œโ”€โ”€ analysis-constants.ts          Shared analysis thresholds
โ”‚   โ”œโ”€โ”€ committee-indicator-map.ts     Committee โ†’ indicator mapping
โ”‚   โ”œโ”€โ”€ language-core.ts               ALL_LANGUAGES (14), LANGUAGE_PRESETS
โ”‚   โ”œโ”€โ”€ language-articles.ts           Per-language article-type labels
โ”‚   โ”œโ”€โ”€ language-ui.ts                 Per-language UI strings
โ”‚   โ””โ”€โ”€ languages.ts                   Language metadata (name, flag, direction)
โ”œโ”€โ”€ mcp/                               โ†’ mcp/   โญ Layered after Refactor 5/8 (#2033)
โ”‚   โ”œโ”€โ”€ transport/                     Shared JSON-RPC transport (stdio + HTTP gateway)
โ”‚   โ”‚   โ”œโ”€โ”€ connection.ts              MCPConnection class (stdio + gateway transport, request lifecycle)
โ”‚   โ”‚   โ”œโ”€โ”€ errors.ts                  MCPSessionExpiredError, MCPRateLimitError
โ”‚   โ”‚   โ”œโ”€โ”€ gateway.ts                 HTTP SSE gateway transport helpers
โ”‚   โ”‚   โ”œโ”€โ”€ process.ts                 Stdio spawn/teardown helpers + JSON-RPC message routing
โ”‚   โ”‚   โ”œโ”€โ”€ reconnect.ts               Exponential back-off reconnect loop + retry logic
โ”‚   โ”‚   โ”œโ”€โ”€ retry-policy.ts            isRetriableError, formatRetryAfter, parseRetryAfterMs, RECONNECT_MAX_DELAY_MS
โ”‚   โ”‚   โ””โ”€โ”€ sse-parser.ts              parseSSEResponse โ€” extracts JSON-RPC from SSE data: lines
โ”‚   โ”œโ”€โ”€ ep/                            European Parliament MCP client (split from ep-mcp-client.ts)
โ”‚   โ”‚   โ”œโ”€โ”€ client.ts                  EuropeanParliamentMCPClient class โ€” extends MCPConnection
โ”‚   โ”‚   โ”œโ”€โ”€ election-calendar.ts       getElectionCalendarContext, ElectionImminentTier (T-180/T-90/T-30)
โ”‚   โ”‚   โ”œโ”€โ”€ error-classifier.ts        classifyToolError, isFeedUnavailable
โ”‚   โ”‚   โ”œโ”€โ”€ fallbacks.ts               *_FALLBACK sentinel JSON payloads (EFFECTIVENESS, MEPS, DOCUMENTS, โ€ฆ)
โ”‚   โ”‚   โ”œโ”€โ”€ parse.ts                   _parseResultPayload, _isEmptyStringSentinel, content-not-yet-available
โ”‚   โ”‚   โ”œโ”€โ”€ reliability.ts             TOOL_RELIABILITY_TIMEOUT_MS / _RETRIES โ€” consumed by mcp:probe CLI
โ”‚   โ”‚   โ”œโ”€โ”€ staleness.ts               detectProceduresFeedStaleTail + PROCEDURES_STALENESS_YEAR_THRESHOLD
โ”‚   โ”‚   โ”œโ”€โ”€ tools-data.ts              getMEPs, getPlenarySessions, search/analysis prototype mixins
โ”‚   โ”‚   โ”œโ”€โ”€ tools-documents.ts         getDocuments, getAdoptedTexts prototype mixins
โ”‚   โ”‚   โ”œโ”€โ”€ tools-feeds.ts             Per-feed retrieval prototype mixins
โ”‚   โ”‚   โ”œโ”€โ”€ tools-list.ts              Canonical EP_MCP_TOOLS tool-id list (drift-guarded)
โ”‚   โ”‚   โ””โ”€โ”€ tools-procedures.ts        getProcedures, getProcedureEvents prototype mixins
โ”‚   โ”œโ”€โ”€ imf/                           IMF REST/SDMX-3.0 client (split from imf-mcp-client.ts)
โ”‚   โ”‚   โ”œโ”€โ”€ client.ts                  IMFMCPClient class
โ”‚   โ”‚   โ”œโ”€โ”€ config.ts                  DEFAULT_IMF_API_BASE_URL, IMF_REQUEST_HEADERS, IMF_SUBSCRIPTION_KEY_HEADER, IMF_DATAFLOW_AGENCY, IMF_FALLBACK, IMF_MCP_TOOLS
โ”‚   โ”‚   โ”œโ”€โ”€ http-transport.ts          HTTP fetch helpers with subscription-key rotation + gateway proxy fallback
โ”‚   โ”‚   โ”œโ”€โ”€ lifecycle.ts               Singleton lifecycle (getIMFMCPClient / closeIMFMCPClient)
โ”‚   โ”‚   โ”œโ”€โ”€ observations.ts            countIMFSDMXObservations
โ”‚   โ”‚   โ”œโ”€โ”€ sdmx.ts                    parseSDMXUrn, resolveCodelistUrn, resolveCodelistCodes, resolveAgency
โ”‚   โ”‚   โ”œโ”€โ”€ types.ts                   SDMX* interfaces (Dataflow, Dimension, Concept, ObservationSeries, โ€ฆ) + IMFClientOptions
โ”‚   โ”‚   โ””โ”€โ”€ utils.ts                   readBaseAndTimeout, stripTrailingSlashes, readImfSubscriptionKeysFromEnv
โ”‚   โ”œโ”€โ”€ ep-open-data/                  EP Open Data Portal REST client (split from ep-open-data-client.ts)
โ”‚   โ”‚   โ”œโ”€โ”€ client.ts                  EPOpenDataClient class + getVotingRecordsWithFallback (three-state)
โ”‚   โ”‚   โ”œโ”€โ”€ config.ts                  Endpoint URL constants, EP_OPEN_DATA_TOOLS, EMPTY_VOTES_FALLBACK, EP_OPEN_DATA_ATTRIBUTION
โ”‚   โ”‚   โ”œโ”€โ”€ types.ts                   VotingDataSource, VotingRecordsFallbackResult, VotingRecordsFallbackOptions
โ”‚   โ”‚   โ””โ”€โ”€ utils.ts                   unwrapLabel, wrapAsMCPResult, extractIdentifier helpers
โ”‚   โ”œโ”€โ”€ ep-mcp-client.ts               BARREL โ†’ ./ep/*
โ”‚   โ”œโ”€โ”€ ep-open-data-client.ts         BARREL โ†’ ./ep-open-data/*
โ”‚   โ”œโ”€โ”€ imf-mcp-client.ts              BARREL โ†’ ./imf/*
โ”‚   โ”œโ”€โ”€ mcp-connection.ts              BARREL โ†’ ./transport/*
โ”‚   โ”œโ”€โ”€ wb-mcp-client.ts               World Bank MCP client; exports WORLD_BANK_MCP_TOOLS (7 tools)
โ”‚   โ”œโ”€โ”€ fetch-proxy-server.ts          IMF-only MCP fetch-proxy stdio server (gh-aw container โ€” bypasses AWF Squid proxy)
โ”‚   โ”œโ”€โ”€ html-lang-patcher.ts           Patches HTML metadata regions for language-specific article copies
โ”‚   โ”œโ”€โ”€ mcp-config-reader.ts           Reads ~/.copilot/mcp-config.json โ€” gateway API key + address
โ”‚   โ”œโ”€โ”€ mcp-health.ts                  Health probes for MCP backends
โ”‚   โ”œโ”€โ”€ mcp-retry.ts                   Exponential backoff retry with jitter
โ”‚   โ”œโ”€โ”€ pending-documents.ts           Pending-document tracking for reprobe/escalation
โ”‚   โ””โ”€โ”€ procedure-seen-cache.ts        Dedup cache for legislative procedures across runs
โ”œโ”€โ”€ templates/                         โ†’ templates/
โ”‚   โ”œโ”€โ”€ icons.ts                       SVG icon library (moon, sun, link, etc.)
โ”‚   โ”œโ”€โ”€ section-builders.ts            Thin barrel โ€” re-exports per-section builders from sections/
โ”‚   โ””โ”€โ”€ sections/                      Per-section builders (Refactor 8/8):
โ”‚       โ”œโ”€โ”€ banner.ts                  buildPageBanner โ€” top-of-page editorial banner
โ”‚       โ”œโ”€โ”€ comparison.ts              Before/after comparison table builder
โ”‚       โ”œโ”€โ”€ footer.ts                  buildSiteFooter โ€” Hack23 AB footer (single source of truth, 14-lang)
โ”‚       โ”œโ”€โ”€ header.ts                  buildSiteHeader โ€” stacked site header + nav
โ”‚       โ”œโ”€โ”€ key-figures.ts             Key-figures bar builder
โ”‚       โ”œโ”€โ”€ quality-score.ts           Quality scoring + badge rendering
โ”‚       โ”œโ”€โ”€ timeline.ts                Timeline section builder
โ”‚       โ””โ”€โ”€ toc.ts                     TOC entry builder (TOCEntry, TOC_ARIA_LABELS consumer)
โ”œโ”€โ”€ aggregator/                        โ†’ aggregator/  โญ Deterministic article renderer
โ”‚   โ”œโ”€โ”€ article-generator.ts           Entry point CLI (`npm run generate-article -- --run <dir>` or `--all`)
โ”‚   โ”œโ”€โ”€ analysis-aggregator.ts         aggregateAnalysisRun() โ€” manifest discovery, .md filter, Provenance & Audit block
โ”‚   โ”œโ”€โ”€ artifact-order.ts              ARTIFACT_SECTIONS โ€” canonical 19-section order (executive-brief โ†’ quality-reflection)
โ”‚   โ”œโ”€โ”€ clean-artifact.ts              Strips SPDX/banner/provenance front matter from artifacts before merge
โ”‚   โ”œโ”€โ”€ markdown-renderer.ts           markdown-it + plugin allowlist (anchor, footnote, attrs, deflist); mermaid fenceโ†’<pre>
โ”‚   โ”œโ”€โ”€ article-html.ts                HTML5 wrapper: site header, language switcher, TOC sidebar, JSON-LD NewsArticle, hreflang
โ”‚   โ”œโ”€โ”€ article-metadata.ts            5-tier editorial-highlight resolver for <title> / <meta description>
โ”‚   โ”œโ”€โ”€ article-meta.ts                buildArticleMeta() โ€” deterministic article-meta.json output
โ”‚   โ”œโ”€โ”€ key-takeaways.ts               buildKeyTakeaways() โ€” 3โ€“7 bullet extraction from synthesis-summary + intelligence-assessment
โ”‚   โ”œโ”€โ”€ lead-extractor.ts              trimToLeadSentence() โ€” plain-text SEO lead for meta description
โ”‚   โ”œโ”€โ”€ reader-intelligence-guide.ts   Reader Guide HTML overlay (collapsible methodology panel)
โ”‚   โ”œโ”€โ”€ reader-guide-constants.ts      Section IDs and titles for the Reader Guide
โ”‚   โ”œโ”€โ”€ metadata/                      5-tier editorial-highlight resolver sub-modules (Refactor series)
โ”‚   โ”œโ”€โ”€ clean/                         Artifact cleaning pipeline: strip-spdx, strip-banners, demote-headings, rewrite-links, โ€ฆ
โ”‚   โ”œโ”€โ”€ html/                          HTML fragment builders: shell, headline, toc, hreflang, tradecraft-cards, โ€ฆ
โ”‚   โ”œโ”€โ”€ generator/                     CLI + render-one + render-batch + discovery (Refactor series)
โ”‚   โ”œโ”€โ”€ reader-guide/                  Reader Guide row builders: rows-core, rows-extended, builder, icons, โ€ฆ
โ”‚   โ”œโ”€โ”€ run/                           Per-run helpers: provenance, tradecraft, reader-guide, analysis-index, โ€ฆ
โ”‚   โ”œโ”€โ”€ cli/                           CLI argument parsing (parse.ts)
โ”‚   โ”œโ”€โ”€ manifest/                      manifest.json schema (types.ts), reader (reader.ts), resolver (resolver.ts), manifest-writer.ts
โ”‚   โ”œโ”€โ”€ runs/                          Run discovery (discover.ts) + collision grouping (grouping.ts)
โ”‚   โ”œโ”€โ”€ slug/                          Article URL slug generation (slug.ts)
โ”‚   โ””โ”€โ”€ infra/                         GitHub URL helpers (github-urls.ts)
โ”œโ”€โ”€ generators/                        โ†’ generators/
โ”‚   โ”œโ”€โ”€ news-indexes.ts                Top-level orchestrator + barrel (was 959-LOC; split Refactor 8/8)
โ”‚   โ”œโ”€โ”€ news-indexes/                  Sub-modules: per-language.ts (HTML composer), backfill.ts (SEO/hreflang/JSON-LD healing)
โ”‚   โ”œโ”€โ”€ sitemap.ts                     Orchestrator: writes sitemap.xml, rss.xml, 14 sitemap_<lang>.html, 14 political-intelligence pages
โ”‚   โ”œโ”€โ”€ sitemap/                       Sitemap sub-modules: xml.ts, html.ts, rss.ts, xml-utils.ts, copy.ts
โ”‚   โ”œโ”€โ”€ political-intelligence.ts      Political Intelligence Hub generator entry point
โ”‚   โ”œโ”€โ”€ political-intelligence/        Sub-modules: html.ts, data.ts, icons.ts, markdown.ts, types.ts
โ”‚   โ”‚   โ”œโ”€โ”€ copy.ts                    Thin barrel re-exporting from copy/ (was 617-LOC; split Refactor 8/8)
โ”‚   โ”‚   โ”œโ”€โ”€ copy/                      Per-language-group landing-page copy: types.ts, eu-core.ts, nordic.ts, other.ts, index.ts
โ”‚   โ”‚   โ””โ”€โ”€ descriptions/              Per-category curated descriptions + titles (Refactor 8/8 โ€” was a single 3324-LOC file):
โ”‚   โ”‚       โ”œโ”€โ”€ desc-{methodologies,templates,references}.ts  Per-file English description + 14-language overlays
โ”‚   โ”‚       โ”œโ”€โ”€ titles-{methodologies,references,templates-a,templates-b}.ts  Per-file English title + 14-language overlays
โ”‚   โ”‚       โ”œโ”€โ”€ run-types-{titles,descriptions}.ts            Localized titles + descriptions for canonical run-type slugs
โ”‚   โ”‚       โ”œโ”€โ”€ curated-{descriptions,titles}.ts              Aggregators producing CURATED_DESCRIPTIONS / CURATED_TITLES
โ”‚   โ”‚       โ”œโ”€โ”€ fallback.ts            Per-language kind words + buildGenericFallback / stripEmojiAndPunct
โ”‚   โ”‚       โ”œโ”€โ”€ lookup.ts              Public resolver API: getCuratedDescription / getCuratedTitle (+ has* predicates)
โ”‚   โ”‚       โ”œโ”€โ”€ run-types.ts           parseRunSlug, getRunTypeInfo, canonicalizeArtifactStem
โ”‚   โ”‚       โ”œโ”€โ”€ artifact-info.ts       FEED_PREFIX_LABELS + ORPHAN_ARTIFACT_INFO + getArtifactInfo resolver
โ”‚   โ”‚       โ”œโ”€โ”€ types.ts               Shared TextI18n / CuratedDescription types
โ”‚   โ”‚       โ””โ”€โ”€ index.ts               Barrel re-exporting the full public API
โ”‚   โ”œโ”€โ”€ political-intelligence-descriptions.ts  Thin 25-line barrel re-exporting from political-intelligence/descriptions/
โ”‚   โ””โ”€โ”€ seo-copy.ts                    SEO meta tag copy tables (14 languages)
โ”œโ”€โ”€ types/                             โ†’ types/
โ”‚   โ”œโ”€โ”€ analysis.ts, generation.ts, imf.ts, intelligence.ts,
โ”‚   โ”‚   parliament.ts, political-classification.ts, political-risk.ts,
โ”‚   โ”‚   political-threats.ts, quality.ts, significance.ts, stakeholder.ts,
โ”‚   โ”‚   world-bank.ts, index.ts
โ”‚   โ”œโ”€โ”€ languages.ts                   LanguageCode / RTLLanguageCode / LanguageMap
โ”‚   โ”œโ”€โ”€ article-category.ts            ArticleCategory + ArticlePerspective + CATEGORY_* maps
โ”‚   โ”œโ”€โ”€ article-strings/               Per-article-type localized string interfaces
โ”‚   โ”‚   โ”œโ”€โ”€ propositions.ts, motions.ts, week-ahead.ts,
โ”‚   โ”‚   โ”‚   breaking.ts (incl. DeepAnalysis), committee.ts
โ”‚   โ”œโ”€โ”€ visualization/                 Visualization bounded contexts
โ”‚   โ”‚   โ”œโ”€โ”€ swot.ts, charts.ts, dashboard.ts,
โ”‚   โ”‚   โ”‚   mindmap.ts, voting-bloc.ts
โ”‚   โ”œโ”€โ”€ mcp/                           MCP transport + per-tool option shapes
โ”‚   โ”‚   โ”œโ”€โ”€ client.ts, ep-tools.ts,
โ”‚   โ”‚   โ”‚   ep-feeds.ts, reports.ts
โ”‚   โ””โ”€โ”€ common.ts, visualization.ts, mcp.ts   Thin re-export barrels (transitional)
โ””โ”€โ”€ utils/                             โ†’ utils/
    โ”œโ”€โ”€ articles/                      Article-domain helpers: analysis-discovery, filename, metadata, slug
    โ”œโ”€โ”€ fs/                            File-system primitives: atomic-write, directory
    โ”œโ”€โ”€ html/                          HTML utilities: escape, validate
    โ”œโ”€โ”€ intelligence/                  Intelligence index builders: build, html, internals, persist, trends, types
    โ”œโ”€โ”€ article-category.ts            ArticleCategory enum helpers and slug mapping
    โ”œโ”€โ”€ content-metadata.ts            Content metadata extraction from HTML/Markdown
    โ”œโ”€โ”€ copy-test-reports.ts           Copies test report artifacts to docs/
    โ”œโ”€โ”€ file-utils.ts                  escapeHTML, file I/O helpers, path utilities
    โ”œโ”€โ”€ generate-docs-index.ts         Generates docs/ index page
    โ”œโ”€โ”€ html-sanitize.ts               HTML sanitization for safe output
    โ”œโ”€โ”€ intelligence-index.ts          Intelligence document index builder (barrel)
    โ”œโ”€โ”€ metadata-utils.ts              Shared metadata transformation utilities
    โ”œโ”€โ”€ news-metadata.ts               Article metadata aggregation (articles-metadata.json)
    โ”œโ”€โ”€ validate-ep-api.ts             EP API endpoint validation script
    โ””โ”€โ”€ mcp-probe.ts                   MCP reliability probe CLI (`npm run mcp:probe`)
workflows/                             โ†’ workflows/  โญ Agentic-workflow support (added via #2086)
โ”œโ”€โ”€ index.ts                           Public barrel + shared workflow contract type re-exports
โ”œโ”€โ”€ types.ts                           DataMode, GateVerdict, StageHistoryEntry, ValidationIssue, โ€ฆ
โ”œโ”€โ”€ completeness-gate/                 Stage C validation (constants, types, validators, index barrel)
โ”œโ”€โ”€ infrastructure/                    Shell-safety validators (banned-pattern matchers + barrel)
โ””โ”€โ”€ safe-outputs/                      Stage E PR creation constraints (types + barrel)

Key build / generation commands:

  • npm run build โ€” Runs tsc (TypeScript compilation src/ โ†’ scripts/)
  • npm run lint โ€” ESLint on src/
  • npm run generate-article -- --run <dir> โ€” Renders a single analysis run to 14-language HTML
  • npm run generate-article:all โ€” Batch-renders every discoverable analysis run
  • npm run generate-news-indexes โ€” Executes scripts/generators/news-indexes.js (prebuild hook)
  • npm run generate-sitemap โ€” Executes scripts/generators/sitemap.js (prebuild hook โ€” also generates political-intelligence pages)
  • npm run copy-vendor โ€” Vendors chart.js, d3, and mermaid ESM bundles into js/vendor/
  • npm run validate-analysis โ€” Stage-C completeness validation against reference-quality-thresholds.json
  • npm run sync:templates โ€” Syncs ANALYSIS-TEMPLATE-FRONTMATTER:v1 blocks into analysis templates
  • npm run prior-run-diff โ€” Generates carry-forward plan for re-run improvement
  • npm run lint:prompts โ€” Validates gh-aw workflow prompt imports and banned patterns
  • npm run test / test:unit / test:integration / test:e2e / test:coverage โ€” Test suite

TypeScript configuration (tsconfig.json):

  • target: ES2025, module: NodeNext, strict: true, rootDir: ./src, outDir: ./scripts, "type": "module" in package.json

Runtime JS (browser):

  • js/index-runtime.js โ€” Index page filter + theme toggle
  • js/article-runtime.js โ€” Reading progress + theme toggle
  • js/mermaid-init.js โ€” Lazy Mermaid diagram rendering
  • External scripts only (no inline scripts โ€” CSP script-src 'self')
  • js/vendor/ โ€” Vendored Chart.js, D3, and Mermaid ESM bundles

๐Ÿ—‚๏ธ Source Module Topology (Post-Refactor 8/8 Series + Follow-ups)

The Refactor 8/8 series (#2029โ€“#2036) and its enforcement follow-ups (#2069โ€“#2086) split every original >600-LOC file into bounded-context sub-modules. The sections below document the resulting layered topology, showing the actual sub-directory structure under src/ (354 TypeScript files across 8 bounded contexts at the latest commit).

Each area is enforced by ESLint max-lines rules in eslint.config.js and guarded by test/unit/source-file-size.test.js. Per ISO 27001 A.8.28 / A.8.32 and NIST CSF 2.0 PR.PS-01, no src/**/*.ts file may exceed 600 raw lines; tighter 400-line ceilings apply to the per-concern sub-directories below.

Types & Contracts (src/types/)

Pure TypeScript type declarations split into bounded-context sub-modules with thin re-export barrels (common.ts, visualization.ts, mcp.ts) for backward-compatibility. Every sub-module is a single-responsibility type catalogue; no business logic is permitted.

src/types/
โ”œโ”€โ”€ languages.ts             LanguageCode / RTLLanguageCode / LanguageMap
โ”œโ”€โ”€ article-category.ts      ArticleCategory + ArticlePerspective + CATEGORY_* maps
โ”œโ”€โ”€ analysis.ts              Stage-A/B analysis artefact contract types
โ”œโ”€โ”€ generation.ts            Article-generation pipeline types
โ”œโ”€โ”€ imf.ts                   IMF SDMX-3.0 response shapes
โ”œโ”€โ”€ intelligence.ts          Political-intelligence artefact types
โ”œโ”€โ”€ parliament.ts            EP plenary / committee / vote types
โ”œโ”€โ”€ political-classification.ts  Political classification rubric types
โ”œโ”€โ”€ political-risk.ts        Risk-scoring vocabulary types
โ”œโ”€โ”€ political-threats.ts     Threat-modelling vocabulary types
โ”œโ”€โ”€ quality.ts               Quality-scoring rubric types
โ”œโ”€โ”€ significance.ts          News-significance scoring types
โ”œโ”€โ”€ stakeholder.ts           Stakeholder-analysis vocabulary types
โ”œโ”€โ”€ world-bank.ts            World Bank indicator catalog types
โ”œโ”€โ”€ article-strings/         Per-article-type localized string interfaces (โ‰ค 400 LOC each)
โ”‚   โ”œโ”€โ”€ breaking.ts          Includes DeepAnalysis article-string types
โ”‚   โ”œโ”€โ”€ committee.ts
โ”‚   โ”œโ”€โ”€ motions.ts
โ”‚   โ”œโ”€โ”€ propositions.ts
โ”‚   โ””โ”€โ”€ week-ahead.ts
โ”œโ”€โ”€ visualization/           Visualization bounded contexts (โ‰ค 400 LOC each)
โ”‚   โ”œโ”€โ”€ charts.ts
โ”‚   โ”œโ”€โ”€ dashboard.ts
โ”‚   โ”œโ”€โ”€ mindmap.ts
โ”‚   โ”œโ”€โ”€ swot.ts
โ”‚   โ””โ”€โ”€ voting-bloc.ts
โ”œโ”€โ”€ mcp/                     MCP transport + per-tool option shapes (โ‰ค 400 LOC each)
โ”‚   โ”œโ”€โ”€ client.ts            MCPClientOptions, JSON-RPC envelopes
โ”‚   โ”œโ”€โ”€ ep-tools.ts          EP MCP tool option types
โ”‚   โ”œโ”€โ”€ ep-feeds.ts          EP MCP feed-payload types
โ”‚   โ””โ”€โ”€ reports.ts           Cross-tool report types
โ”œโ”€โ”€ common.ts                Thin barrel โ†’ article-strings/* + languages + article-category
โ”œโ”€โ”€ visualization.ts         Thin barrel โ†’ visualization/*
โ”œโ”€โ”€ mcp.ts                   Thin barrel โ†’ mcp/*
โ””โ”€โ”€ index.ts                 Root barrel re-exporting the full public surface

Configuration Registry (src/config/)

Horizon definitions, cadence data, and lookup tables for the 15 article types. Every setting used by both the aggregator and agentic workflows is declared here (ADR-007) to prevent the drift-and-override anti-pattern. All files stay under 400 code lines; the registry is the single source of truth for per-slug budgets.

src/config/
โ”œโ”€โ”€ article-horizons.ts      Top-level barrel โ€” re-exports the public registry API
โ”œโ”€โ”€ index.ts                 Root barrel for src/config/
โ””โ”€โ”€ horizons/
    โ”œโ”€โ”€ registry.ts          ARTICLE_HORIZONS registry โ€” all 15 article types
    โ”œโ”€โ”€ lookup.ts            getHorizon(), getArticleTypeConfig() resolvers
    โ”œโ”€โ”€ stage-budgets.ts     Per-stage time budgets (target / hard deadline)
    โ”œโ”€โ”€ forward-projection.ts  Forward-looking window helpers
    โ”œโ”€โ”€ artifact-paths.ts    Canonical artifact-path constants
    โ””โ”€โ”€ types.ts             ArticleHorizon / StageBudget interfaces

Internationalization & Constants (src/constants/)

Localisation strings, SEO copy, UI labels, World Bank indicator/committee mappings, and the committeeโ†’indicator map. The Refactor 8/8 series split every oversized aggregate string table into per-language files (<slug>/<lang>.ts) and per-region content tables (-{central,east,nordic,west,eu,global}).

src/constants/
โ”œโ”€โ”€ analysis-constants.ts    Shared analysis thresholds
โ”œโ”€โ”€ build-info-meta.ts       Build-info metadata for the PWA footer
โ”œโ”€โ”€ committee-indicator-map.ts  Committee โ†’ World Bank indicator mapping
โ”œโ”€โ”€ config.ts                Project paths, BASE_URL, filename patterns
โ”œโ”€โ”€ language-articles.ts     Per-language article-type labels
โ”œโ”€โ”€ language-core.ts         ALL_LANGUAGES (14), LANGUAGE_PRESETS
โ”œโ”€โ”€ language-ui.ts           Per-language UI strings
โ”œโ”€โ”€ languages.ts             Language metadata (name, flag, RTL direction)
โ”œโ”€โ”€ og-locales.ts            BCP-47 OpenGraph locale shim (thin barrel โ†’ seo/)
โ”œโ”€โ”€ social-handles.ts        Thin barrel โ†’ seo/social-handles
โ”œโ”€โ”€ articles/                Per-article-type localized string tables (Refactor 8/8 splits)
โ”‚   โ”œโ”€โ”€ _shared.ts           Shared article-strings helpers
โ”‚   โ”œโ”€โ”€ breaking.ts          Aggregator barrel re-exporting the 4 regional splits
โ”‚   โ”œโ”€โ”€ breaking-strings-central.ts   Central-European breaking-news strings
โ”‚   โ”œโ”€โ”€ breaking-strings-east.ts      Eastern-European breaking-news strings
โ”‚   โ”œโ”€โ”€ breaking-strings-nordic.ts    Nordic breaking-news strings
โ”‚   โ”œโ”€โ”€ breaking-strings-west.ts      Western-European breaking-news strings
โ”‚   โ”œโ”€โ”€ committee-reports.ts          Committee-reports aggregator barrel
โ”‚   โ”œโ”€โ”€ committee-reports-content-eu.ts      EU committee reports content
โ”‚   โ”œโ”€โ”€ committee-reports-content-global.ts  Global committee reports content
โ”‚   โ”œโ”€โ”€ dashboard.ts                  Thin barrel โ†’ dashboard/index
โ”‚   โ”œโ”€โ”€ dashboard/                    Per-language dashboard-builder strings (14 files)
โ”‚   โ”‚   โ”œโ”€โ”€ ar.ts da.ts de.ts en.ts es.ts fi.ts fr.ts he.ts
โ”‚   โ”‚   โ”œโ”€โ”€ ja.ts ko.ts nl.ts no.ts sv.ts zh.ts
โ”‚   โ”‚   โ””โ”€โ”€ index.ts          Assembles LanguageMap<DashboardBuilderStrings>
โ”‚   โ”œโ”€โ”€ deep-analysis.ts              Thin barrel โ†’ deep-analysis/index
โ”‚   โ”œโ”€โ”€ deep-analysis/                Per-language deep-analysis strings (14 files + index)
โ”‚   โ”‚   โ”œโ”€โ”€ ar.ts da.ts de.ts en.ts es.ts fi.ts fr.ts he.ts
โ”‚   โ”‚   โ”œโ”€โ”€ ja.ts ko.ts nl.ts no.ts sv.ts zh.ts
โ”‚   โ”‚   โ””โ”€โ”€ index.ts          Assembles LanguageMap<DeepAnalysisStrings>
โ”‚   โ”œโ”€โ”€ editorial.ts                  Editorial-headline string helpers
โ”‚   โ”œโ”€โ”€ extended-horizons.ts          Extended-horizon string tables
โ”‚   โ”œโ”€โ”€ index.ts                      Top-level articles/ barrel
โ”‚   โ”œโ”€โ”€ localized-keywords.ts         Localized keywords aggregator barrel
โ”‚   โ”œโ”€โ”€ localized-keywords-central.ts Central-European keyword tables
โ”‚   โ”œโ”€โ”€ localized-keywords-global.ts  Global keyword tables
โ”‚   โ”œโ”€โ”€ localized-keywords-nordic.ts  Nordic keyword tables
โ”‚   โ”œโ”€โ”€ month-ahead.ts                Month-ahead string tables
โ”‚   โ”œโ”€โ”€ month-in-review.ts            Month-in-review string tables
โ”‚   โ”œโ”€โ”€ motions.ts                    Motions article-string table
โ”‚   โ”œโ”€โ”€ propositions.ts               Propositions article-string table
โ”‚   โ”œโ”€โ”€ swot.ts                       SWOT aggregator barrel
โ”‚   โ”œโ”€โ”€ swot-builder-central.ts       Central-European SWOT-builder strings
โ”‚   โ”œโ”€โ”€ swot-builder-global.ts        Global SWOT-builder strings
โ”‚   โ”œโ”€โ”€ swot-builder-nordic.ts        Nordic SWOT-builder strings
โ”‚   โ”œโ”€โ”€ week-ahead.ts                 Week-ahead aggregator barrel
โ”‚   โ”œโ”€โ”€ week-ahead-eu.ts              EU week-ahead string tables
โ”‚   โ”œโ”€โ”€ week-ahead-global.ts          Global week-ahead string tables
โ”‚   โ””โ”€โ”€ week-in-review.ts             Week-in-review string tables
โ”œโ”€โ”€ seo/                     SEO meta-tag copy tables (Refactor 8/8 split)
โ”‚   โ”œโ”€โ”€ index.ts             Barrel re-exporting OG locales + social handles
โ”‚   โ”œโ”€โ”€ og-locales.ts        OG_LOCALES, buildOgLocaleTags, getOgLocale
โ”‚   โ””โ”€โ”€ social-handles.ts    ORG_SAME_AS, TWITTER_SITE_HANDLE, TWITTER_CREATOR_HANDLE
โ”œโ”€โ”€ ui/                      Per-surface UI localization modules (12 modules)
โ”‚   โ”œโ”€โ”€ accessibility.ts             SKIP_LINK_TEXTS, TOC_ARIA_LABELS, switcher ARIA
โ”‚   โ”œโ”€โ”€ ai-content.ts                AI_SECTION_CONTENT + AISection interface
โ”‚   โ”œโ”€โ”€ article-category-labels.ts   ARTICLE_TYPE_LABELS / _ICONS
โ”‚   โ”œโ”€โ”€ footer-labels.ts             Every FOOTER_*_LABELS map
โ”‚   โ”œโ”€โ”€ methodology-framework-labels.ts  Framework explanation labels
โ”‚   โ”œโ”€โ”€ page-titles.ts               PAGE_TITLES, PAGE_DESCRIPTIONS
โ”‚   โ”œโ”€โ”€ pwa-labels.ts                PWA install/update prompts, offline shell
โ”‚   โ”œโ”€โ”€ reading-time.ts              READ_TIME_LABELS (per-language pluralization)
โ”‚   โ”œโ”€โ”€ related-analysis.ts          SECTION_TITLE_LABELS, RELATED_ANALYSIS_LABELS
โ”‚   โ”œโ”€โ”€ risk-threat-labels.ts        Risk / threat / stakeholder labels
โ”‚   โ”œโ”€โ”€ section-headings.ts          Section headings, nav labels, header chrome
โ”‚   โ”œโ”€โ”€ tradecraft-cards.ts          Tradecraft / analysis-index card labels
โ”‚   โ””โ”€โ”€ index.ts                     Barrel
โ””โ”€โ”€ world-bank/              World Bank indicator and committee mapping
    โ”œโ”€โ”€ category-map.ts                  Aggregator barrel โ†’ category-map-* splits
    โ”œโ”€โ”€ category-map-analysis.ts         Analysis-perspective category mapping
    โ”œโ”€โ”€ category-map-legislative.ts      Legislative-perspective category mapping
    โ”œโ”€โ”€ category-map-periodic.ts         Periodic-perspective category mapping
    โ”œโ”€โ”€ committee-map.ts                 Aggregator barrel โ†’ committee-map-* splits
    โ”œโ”€โ”€ committee-map-part1.ts           Committeeโ†’indicator map (first half)
    โ”œโ”€โ”€ committee-map-part2.ts           Committeeโ†’indicator map (second half)
    โ”œโ”€โ”€ committee-map-types.ts           Shared committee-map type definitions
    โ”œโ”€โ”€ indicator-catalog.ts             34 curated WB_INDICATORS records
    โ””โ”€โ”€ index.ts                         Public barrel

Utility Taxonomy (src/utils/)

Focused utility sub-modules replace a flat file collection. Each sub-directory groups utilities by a single responsibility axis.

src/utils/
โ”œโ”€โ”€ articles/                Article-domain helpers
โ”‚   โ”œโ”€โ”€ analysis-discovery.ts  Discover analysis runs in the file tree
โ”‚   โ”œโ”€โ”€ filename.ts            Article filename construction helpers
โ”‚   โ”œโ”€โ”€ metadata.ts            Article metadata aggregation
โ”‚   โ””โ”€โ”€ slug.ts                URL slug derivation helpers
โ”œโ”€โ”€ fs/                      File-system primitives
โ”‚   โ”œโ”€โ”€ atomic-write.ts        Atomic temp-file write helper
โ”‚   โ””โ”€โ”€ directory.ts           ensureDir / mkdirpSync helpers
โ”œโ”€โ”€ html/                    HTML-specific utilities
โ”‚   โ”œโ”€โ”€ escape.ts              escapeHTML (context-aware escaping)
โ”‚   โ””โ”€โ”€ validate.ts            HTMLHint-based HTML fragment validation
โ”œโ”€โ”€ intelligence/            Intelligence document index utilities
โ”‚   โ”œโ”€โ”€ build.ts               buildIntelligenceIndex orchestrator
โ”‚   โ”œโ”€โ”€ html.ts                HTML fragment renderers for index pages
โ”‚   โ”œโ”€โ”€ internals.ts           Internal helpers (path, label, sort)
โ”‚   โ”œโ”€โ”€ persist.ts             Read/write intelligence index JSON
โ”‚   โ”œโ”€โ”€ trends.ts              Trend signal extraction from artifacts
โ”‚   โ””โ”€โ”€ types.ts               IntelligenceIndexEntry / TrendSignal types
โ”œโ”€โ”€ article-category.ts      ArticleCategory enum helpers and slug mapping
โ”œโ”€โ”€ content-metadata.ts      Content metadata extraction from HTML/Markdown
โ”œโ”€โ”€ copy-test-reports.ts     Copies test report artifacts to docs/
โ”œโ”€โ”€ file-utils.ts            escapeHTML, file I/O helpers, path utilities
โ”œโ”€โ”€ generate-docs-index.ts   Generates docs/ index page
โ”œโ”€โ”€ html-sanitize.ts         HTML sanitization for safe output
โ”œโ”€โ”€ intelligence-index.ts    Intelligence document index builder (barrel)
โ”œโ”€โ”€ mcp-probe.ts             MCP reliability probe CLI (npm run mcp:probe)
โ”œโ”€โ”€ metadata-utils.ts        Shared metadata transformation utilities
โ”œโ”€โ”€ news-metadata.ts         Article metadata aggregation (articles-metadata.json)
โ””โ”€โ”€ validate-ep-api.ts       EP API endpoint validation script

MCP Integration Layer (src/mcp/)

The MCP layer is split into four bounded contexts plus a shared transport. Each context exposes a barrel file at the old path for backward-compatibility. Top-level utility modules (fetch-proxy-server, html-lang-patcher, mcp-config-reader) provide stand-alone helpers that don't belong to any particular backend.

src/mcp/
โ”œโ”€โ”€ transport/               Shared JSON-RPC transport (โ‰ค 400 LOC each)
โ”‚   โ”œโ”€โ”€ connection.ts        MCPConnection class (stdio + gateway transport)
โ”‚   โ”œโ”€โ”€ errors.ts            MCPSessionExpiredError, MCPRateLimitError
โ”‚   โ”œโ”€โ”€ gateway.ts           HTTP SSE gateway transport helpers
โ”‚   โ”œโ”€โ”€ process.ts           Stdio spawn/teardown helpers and JSON-RPC routing
โ”‚   โ”œโ”€โ”€ reconnect.ts         Exponential back-off reconnect loop + retry logic
โ”‚   โ”œโ”€โ”€ retry-policy.ts      isRetriableError, RECONNECT_MAX_DELAY_MS
โ”‚   โ””โ”€โ”€ sse-parser.ts        parseSSEResponse (JSON-RPC from SSE data: lines)
โ”œโ”€โ”€ ep/                      European Parliament MCP client
โ”‚   โ”œโ”€โ”€ client.ts            EuropeanParliamentMCPClient (extends MCPConnection)
โ”‚   โ”œโ”€โ”€ election-calendar.ts getElectionCalendarContext, ElectionImminentTier
โ”‚   โ”œโ”€โ”€ error-classifier.ts  classifyToolError, isFeedUnavailable
โ”‚   โ”œโ”€โ”€ fallbacks.ts         Sentinel JSON fallback payloads
โ”‚   โ”œโ”€โ”€ parse.ts             _parseResultPayload helpers
โ”‚   โ”œโ”€โ”€ reliability.ts       TOOL_RELIABILITY_TIMEOUT_MS / _RETRIES
โ”‚   โ”œโ”€โ”€ staleness.ts         detectProceduresFeedStaleTail
โ”‚   โ”œโ”€โ”€ tools-data.ts        getMEPs, getPlenarySessions, search/analysis mixins
โ”‚   โ”œโ”€โ”€ tools-documents.ts   getDocuments, getAdoptedTexts mixins
โ”‚   โ”œโ”€โ”€ tools-feeds.ts       Per-feed retrieval mixins
โ”‚   โ”œโ”€โ”€ tools-list.ts        Canonical EP_MCP_TOOLS tool-id list (drift-guarded)
โ”‚   โ””โ”€โ”€ tools-procedures.ts  getProcedures, getProcedureEvents mixins
โ”œโ”€โ”€ ep-open-data/            EP Open Data Portal REST client
โ”‚   โ”œโ”€โ”€ client.ts            EPOpenDataClient + getVotingRecordsWithFallback
โ”‚   โ”œโ”€โ”€ config.ts            Endpoint URLs, EP_OPEN_DATA_TOOLS, fallbacks
โ”‚   โ”œโ”€โ”€ types.ts             VotingDataSource, VotingRecordsFallbackResult
โ”‚   โ””โ”€โ”€ utils.ts             unwrapLabel, wrapAsMCPResult helpers
โ”œโ”€โ”€ imf/                     IMF REST/SDMX-3.0 client
โ”‚   โ”œโ”€โ”€ client.ts            IMFMCPClient class
โ”‚   โ”œโ”€โ”€ config.ts            DEFAULT_IMF_API_BASE_URL, IMF_MCP_TOOLS
โ”‚   โ”œโ”€โ”€ http-transport.ts    HTTP fetch helpers with key rotation + gateway fallback
โ”‚   โ”œโ”€โ”€ lifecycle.ts         Singleton lifecycle (getIMFMCPClient / closeIMFMCPClient)
โ”‚   โ”œโ”€โ”€ observations.ts      countIMFSDMXObservations
โ”‚   โ”œโ”€โ”€ sdmx.ts              SDMX URN parsing and codelist resolution
โ”‚   โ”œโ”€โ”€ types.ts             SDMX* interfaces + IMFClientOptions
โ”‚   โ””โ”€โ”€ utils.ts             readBaseAndTimeout, stripTrailingSlashes
โ”œโ”€โ”€ ep-mcp-client.ts         BARREL โ†’ ./ep/*
โ”œโ”€โ”€ ep-open-data-client.ts   BARREL โ†’ ./ep-open-data/*
โ”œโ”€โ”€ imf-mcp-client.ts        BARREL โ†’ ./imf/*
โ”œโ”€โ”€ mcp-connection.ts        BARREL โ†’ ./transport/*
โ”œโ”€โ”€ fetch-proxy-server.ts    IMF-only MCP fetch-proxy stdio server (gh-aw container)
โ”œโ”€โ”€ html-lang-patcher.ts     Patches HTML metadata regions for language-specific copies
โ”œโ”€โ”€ mcp-config-reader.ts     Reads ~/.copilot/mcp-config.json โ€” gateway key + address
โ”œโ”€โ”€ mcp-health.ts            Health probes for MCP backends
โ”œโ”€โ”€ mcp-retry.ts             Exponential backoff retry with jitter (shared)
โ”œโ”€โ”€ pending-documents.ts     Pending-document tracking for reprobe/escalation
โ””โ”€โ”€ procedure-seen-cache.ts  Dedup cache for legislative procedures across runs

Aggregator Pipeline (src/aggregator/)

The deterministic aggregator is split into per-responsibility sub-directories. Each sub-directory handles one phase of the manifest.json โ†’ HTML pipeline. All sub-directory files carry a 400-line ceiling; only the root-level barrel files (which existed before the refactor series) remain under the global 600-line cap.

src/aggregator/
โ”œโ”€โ”€ article-generator.ts     Entry-point CLI (`npm run generate-article`)
โ”œโ”€โ”€ analysis-aggregator.ts   aggregateAnalysisRun() โ€” manifest discovery + .md filter
โ”œโ”€โ”€ artifact-order.ts        ARTIFACT_SECTIONS โ€” canonical 19-section order
โ”œโ”€โ”€ article-html.ts          HTML5 wrapper: header, language switcher, TOC, JSON-LD
โ”œโ”€โ”€ reader-friendly-transform.ts Post-render terminology transform for public HTML
โ”œโ”€โ”€ article-meta.ts          buildArticleMeta() โ€” deterministic article-meta.json
โ”œโ”€โ”€ article-metadata.ts      5-tier editorial-highlight resolver public API
โ”œโ”€โ”€ clean-artifact.ts        Top-level clean-pipeline barrel
โ”œโ”€โ”€ editorial-brief-resolver.ts  Language-aware executive-brief sibling lookup
โ”œโ”€โ”€ key-takeaways.ts         buildKeyTakeaways() โ€” 3โ€“7 bullet extraction
โ”œโ”€โ”€ lead-extractor.ts        trimToLeadSentence() โ€” plain-text SEO lead
โ”œโ”€โ”€ markdown-renderer.ts     markdown-it + plugin allowlist; mermaid fenceโ†’<pre>
โ”œโ”€โ”€ reader-intelligence-guide.ts  Reader Guide HTML overlay
โ”œโ”€โ”€ reader-guide-constants.ts     Section IDs / titles for the Reader Guide
โ”œโ”€โ”€ artifacts/               Public re-exports for artifact ordering + cleaning
โ”‚   โ”œโ”€โ”€ index.ts             Barrel
โ”‚   โ””โ”€โ”€ types.ts             ArtifactContent, CleanedArtifactWithPath, ResolvedSection
โ”œโ”€โ”€ cli/                     CLI argument parsing
โ”‚   โ”œโ”€โ”€ index.ts             Barrel
โ”‚   โ””โ”€โ”€ parse.ts             parseArgs() โ€” slug, run-dir, --all flags
โ”œโ”€โ”€ clean/                   Artifact cleaning pipeline (โ‰ค 400 LOC each)
โ”‚   โ”œโ”€โ”€ dedupe-mermaid.ts    Deduplicate Mermaid diagram blocks
โ”‚   โ”œโ”€โ”€ demote-headings.ts   Demote H1โ†’H2 in merged artifacts
โ”‚   โ”œโ”€โ”€ pipeline.ts          cleanArtifact() orchestrator
โ”‚   โ”œโ”€โ”€ rewrite-links.ts     Rewrite relative links to absolute URLs
โ”‚   โ”œโ”€โ”€ strip-banners.ts     Strip SPDX/banner front matter
โ”‚   โ”œโ”€โ”€ strip-frontmatter.ts Strip YAML/TOML frontmatter blocks
โ”‚   โ”œโ”€โ”€ strip-preamble.ts    Strip repeated-title preambles
โ”‚   โ””โ”€โ”€ strip-spdx.ts        Strip SPDX licence identifiers
โ”œโ”€โ”€ content/                 Lead / takeaways / intelligence-guide re-exports
โ”‚   โ”œโ”€โ”€ index.ts             Barrel
โ”‚   โ””โ”€โ”€ types.ts             ExtractedLead, SynthesisedTakeaway, KeyTakeawaysResult
โ”œโ”€โ”€ generator/               Rendering entry points and CLI (โ‰ค 400 LOC each)
โ”‚   โ”œโ”€โ”€ cli.ts               CLI argument parsing and dispatch
โ”‚   โ”œโ”€โ”€ discovery.ts         Analysis run discovery helpers
โ”‚   โ”œโ”€โ”€ reader-guide-insertion.ts  Reader Guide insertion into HTML
โ”‚   โ”œโ”€โ”€ render-batch.ts      Batch render orchestrator
โ”‚   โ”œโ”€โ”€ render-one.ts        Single-run render orchestrator
โ”‚   โ””โ”€โ”€ slug.ts              Slug derivation for generator
โ”œโ”€โ”€ html/                    HTML fragment builders (โ‰ค 400 LOC each)
โ”‚   โ”œโ”€โ”€ analysis-index-cards.ts  Analysis artifact index card HTML
โ”‚   โ”œโ”€โ”€ headline.ts          Article headline + byline HTML
โ”‚   โ”œโ”€โ”€ hreflang.ts          Hreflang alternate link elements
โ”‚   โ”œโ”€โ”€ localize-body.ts     RTL/LTR body localisation wrapper
โ”‚   โ”œโ”€โ”€ shell.ts             Full HTML5 shell (header + body + footer)
โ”‚   โ”œโ”€โ”€ toc.ts               Table of contents builder
โ”‚   โ””โ”€โ”€ tradecraft-cards.ts  Tradecraft methodology panel cards
โ”œโ”€โ”€ infra/                   Infrastructure helpers
โ”‚   โ””โ”€โ”€ github-urls.ts       githubBlobUrl / githubRawUrl helpers
โ”œโ”€โ”€ manifest/                manifest.json schema and I/O
โ”‚   โ”œโ”€โ”€ index.ts
โ”‚   โ”œโ”€โ”€ manifest-writer.ts
โ”‚   โ”œโ”€โ”€ reader.ts
โ”‚   โ”œโ”€โ”€ resolver.ts
โ”‚   โ””โ”€โ”€ types.ts
โ”œโ”€โ”€ markdown/                Markdown rendering public re-exports
โ”‚   โ””โ”€โ”€ index.ts             Barrel โ†’ markdown-renderer.ts
โ”œโ”€โ”€ markdown-it-plugins.d.ts Ambient types for markdown-it plugin allowlist
โ”œโ”€โ”€ metadata/                5-tier editorial-highlight resolver
โ”‚   โ”œโ”€โ”€ artifact-highlight.ts        Manifest-override highlight resolution
โ”‚   โ”œโ”€โ”€ artifact-walker.ts           Recursive run-dir artefact walker
โ”‚   โ”œโ”€โ”€ date-labels.ts               Localized date string helpers
โ”‚   โ”œโ”€โ”€ editorial-highlight.ts       Primary editorial-artefact resolver
โ”‚   โ”œโ”€โ”€ h1-extractor.ts              First-artifact H1 extraction
โ”‚   โ”œโ”€โ”€ heading-rules.ts             Heading promotion rules
โ”‚   โ”œโ”€โ”€ index.ts                     Public barrel
โ”‚   โ”œโ”€โ”€ lede-extractor.ts            Plain-text SEO lead extraction
โ”‚   โ”œโ”€โ”€ priority-finding-highlight.ts  "First strong prose" resolver
โ”‚   โ”œโ”€โ”€ resolve-helpers.ts           Shared resolution helpers
โ”‚   โ”œโ”€โ”€ slug.ts                      Article slug from manifest
โ”‚   โ”œโ”€โ”€ template-fallback.ts         Localized template fallback resolver
โ”‚   โ”œโ”€โ”€ text-utils.ts                Plain-text strip utilities
โ”‚   โ”œโ”€โ”€ translated-sibling.ts        Translated executive-brief sibling predicate
โ”‚   โ””โ”€โ”€ types.ts                     ResolvedMetadata interface
โ”œโ”€โ”€ reader-guide/            Reader Guide methodology panel
โ”‚   โ”œโ”€โ”€ builder.ts           buildReaderGuide() HTML assembler
โ”‚   โ”œโ”€โ”€ icons.ts             SVG icons for the Reader Guide
โ”‚   โ”œโ”€โ”€ labels.ts            Localized label constants
โ”‚   โ”œโ”€โ”€ rows-core.ts         Core methodology row builders
โ”‚   โ”œโ”€โ”€ rows-extended.ts     Extended methodology row builders
โ”‚   โ”œโ”€โ”€ rows-types.ts        Row type definitions
โ”‚   โ”œโ”€โ”€ rows.ts              Row assembly barrel
โ”‚   โ””โ”€โ”€ strip.ts             Reader Guide HTML stripping helpers
โ”œโ”€โ”€ run/                     Per-run rendering helpers (โ‰ค 400 LOC each)
โ”‚   โ”œโ”€โ”€ analysis-index.ts    Analysis index card data builder
โ”‚   โ”œโ”€โ”€ index.ts             Public barrel
โ”‚   โ”œโ”€โ”€ manifest.ts          Run manifest loading helpers
โ”‚   โ”œโ”€โ”€ provenance.ts        Provenance & Audit block generator
โ”‚   โ”œโ”€โ”€ reader-guide.ts      Reader Guide HTML insertion helpers
โ”‚   โ”œโ”€โ”€ section-expansion.ts Supplementary section expansion
โ”‚   โ””โ”€โ”€ tradecraft.ts        Tradecraft card data builder
โ”œโ”€โ”€ runs/                    Run discovery and collision detection
โ”‚   โ”œโ”€โ”€ discover.ts          Run-directory discovery
โ”‚   โ”œโ”€โ”€ grouping.ts          Collision detection across runs
โ”‚   โ””โ”€โ”€ index.ts             Barrel
โ””โ”€โ”€ slug/                    Article URL slug generation
    โ”œโ”€โ”€ index.ts             Barrel
    โ””โ”€โ”€ slug.ts              deriveArticleSlug()

Static-Site Generators (src/generators/)

Generators produce index pages, sitemaps, and the Political Intelligence Hub. Large monolithic files were split into per-concern sub-modules during Refactor 8/8.

src/generators/
โ”œโ”€โ”€ news-indexes.ts          Top-level orchestrator + barrel
โ”œโ”€โ”€ news-indexes/            Per-language index page sub-modules
โ”‚   โ”œโ”€โ”€ per-language.ts      HTML composer for a single language index
โ”‚   โ””โ”€โ”€ backfill.ts          SEO/hreflang/JSON-LD healing for legacy articles
โ”œโ”€โ”€ sitemap.ts               Sitemap orchestrator
โ”œโ”€โ”€ sitemap/                 Sitemap generation sub-modules
โ”‚   โ”œโ”€โ”€ copy.ts              Sitemap copy helpers
โ”‚   โ”œโ”€โ”€ html.ts              Per-language sitemap_<lang>.html generation
โ”‚   โ”œโ”€โ”€ index.ts             Barrel
โ”‚   โ”œโ”€โ”€ rss.ts               RSS feed generation
โ”‚   โ”œโ”€โ”€ xml-utils.ts         XML escaping and formatting utilities
โ”‚   โ””โ”€โ”€ xml.ts               sitemap.xml generation
โ”œโ”€โ”€ political-intelligence.ts  Political Intelligence Hub entry point
โ”œโ”€โ”€ political-intelligence-descriptions.ts  Thin barrel โ†’ political-intelligence/descriptions/
โ”œโ”€โ”€ political-intelligence/  Political Intelligence Hub
โ”‚   โ”œโ”€โ”€ copy.ts              Thin barrel โ†’ copy/index
โ”‚   โ”œโ”€โ”€ copy/                Per-language-group landing-page copy
โ”‚   โ”‚   โ”œโ”€โ”€ eu-core.ts
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts
โ”‚   โ”‚   โ”œโ”€โ”€ nordic.ts
โ”‚   โ”‚   โ”œโ”€โ”€ other.ts
โ”‚   โ”‚   โ””โ”€โ”€ types.ts
โ”‚   โ”œโ”€โ”€ descriptions/        Per-category curated descriptions and titles
โ”‚   โ”‚   โ”œโ”€โ”€ artifact-info.ts             FEED_PREFIX_LABELS, ORPHAN_ARTIFACT_INFO
โ”‚   โ”‚   โ”œโ”€โ”€ curated-descriptions.ts      CURATED_DESCRIPTIONS aggregator
โ”‚   โ”‚   โ”œโ”€โ”€ curated-titles.ts            CURATED_TITLES aggregator
โ”‚   โ”‚   โ”œโ”€โ”€ desc-methodologies.ts        Methodology descriptions
โ”‚   โ”‚   โ”œโ”€โ”€ desc-references.ts           Reference-doc descriptions
โ”‚   โ”‚   โ”œโ”€โ”€ desc-templates.ts            Template descriptions
โ”‚   โ”‚   โ”œโ”€โ”€ fallback.ts                  Per-language kind-words fallbacks
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts                     Public barrel
โ”‚   โ”‚   โ”œโ”€โ”€ lookup.ts                    getCuratedDescription / getCuratedTitle
โ”‚   โ”‚   โ”œโ”€โ”€ run-types.ts                 parseRunSlug, getRunTypeInfo helpers
โ”‚   โ”‚   โ”œโ”€โ”€ run-types-descriptions.ts    Per-run-type descriptions
โ”‚   โ”‚   โ”œโ”€โ”€ run-types-titles.ts          Per-run-type titles
โ”‚   โ”‚   โ”œโ”€โ”€ titles-methodologies.ts      Methodology titles
โ”‚   โ”‚   โ”œโ”€โ”€ titles-references.ts         Reference titles
โ”‚   โ”‚   โ”œโ”€โ”€ titles-templates-a.ts        Template titles (group A)
โ”‚   โ”‚   โ”œโ”€โ”€ titles-templates-b.ts        Template titles (group B)
โ”‚   โ”‚   โ””โ”€โ”€ types.ts                     Shared TextI18n / CuratedDescription types
โ”‚   โ”œโ”€โ”€ data.ts              Data aggregation helpers
โ”‚   โ”œโ”€โ”€ html.ts              HTML fragment builders
โ”‚   โ”œโ”€โ”€ icons.ts             SVG icon library
โ”‚   โ”œโ”€โ”€ index.ts             Public barrel
โ”‚   โ”œโ”€โ”€ markdown.ts          Markdown rendering helpers
โ”‚   โ””โ”€โ”€ types.ts             Political-intelligence type definitions
โ”œโ”€โ”€ seo-copy.ts              SEO meta-tag copy tables (14 languages)
โ””โ”€โ”€ shared/                  Shared generator utilities
    โ”œโ”€โ”€ html-escape.ts       Local HTML-escape helper
    โ”œโ”€โ”€ index.ts             Barrel
    โ”œโ”€โ”€ template-helpers.ts  Shared template composition helpers
    โ””โ”€โ”€ types.ts             Shared type definitions

Template Section Builders (src/templates/)

Per-section HTML builders for the shared site chrome. Each builder handles exactly one UI section and stays under 400 code lines.

src/templates/
โ”œโ”€โ”€ icons.ts                 SVG icon library (moon, sun, link, etc.)
โ”œโ”€โ”€ section-builders.ts      Thin barrel re-exporting from sections/
โ””โ”€โ”€ sections/                Per-section builders (โ‰ค 400 LOC each)
    โ”œโ”€โ”€ banner.ts            buildPageBanner โ€” top-of-page editorial banner
    โ”œโ”€โ”€ comparison.ts        Before/after comparison table builder
    โ”œโ”€โ”€ footer.ts            buildSiteFooter โ€” Hack23 AB footer (14 languages)
    โ”œโ”€โ”€ header.ts            buildSiteHeader โ€” stacked site header + nav
    โ”œโ”€โ”€ key-figures.ts       Key-figures bar builder
    โ”œโ”€โ”€ quality-score.ts     Quality scoring + badge rendering
    โ”œโ”€โ”€ timeline.ts          Timeline section builder
    โ””โ”€โ”€ toc.ts               TOC entry builder (TOCEntry, TOC_ARIA_LABELS)

Agentic Workflow Support (src/workflows/)

Bounded contexts that back the agentic-workflow pipeline (Stage C completeness gate, Stage E safe-output PR constraints, and shell-safety infrastructure validators). Extracted from former scripts/validate-*.js ad-hoc scripts so TypeScript types now flow through the entire manifest.json lifecycle.

src/workflows/
โ”œโ”€โ”€ index.ts                       Public barrel
โ”œโ”€โ”€ types.ts                       Shared workflow contract types
โ”‚                                  (DataMode, GateVerdict, StageHistoryEntry, โ€ฆ)
โ”œโ”€โ”€ completeness-gate/             Stage C validation (was validate-analysis-completeness.js)
โ”‚   โ”œโ”€โ”€ constants.ts               Per-slug thresholds and required-artefact catalogues
โ”‚   โ”œโ”€โ”€ index.ts                   Public barrel
โ”‚   โ”œโ”€โ”€ types.ts                   StageCVerdict, ValidationIssue, ArtifactValidationResult
โ”‚   โ””โ”€โ”€ validators.ts              Per-artefact validators
โ”œโ”€โ”€ infrastructure/                Shell safety + runtime guards
โ”‚   โ”œโ”€โ”€ index.ts                   Public barrel
โ”‚   โ””โ”€โ”€ shell-safety.ts            Banned-pattern matchers (eval, ${var@P}, โ€ฆ)
โ””โ”€โ”€ safe-outputs/                  Stage E PR creation constraints
    โ”œโ”€โ”€ index.ts                   Public barrel
    โ””โ”€โ”€ types.ts                   SafeOutputConstraints, PRCreationVerdict

See WORKFLOWS.md for the full bounded-context architecture and the gh-aw integration story.

File-Size Enforcement Summary

PatternMax code linesEnforcement
src/**/*.ts (global)600ESLint max-lines + test/unit/source-file-size.test.js (raw lines)
src/types/**400ESLint max-lines per-area override
src/config/**400ESLint max-lines per-area override
src/aggregator/clean/**400ESLint max-lines per-area override
src/aggregator/run/**400ESLint max-lines per-area override
src/aggregator/html/**400ESLint max-lines per-area override
src/aggregator/generator/**400ESLint max-lines per-area override
src/templates/sections/**400ESLint max-lines per-area override
src/mcp/transport/**400ESLint max-lines per-area override

Areas with current files between 400โ€“600 code lines (src/utils/**, src/aggregator/reader-guide/**, src/workflows/completeness-gate/**) are protected by the global 600-line cap only, pending a future refactor that brings them below the 400-line ceiling.


๐Ÿ”„ Data Flow

News Generation Flow (End-to-End)

sequenceDiagram
    participant GHA as GitHub Actions (gh-aw)
    participant Agent as Copilot Agent (Claude Opus 4.8)
    participant MCP as EP MCP Client
    participant EP as EP MCP Server (stdio)
    participant IMF as IMF SDMX 3.0 (HTTPS)
    participant FS as File System
    participant AGG as Aggregator CLI

    GHA->>Agent: Start agentic workflow (news-<type>.md)
    Note over GHA,Agent: Stage A โ€” Data Collection (~5 min)
    Agent->>MCP: Call EP tools (get_plenary_sessions, get_voting_records, etc.)
    MCP->>EP: JSON-RPC via stdio
    EP-->>MCP: EP data response
    MCP-->>Agent: Structured data
    Agent->>IMF: fetch() SDMX 3.0 (WEO, FM, IFS)
    IMF-->>Agent: Economic context data

    Note over Agent,FS: Stage B โ€” Analysis (2-pass, ~22 min)
    Agent->>FS: Write analysis artifacts (intelligence/, classification/, risk-scoring/, extended/)
    Agent->>FS: Write manifest.json

    Note over Agent,FS: Stage C โ€” Completeness Gate (~4 min)
    Agent->>FS: Read artifacts + grade against reference-quality-thresholds.json

    Note over Agent,AGG: Stage D โ€” Article Rendering (~2 min)
    Agent->>AGG: npm run generate-article -- --run analysis/daily/<date>/<type>
    AGG->>FS: Read manifest.json + all .md artifacts
    AGG->>FS: Write 14 HTML files + article.md + article-meta.json

    Note over Agent,GHA: Stage E โ€” Single PR (~2 min)
    Agent->>GHA: safeoutputs create_pull_request (max: 1)
    GHA->>GHA: Deploy to S3 + CloudFront invalidation on merge

User Request Flow

sequenceDiagram
    participant User as User Browser
    participant CDN as CloudFront CDN
    participant S3 as Amazon S3
    participant Repo as Git Repository

    User->>CDN: GET /index.html
    CDN->>S3: Forward request (cache miss)
    S3-->>CDN: HTML response
    CDN-->>User: Cached HTML

    User->>CDN: GET /news/week-ahead-2026-02-17-en.html
    CDN-->>User: Cached article (or fetch from S3)

๐Ÿ”€ Cross-Cutting Concerns

Cross-cutting concerns are aspects of the system that affect multiple components and layers. These concerns are implemented consistently across the entire architecture.

Logging Strategy

Logging Levels:

LevelUsageOutputRetention
ERRORUnrecoverable errors (API failures, file write errors)console.error(), GitHub Actions logs90 days (GitHub)
WARNRecoverable issues (MCP connection retry/backoff, MCP tool fallback, JSON.parse recovery)console.warn(), GitHub Actions logs90 days (GitHub)
INFONormal operations (generation start/complete, article count)console.log(), GitHub Actions logs90 days (GitHub)
DEBUGDetailed diagnostics (API responses, intermediate data)Disabled in productionDev only

Structured Logging Format:

{
  timestamp: "2026-02-20T10:30:00.000Z",
  level: "INFO",
  component: "ArticleGenerator",
  action: "generate_article",
  language: "en",
  article_type: "week-ahead",
  duration_ms: 1234,
  status: "success"
}

Logging Implementation:

  • Build Logs: All GitHub Actions workflow logs (generation, deployment, tests)
  • Error Tracking: Errors logged to GitHub Actions workflow logs for visibility
  • Performance Metrics: Generation time per article, API call durations
  • Audit Trail: Git commit history serves as audit log for all content changes

Monitoring and Observability

graph TB
    subgraph "Generation Monitoring"
        Workflow[GitHub Actions Workflow]
        GenMetrics[Generation Metrics<br/>Article count, Duration, Errors]
        TestResults[Test Results<br/>Unit, Integration, E2E]
    end
    
    subgraph "Application Monitoring"
        Pages[Amazon CloudFront + S3]
        Analytics[Web Analytics<br/>Visits, Bounce Rate, Countries]
        Uptime[Uptime Monitoring<br/>AWS Health Dashboard]
    end
    
    subgraph "Security Monitoring"
        Dependabot[Dependabot Alerts]
        CodeQL[CodeQL Security Scans]
        Audit[npm audit]
    end
    
    subgraph "Alerting"
        Email[Email Notifications]
        GitHubUI[GitHub UI Alerts]
        Status[Status Checks]
    end
    
    Workflow -->|Logs| GenMetrics
    Workflow -->|Results| TestResults
    Pages -->|Metrics| Analytics
    Pages -->|Health| Uptime
    Dependabot -->|Alerts| Email
    CodeQL -->|Findings| GitHubUI
    Audit -->|Vulnerabilities| Status
    
    GenMetrics -->|Failures| Email
    TestResults -->|Failures| Status
    
    style Dependabot fill:#f99,stroke:#333,stroke-width:2px
    style CodeQL fill:#f99,stroke:#333,stroke-width:2px

Monitoring Tools:

MetricToolThresholdAlert
Build Success RateGitHub Actions<95% over 7 daysEmail to maintainers
Generation DurationWorkflow logs>15 minutesWarning annotation
Test Pass RateVitest + Playwright<100%Block merge
Security VulnerabilitiesDependabot + CodeQLAny high/criticalEmail + PR
Site AvailabilityAWS Health Dashboard<99.9%AWS Health event notification
Page Load TimeLighthouse (manual runs)>3 secondsWarning annotation

Error Handling

Error Handling Strategy:

flowchart TD
    Start([API Call / Operation])
    Try{Try Operation}
    Success[โœ… Success]
    Catch{Catch Error}
    Transient{Transient<br/>Error?}
    Retry[Retry with<br/>Exponential Backoff]
    MaxRetries{Max Retries<br/>Reached?}
    Fallback{Fallback<br/>Available?}
    UseFallback[Use Fallback Data]
    LogError[Log Error]
    PropagateError[Propagate Error]
    GracefulDegradation[Graceful Degradation]
    
    Start --> Try
    Try -->|Success| Success
    Try -->|Error| Catch
    Catch --> Transient
    Transient -->|Yes| Retry
    Transient -->|No| Fallback
    Retry --> MaxRetries
    MaxRetries -->|No| Try
    MaxRetries -->|Yes| Fallback
    Fallback -->|Yes| UseFallback
    Fallback -->|No| LogError
    UseFallback --> GracefulDegradation
    LogError --> PropagateError
    
    style Success fill:#9f9,stroke:#333,stroke-width:2px
    style LogError fill:#f99,stroke:#333,stroke-width:2px
    style PropagateError fill:#f99,stroke:#333,stroke-width:2px
    style GracefulDegradation fill:#ff9,stroke:#333,stroke-width:2px

Error Categories and Handling:

Error CategoryExamplesRetry StrategyFallbackUser Impact
Transient Network ErrorsMCP connection failure during startup, LLM API rate limitExponential backoff (1s, 2s, 4s), max 3 retries for MCP connection establishment and LLM calls; individual MCP requests use a single fixed timeout with no retryUse placeholder events or skip affected items (no cache)Missing or placeholder content for affected items
Permanent API ErrorsInvalid API key, malformed requestNo retrySkip article generation for affected languageMissing article for specific language
Data Validation ErrorsInvalid EP data structure, missing required fieldsNo automatic regeneration loopSkip invalid items (no cached-data fallback)Missing content for invalid items
File System ErrorsDisk full, permission deniedNo retryFail workflowBuild failure (no deployment)
Content Generation ErrorsLLM refusal, prompt injection detectedSingle generation attempt (no automatic regeneration loop)Insert placeholder events when content generation failsReduced content quality or placeholder content

Error Propagation:

  1. Component Level: Catch and log errors, attempt recovery
  2. Service Level: Propagate if unrecoverable, aggregate errors for reporting
  3. Workflow Level: Fail fast if critical (file system), continue if non-critical (single article failure)

Internationalization (i18n)

14 Languages Supported:

  • ๐Ÿ‡ฌ๐Ÿ‡ง English (en) - 67 million
  • ๏ฟฝ๐Ÿ‡ช Swedish (sv) - 10 million
  • ๐Ÿ‡ฉ๐Ÿ‡ฐ Danish (da) - 6 million
  • ๐Ÿ‡ณ๐Ÿ‡ด Norwegian (no) - 5 million
  • ๐Ÿ‡ซ๐Ÿ‡ฎ Finnish (fi) - 5 million
  • ๐Ÿ‡ฉ๐Ÿ‡ช German (de) - 95 million
  • ๐Ÿ‡ซ๐Ÿ‡ท French (fr) - 67 million
  • ๐Ÿ‡ช๐Ÿ‡ธ Spanish (es) - 47 million
  • ๐Ÿ‡ณ๐Ÿ‡ฑ Dutch (nl) - 24 million
  • ๐Ÿ‡ธ๐Ÿ‡ฆ Arabic (ar) - 420 million
  • ๐Ÿ‡ฎ๐Ÿ‡ฑ Hebrew (he) - 9 million
  • ๐Ÿ‡ฏ๐Ÿ‡ต Japanese (ja) - 125 million
  • ๐Ÿ‡ฐ๐Ÿ‡ท Korean (ko) - 77 million
  • ๐Ÿ‡จ๐Ÿ‡ณ Chinese (zh) - 1.3 billion

i18n Architecture:

graph LR
    subgraph "Content Generation"
        EPData[EP Data<br/>Language-Neutral]
        LLM[LLM Service]
        Prompt[Language-Specific Prompt]
    end
    
    subgraph "14 Language Variants"
        EN[English Article]
        SV[Swedish Article]
        DA[Danish Article]
        NO[Norwegian Article]
        FI[Finnish Article]
        DE[German Article]
        FR[French Article]
        ES[Spanish Article]
        NL[Dutch Article]
        AR[Arabic Article]
        HE[Hebrew Article]
        JA[Japanese Article]
        KO[Korean Article]
        ZH[Chinese Article]
    end
    
    subgraph "Delivery"
        Index[Language-Specific<br/>Index Pages]
        Sitemap[Multilingual<br/>Sitemap.xml]
    end
    
    EPData --> LLM
    Prompt --> LLM
    LLM --> EN
    LLM --> SV
    LLM --> DA
    LLM --> NO
    LLM --> FI
    LLM --> DE
    LLM --> FR
    LLM --> ES
    LLM --> NL
    LLM --> AR
    LLM --> HE
    LLM --> JA
    LLM --> KO
    LLM --> ZH
    
    EN --> Index
    DE --> Index
    FR --> Index
    ES --> Index
    Index --> Sitemap
    
    style EPData fill:#9cf,stroke:#333,stroke-width:2px
    style LLM fill:#fc9,stroke:#333,stroke-width:2px

i18n Implementation:

AspectImplementationExample
Content GenerationPlaceholder English content for all languages (current); native LLM per-language generation planned (ADR-004)Current: shared English body with localized titles/subtitles; Future: each article written directly in target language
File NamingLanguage suffix in filenameweek-ahead-2026-02-17-en.html, week-ahead-2026-02-17-de.html
HTML lang AttributeSet per page<html lang="en">, <html lang="de">
NavigationLanguage-specific index pagesindex.html, index-de.html
SEOhreflang tags for alternate languages<link rel="alternate" hreflang="de" href="...">
Date FormattingLocale-specific date formatsEN: "February 17, 2026", DE: "17. Februar 2026"
Character EncodingUTF-8 for all languages<meta charset="UTF-8">

Language Quality Assurance:

  • Current State: Placeholder English body content with localized metadata (title, subtitle, HTML lang attribute, date formats) per language
  • Target State (ADR-004): LLM generates content natively in each language (not machine translation)
  • Cultural Adaptation: Planned โ€” prompts will include cultural context for each language/region
  • Terminology Consistency: EP terminology to be used consistently per language
  • Quality Metrics: Human review of sample articles per language quarterly


๐Ÿ“ Architecture Decision Records (ADR)

Architecture Decision Records document significant architectural decisions made during the design and development of EU Parliament Monitor. Each ADR captures the context, decision, and consequences of a specific architectural choice.

ADR-001: Static Site Architecture over Dynamic Web Application

Status: Accepted
Date: 2025-12-01
Decision Makers: CEO, Development Team

Context:

  • Need to display European Parliament news to public audience
  • Security is paramount (public-facing system)
  • Limited development resources
  • GitHub Pages available as free hosting solution; AWS S3 + CloudFront chosen for production (see ADR-002)

Decision: We will build EU Parliament Monitor as a static site generator rather than a dynamic web application with backend services.

Rationale:

  1. Security: Static sites eliminate entire classes of vulnerabilities (SQL injection, XSS via server-side rendering, authentication bypass)
  2. Scalability: Static content scales infinitely via CDN with no server infrastructure
  3. Cost: Static hosting on AWS S3 + CloudFront is low-cost, no server infrastructure
  4. Maintainability: Simpler architecture with fewer moving parts
  5. Reliability: No database or server downtime risks

Alternatives Considered:

  • WordPress: Rejected due to security vulnerabilities, plugin maintenance overhead
  • Node.js/Express backend: Rejected due to hosting costs, operational complexity
  • JAMstack with headless CMS: Rejected due to unnecessary complexity for simple content

Consequences:

  • โœ… Positive: Minimal attack surface, zero infrastructure costs, infinite scalability
  • โœ… Positive: Fast page loads, excellent SEO, simple deployment
  • โš ๏ธ Negative: Content updates require regeneration (acceptable for daily news)
  • โš ๏ธ Negative: No real-time interactivity (not required for news consumption)

Compliance: Aligns with ISO 27001 A.8.28 (Secure Development), NIST CSF PR.DS-5 (Minimal Attack Surface)


ADR-002: AWS S3 + CloudFront for Hosting

Status: Accepted
Date: 2025-12-05
Decision Makers: CEO, DevOps Team

Context:

  • Static site architecture chosen (ADR-001)
  • Need reliable, secure hosting with global CDN
  • Budget constraints (low-cost solution preferred)
  • Already using GitHub for source control and CI/CD

Decision: We will host EU Parliament Monitor on AWS S3 with Amazon CloudFront as the global CDN (see .github/workflows/deploy-s3.yml).

Rationale:

  1. Cost: Low-cost static hosting within current traffic and budget constraints
  2. Integration: GitHub Actions CI/CD deploys to S3 and invalidates the CloudFront distribution
  3. Security: HTTPS via AWS Certificate Manager, TLS termination at CloudFront edge
  4. Reliability: AWS S3 and CloudFront SLAs provide high availability and durability
  5. Performance: CloudFront global edge network with caching for low-latency delivery

Alternatives Considered:

  • GitHub Pages: Considered for simplicity and zero direct hosting cost; kept as a documented alternative but not chosen due to less flexible edge configuration
  • Netlify: Rejected due to build minute limits on free tier
  • Vercel: Rejected due to commercial focus, potential future costs
  • Self-hosted Nginx: Rejected due to operational burden, security maintenance

Consequences:

  • โœ… Positive: Globally distributed static hosting with strong reliability and performance
  • โœ… Positive: Automated deployments from GitHub Actions to S3 with CloudFront cache invalidation
  • โœ… Positive: Integration with AWS security services (WAF, Shield, ACM)
  • โš ๏ธ Negative: Ongoing AWS hosting costs and need to manage AWS credentials securely
  • โš ๏ธ Negative: Increased operational complexity compared to GitHub Pages

Compliance: Aligns with ISO 27001 A.8.24 (Cryptography - HTTPS), CIS Control 1 (Asset Management)


ADR-003: Model Context Protocol (MCP) for European Parliament Data Access

Status: Accepted
Date: 2025-12-10
Decision Makers: CEO, Data Team

Context:

  • Need structured access to European Parliament data (MEPs, plenary sessions, votes, documents)
  • Official EP APIs are fragmented, inconsistent, and poorly documented
  • Data schemas vary across endpoints
  • Need caching, validation, and error handling

Decision: We will access European Parliament data via the European Parliament MCP Server using the Model Context Protocol (MCP) rather than calling official EP APIs directly.

Rationale:

  1. Abstraction: MCP Server provides unified interface to fragmented EP APIs
  2. Data Normalization: Consistent data structures across EP data sources
  3. Error Handling: Connection retry logic and graceful degradation
  4. Maintainability: API changes isolated to MCP Server, not news generator
  5. Local Process: Spawned as stdio JSON-RPC process during build, no separate deployment needed

Alternatives Considered:

  • Direct EP API calls: Rejected due to fragmentation, lack of validation, poor error handling
  • Custom wrapper library: Rejected due to development overhead, maintenance burden
  • Third-party EP data services: Rejected due to cost, data freshness concerns

Consequences:

  • โœ… Positive: Clean separation of concerns, reusable data layer
  • โœ… Positive: Standardized data structures, no direct EP API fragmentation
  • โœ… Positive: MCP Server maintained separately, used by multiple clients
  • โš ๏ธ Negative: Additional dependency (mitigated by fallback data strategy)
  • โš ๏ธ Negative: Requires MCP Server process availability during build

Compliance: Aligns with ISO 27001 A.8.3 (Input Validation), NIST CSF PR.DS-2 (Data in Transit Protection)


ADR-004: Multi-Language Content via LLM Generation (Not Translation)

Status: Accepted
Date: 2025-12-15
Decision Makers: CEO, Content Team

Context:

  • Need to support 14 languages
  • Machine translation often produces unnatural, awkward phrasing
  • European Parliament terminology requires domain expertise
  • Budget available for LLM API costs

Decision: We will generate content natively in each language using LLMs rather than translating from a base language.

Rationale:

  1. Quality: Native generation produces natural, idiomatic language
  2. Cultural Adaptation: LLM can adapt content for cultural context per language
  3. Terminology: LLM trained on EP documents uses correct terminology
  4. Flexibility: Different article structures possible per language/culture
  5. Scalability: Parallel generation for all languages

Alternatives Considered:

  • Machine Translation (Google Translate, DeepL): Rejected due to unnatural phrasing, terminology issues
  • Human Translation: Rejected due to cost (~โ‚ฌ0.10/word x 14 languages), time delays
  • English-only: Rejected due to accessibility concerns, limited audience

Consequences:

  • โœ… Positive: High-quality, natural language content in all 14 languages
  • โœ… Positive: Cultural adaptation, correct terminology
  • โš ๏ธ Negative: Higher LLM API costs ($5-10/day) vs translation ($1-2/day)
  • โš ๏ธ Negative: Content may vary slightly across languages (acceptable, even beneficial)

Compliance: Aligns with Hack23 AI Policy (Transparency, Human Oversight), ISO 27001 A.5.10 (Information Processing)


ADR-005: TypeScript with Strict Mode for Type Safety

Status: Accepted
Date: 2026-01-05
Decision Makers: CEO, Development Team

Context:

  • Building news generation scripts and static site generator
  • Need compile-time type safety for complex data structures from EP MCP Server
  • Multiple article categories, 14 languages, and complex data pipelines
  • Small development team (1-2 developers) benefits from IDE support

Decision: We will use TypeScript (strict mode) as the primary development language, compiling from src/ to scripts/ targeting ES2025.

Rationale:

  1. Type Safety: Strict mode catches errors at compile time, especially important for complex EP data structures and MCP client interfaces
  2. IDE Support: Full IntelliSense, refactoring, and navigation in VS Code
  3. Self-Documenting: TypeScript interfaces serve as living documentation for data models (ArticleCategory, LanguageCode, MCPToolResult, etc.)
  4. Build Pipeline: tsc compiles src/*.ts โ†’ scripts/*.js; rootDir: ./src, outDir: ./scripts, target: ES2025, module: NodeNext
  5. Ecosystem: Full access to Node.js and npm ecosystem with type definitions

Alternatives Considered:

  • JavaScript (ES2025) with JSDoc: Rejected due to weaker type guarantees, less comprehensive IDE support for complex interfaces
  • Flow: Rejected due to declining community support
  • JavaScript ES2015: Rejected due to lack of modern features (optional chaining, nullish coalescing)

Consequences:

  • โœ… Positive: Compile-time error detection, comprehensive IDE support, self-documenting code
  • โœ… Positive: Strict null checks prevent runtime errors with optional EP data fields
  • โš ๏ธ Negative: Requires build step (npm run build / tsc) before execution
  • โš ๏ธ Negative: Slightly higher learning curve for contributors unfamiliar with TypeScript

Compliance: Aligns with Hack23 Secure Development Policy (Type Safety Principle), ISO 27001 A.8.28 (Secure Coding)


ADR-006: Week-in-Review Analysis Window โ€” D-36 โ†’ D-8

Status: Accepted
Date: 2026-04-27
Decision Makers: CEO, Development Team

Context:

  • The EP publishes roll-call voting records with a 2โ€“6 week lag after each plenary sitting.
  • The previous week-in-review data window was D-0 โ†’ D-7 (the most-recent 7 days).
  • A D-0โ†’D-7 window structurally never contains published voting data, making the article vote-blind in every run regardless of content quality โ€” a permanently-empty input.
  • analysis/daily/2026-04-26/week-in-review/intelligence/methodology-reflection.md ยง3.1 recommended shifting to a D-36 โ†’ D-8 window to systematically capture voting data.

Decision: We shift the week-in-review analysis window to D-36 โ†’ D-8 (start = D-36, end = D-8 โ€” a 28-day window ending 8 days ago, relative to the run date). This direction matches the workflow's DATE_FROM (start = D-36) โ†’ DATE_TO (end = D-8) variables. It is a 4-week look-back that consistently captures at least one full EP plenary week with published roll-call votes.

Rationale:

  1. Data depth over recency: A vote-populated analysis is more valuable than a vote-empty analysis that is 7 days more recent. Readers of the week-in-review expect vote coverage.
  2. Systematic: The window is deterministic and reproducible โ€” it always yields voting data regardless of EP publication lag variance (2โ€“6 weeks).
  3. Complementary to fallback: This window shift works alongside any future EP Open Data Portal fallback for historical roll-calls; the two are not mutually exclusive.
  4. Article framing updated: The WEEKLY_REVIEW_TITLES subtitles (all 14 languages) now read "last full reporting week" instead of "past week" to accurately describe the shifted window to readers.
  5. SEO metadata: The title date range already shows the exact dateFromโ€“dateTo window, so canonical URLs remain accurate without additional changes.

Alternatives Considered:

  • Keep D-0โ†’D-7 + add EP Open Data Portal fallback query for historical roll-calls: Complementary approach; can be combined with this shift but does not solve the structural vote-empty problem without the window shift.
  • D-8โ†’D-14 (7-day window, offset by 8 days): Narrower window; may miss vote publication for sittings right at the 8-day boundary given the 2โ€“6 week lag variance. Rejected in favour of the wider 28-day window.

Consequences:

  • โœ… Positive: Every week-in-review run now reliably contains roll-call voting data.
  • โœ… Positive: Analysis depth improves without increasing Stage B budget.
  • โœ… Positive: Article subtitles accurately describe the reporting window in all 14 languages.
  • โš ๏ธ Trade-off: Articles cover events from 8โ€“36 days ago rather than the most-recent 7 days; the workflow is less "breaking" but more analytically complete.
  • โš ๏ธ Negative: In this ADR, the DATE_FROM / DATE_TO variables replace LAST_WEEK in week-in-review Stage A bash blocks; other workflows still using LAST_WEEK require separate migration if their reporting windows are changed.

Implementation:

  • src/aggregator/article-metadata.ts: New deriveReportingWindowForWeekInReview() export computes D-36/D-8 from the article date; buildTemplateFallback uses it for week-in-review.
  • .github/workflows/news-week-in-review.md: Stage A sets DATE_FROM (D-36) and DATE_TO (D-8); all MCP tool calls use these variables; LAST_WEEK removed.
  • src/constants/language-articles.ts: WEEKLY_REVIEW_TITLES subtitles updated (14 languages).

Compliance: Aligns with Hack23 AI Policy (unambiguous date semantics in published articles), GDPR (accurate published metadata).


ADR-007: Centralised Horizon Registry โ€” src/config/article-horizons.ts

Status: Accepted
Date: 2026-05-01
Decision Makers: CEO, Development Team

Context:

  • The April-2026 long-horizon expansion (PR #1561 / Look-Ahead epic #1562) added 6 new article types โ€” quarter-ahead, year-ahead, term-outlook, election-cycle, quarter-in-review, year-in-review โ€” bringing the total to 14 article types and 15 unified gh-aw workflows.
  • Each new horizon needs to declare its data window, cadence, mandatory artifacts, optional artifacts, stage budgets, scenario depth, forward-statements horizon and electoral-overlay flag.
  • Before this ADR, equivalent metadata for the 8 short-form types was scattered across the workflow .md files, the aggregator (src/aggregator/article-metadata.ts), the forward-statements registry (scripts/aggregator/forward-statements-registry.js), the prompt library (.github/prompts/02-analysis-protocol.md), the per-language title generators (src/constants/language-articles.ts), and analysis/methodologies/reference-quality-thresholds.json.
  • Adding a new horizon required edits in 6+ separate places with no compile-time guarantee that they agreed. A drift between the workflow's mandatory-artifact list and the Stage-C completeness gate's expectations would silently allow the PR through, defeating the gate.

Decision:

Introduce src/config/article-horizons.ts as the single source of truth for every horizon's configuration. Each ArticleCategory enum value maps to one ArticleHorizonConfig entry containing:

  • slug โ€” canonical slug used in workflow filenames, artifact paths and URLs.
  • perspective โ€” RETROSPECTIVE / PROSPECTIVE / ELECTORAL / POINT-IN-TIME.
  • timePeriod โ€” human-readable label for article titles.
  • dataWindow โ€” { direction: 'forward' | 'backward' | 'span' | 'point', days?, anchor } where anchor is today / next-election / commission-wp / term-end.
  • cadence โ€” { cron, description, triggerEvents? } โ€” supports auxiliary triggers like election-imminent-t180.
  • primaryFeeds โ€” EP MCP tools that must be probed in Stage A.
  • mandatoryArtifacts / optionalArtifacts โ€” relative paths under analysis/daily/<date>/<slug>/.
  • stageBudgets โ€” { A, B, C, D, E } minute ceilings (sum โ‰ค 50 โ€” drift-guard test/unit/horizon-registry.test.js).
  • scenarioMaxHorizonMonths โ€” caps the scenario-forecast time window.
  • forwardStatementsHorizonDays โ€” bounds open-statement carry-forward (week-ahead = 14 โ€ฆ election-cycle = 1825).
  • electoralOverlay โ€” when true, Family-D electoral artifacts become mandatory and the scenario-forecast must include an EP-election outcome branch.

The aggregator (src/aggregator/article-metadata.ts via getHorizonConfig(slug)), the forward-statements registry (scripts/aggregator/forward-statements-registry.js), the Stage-C completeness gate metadata (getMandatoryArtifacts(slug), getProspectiveSlugs(), getElectoralOverlaySlugs()), and the drift-guard tests (test/unit/horizon-registry.test.js, test/unit/horizon-snapshot.test.js) all consume this registry directly. There is no parallel registry; if a workflow needs the data, it imports from this module.

Rationale:

  1. One change, one place. Adding a new horizon is now a four-step edit: new ArticleCategory enum value โ†’ new ARTICLE_HORIZONS entry โ†’ per-language title generator in src/constants/language-articles.ts โ†’ new news-<slug>.md workflow. Everything else (Stage-C gate, aggregator, scenario depth, forward-statements decay) follows automatically.
  2. Compile-time safety. TypeScript's exhaustiveness checks force every consumer to handle every ArticleCategory enum value; missing a case in (e.g.) the article metadata fallback now fails the build.
  3. Drift-guard. test/unit/horizon-registry.test.js asserts every horizon's stage-budget sum โ‰ค 50 (within the 60-min cap leaves the 10-min buffer for sandbox setup, MCP gateway boot and the safe-output round-trip), and that the electoralOverlay flag matches the artifact-list family. test/unit/horizon-snapshot.test.js snapshots every horizon's resolved configuration so unintentional changes are flagged at PR time.
  4. Plan ยง3 alignment. The Look-Ahead Plan ยง3.1 mandates a single registry to drive workflow scheduling, completeness-gate validation, and aggregator metadata. This ADR codifies that decision.

Alternatives Considered:

  • YAML / JSON registry under analysis/methodologies/ โ€” rejected because TypeScript exhaustiveness checks across the aggregator, generator, and validator would not catch missing entries; runtime parsing also reduces type safety.
  • One module per horizon under src/config/horizons/<slug>.ts โ€” rejected because cross-horizon comparisons (e.g. "does this slug have an electoral overlay?") would require N imports and the drift-guard tests would lose their single-pass invariant check.
  • Keep distributed metadata, add a runtime validator โ€” rejected because the validator is the very gate we are trying to harden; making the gate authoritative without making the registry authoritative leaves the structural drift.

Consequences:

  • โœ… Positive: Schema for new horizons is reviewed once on the registry edit; all 6 derivative locations update mechanically. The Stage-C gate, aggregator, and forward-statements decay all stay in lock-step.
  • โœ… Positive: A follow-up issue (cross-referenced in Look-Ahead Plan ยง3.1) refactors the runtime validator (scripts/validate-analysis.js) to read from the registry; once shipped, the gate cannot diverge from the workflow declarations even by accident.
  • โš ๏ธ Trade-off: The registry module is large (15 entries ร— ~25 fields each). Mitigated by extracting shared constants (STANDARD_FEEDS, PROSPECTIVE_MANDATORY, RETROSPECTIVE_MANDATORY, LONG_HORIZON_PROSPECTIVE_EXTRA, ELECTORAL_EXTRA, PROSPECTIVE_BUDGETS, RETROSPECTIVE_BUDGETS, ELECTORAL_BUDGETS) so per-horizon entries stay readable.
  • โš ๏ธ Negative: Renaming a slug now requires a coordinated change across the registry, workflow filename, analysis-folder schema, and any committed analysis artifacts under analysis/daily/<date>/<slug>/. Renames are rare; the aggregator emits a clear error when a slug it cannot resolve is encountered.

Implementation:

Compliance: Aligns with Hack23 Secure Development Policy (single source of truth for security-relevant configuration), AI Policy (deterministic, auditable horizon contract), and ISO 27001 A.8.32 (change management โ€” a single review surface for every new horizon).


๐ŸŽฏ Non-Functional Requirements (NFR)

Non-functional requirements define system qualities that are not directly related to specific features but are critical to overall system success.

Performance Requirements

RequirementTargetMeasurementCurrent Status
Page Load Time (Desktop)<1 second (LCP)Lighthouse (manual runs)โœ… 0.6s average
Page Load Time (Mobile)<2 seconds (LCP)Lighthouse (manual runs)โœ… 1.2s average
Build Time (All Languages)<15 minutesGitHub Actions logsโœ… 8-12 minutes
Article Generation (Single)<30 secondsScript logsโœ… 15-25 seconds
MCP API Response Time<2 seconds (p95)Client logsโœ… 1.1s average
CDN Cache Hit Rate>95%CloudFront metrics (planned)โณ TBD โ€” instrumentation planned

Performance Optimization Strategies:

  • Static Content: All content pre-generated, no server-side processing
  • CDN Caching: Tiered caching strategy (1 hour for HTML, 1 day for metadata, 1 year for immutable assets)
  • Image Optimization: None required (no images in MVP)
  • Minification: HTML minification (future), CSS minification (future)
  • HTTP/2: Enabled by default on Amazon CloudFront

Scalability Requirements

DimensionCurrent CapacityTarget CapacityScaling Strategy
Concurrent UsersUnlimited (static content)UnlimitedCDN auto-scales
Daily Visitors10,000+100,000+CDN bandwidth increase
Articles per Day14 (one per language)140 (ten per language)Parallel generation, workflow optimization
Supported Languages1424+ (expanded markets)Add language configs, LLM prompts
Repository Size150 MB800 MB (GitHub limit)Archive old articles annually

Scalability Constraints:

  • AWS S3: No repository size limit for static hosting; storage costs increase linearly
  • GitHub Actions: 2000 minutes/month free, unlimited for public repos
  • LLM API: Rate limits vary by provider (typically 3000 RPM for tier 2)

Availability and Reliability Requirements

RequirementTargetMeasurementConsequence of Failure
Site Availability99.9% (AWS CloudFront/S3 SLA)GitHub Status + AWS Health DashboardUsers cannot access news
Build Success Rate>98%GitHub Actions logsNo new content deployed
MCP API Availability>99% (best effort)Health checksFallback to placeholder events (no cached/previous data)
LLM API Availability>99.5% (provider SLA)API logsGeneration fails, retry logic
Recovery Time Objective (RTO)<15 minutesManual testingTime to restore service after outage
Recovery Point Objective (RPO)<24 hoursGit historyMaximum data loss acceptable

High Availability Strategies:

  • Static Architecture: No single point of failure (SPOF) in runtime
  • CDN Redundancy: Amazon CloudFront with multiple edge locations globally
  • Fallback Data: Use placeholder events if EP MCP Server unavailable (no cache/previous-data reuse)
  • Retry Logic: Exponential backoff for transient failures
  • Monitoring: GitHub Status, Dependabot alerts, workflow notifications

Security Requirements

RequirementImplementationVerificationCompliance
HTTPS-OnlyCloudFront enforces HTTPS redirect via ACM certificateManual testingISO 27001 A.8.24
Content Security Policy (CSP)Planned strict CSP via CloudFront response headers (no CSP meta tag in HTML templates currently)CSP Evaluator (staging/production)ISO 27001 A.8.23
No Secrets in RepositoryGitHub Secrets for API keysGit history scanISO 27001 A.8.3
Dependency Vulnerability ScanningDependabot daily scansGitHub Security tabCIS Control 10
SAST (Static Application Security Testing)CodeQL weekly + PRGitHub Code ScanningISO 27001 A.8.28
Access ControlGitHub RBAC, branch protectionRepository settingsCIS Control 6
Audit LoggingGitHub audit logs, workflow logsLogs APIISO 27001 A.8.15
Data ClassificationAll content PUBLICCLASSIFICATION.mdISO 27001 A.5.10
Incident ResponseSECURITY.md proceduresQuarterly reviewsNIST CSF RS.RP

Security Testing:

  • SAST: CodeQL (weekly + PR) - JavaScript/TypeScript, HTML
  • Dependency Scanning: Dependabot (daily) + npm audit (pre-commit)
  • Manual Penetration Testing: Not required (static site, no user input)
  • Security Reviews: Quarterly architecture review

Accessibility Requirements (WCAG 2.1 AA)

CriterionRequirementImplementationTesting
PerceivableText alternatives, adaptable content, distinguishableSemantic HTML5, alt text, contrast ratiosPlaywright axe tests
OperableKeyboard accessible, enough time, navigable, input modalitiesFocus management, skip links, ARIA labelsManual keyboard testing
UnderstandableReadable, predictable, input assistancelang attributes, consistent navigation, form labelsLighthouse accessibility
RobustCompatible with assistive technologiesValid HTML5, ARIA rolesHTML validator

Accessibility Targets:

  • WCAG 2.1 AA Compliance: 100% (mandatory)
  • Lighthouse Accessibility Score: >95% (target 100%)
  • Keyboard Navigation: All interactive elements accessible
  • Screen Reader Support: JAWS, NVDA, VoiceOver tested quarterly

Accessibility Testing:

  • Automated: Playwright with axe-core (every PR)
  • Manual: Quarterly screen reader testing, keyboard navigation
  • Tools: Lighthouse (manual runs), axe DevTools, HTML validator

Maintainability Requirements

MetricTargetCurrentTool
Code Coverage>80% lines82%Vitest
Branch Coverage>80% branches83%Vitest
Cognitive Complexity<15 per function<10 averageESLint sonarjs cognitive-complexity rule
Code Duplication<3%<2%Manual review
Documentation Coverage100% public APIs95%JSDoc, manual review
Build Time<5 minutes (tests only)3-4 minutesGitHub Actions

Maintainability Practices:

  • Code Review: All PRs require approval
  • Documentation: Architecture, security, process docs maintained
  • Testing: Unit (Vitest 4.1.5), Integration (incl. MCP contract tests), E2E (Playwright 1.59.1 + axe-core 4.11.3)
  • Linting: ESLint 10.3.0 with eslint-plugin-sonarjs@4.0.3, eslint-plugin-security@4.0.0, eslint-plugin-jsdoc@62.9.0; Prettier 3.8.3 formatting
  • Dependencies: Minimal (1 required production, 1 optional, ~40 dev), weekly Dependabot updates

๐ŸŽฏ Design Principles

1. Security by Design

  • Minimal Attack Surface: Static architecture eliminates server-side vulnerabilities
  • No Runtime Execution: Pure HTML/CSS with no backend processing
  • Content Security Policy: Strict CSP headers prevent XSS
  • HTTPS Only: All content delivered over HTTPS

2. Separation of Concerns

  • Generation: News generation scripts (TypeScript โ†’ Node.js)
  • Presentation: Static HTML/CSS
  • Data Access: MCP Client abstraction
  • Infrastructure: GitHub-managed CI/CD and hosting

3. Multi-Language First

  • 14 Languages Supported: Full multi-language coverage including RTL support
  • Language-Specific Indexes: Separate navigation for each language
  • SEO Per Language: Individual sitemaps and metadata

4. Maintainability

  • Minimal Dependencies: One production dependency (european-parliament-mcp-server for build-time data access), only dev dependencies otherwise
  • Standard Technologies: HTML5, CSS3, TypeScript (compiled to ES2025 JavaScript)
  • Comprehensive Testing: Unit, integration, and E2E tests
  • Documentation: Architecture, security, and process docs

5. Scalability

  • Static Content: Infinite scalability via CDN
  • No Database: No scaling bottlenecks
  • Cacheable: All content highly cacheable
  • GitHub Infrastructure: Leverages GitHub's global infrastructure

๐Ÿ“ˆ System Qualities

Performance

  • Cold Start: N/A (static site, no cold starts)
  • Page Load: < 1s (static HTML, CDN cached)
  • Build Time: ~5-10 minutes (generation for all languages)
  • Deployment Time: ~1-2 minutes (S3 sync + CloudFront invalidation)

Availability

  • Target: 99.9% (AWS CloudFront/S3 SLA)
  • Redundancy: CloudFront with multiple edge locations globally
  • Failover: Automatic via AWS infrastructure
  • Monitoring: AWS Health Dashboard, GitHub Status page

Security

  • Attack Surface: Minimal (static files only)
  • Vulnerability Scanning: Daily (Dependabot + npm audit)
  • SAST: Weekly (CodeQL)
  • Compliance: ISO 27001, GDPR, NIS2, EU CRA aligned

Maintainability

  • Code Complexity: Moderate (5-stage artifact pipeline + deterministic aggregator modules; no SPA framework)
  • Test Coverage: 82%+ lines, 83%+ branches across 76 test files; 3026+ passing tests (unit, integration incl. EP/IMF/WB MCP contract tests, E2E Playwright)
  • Documentation: Comprehensive (25+ architecture & ISMS docs โ€” see Architecture Documentation Map)
  • Dependencies: 1 pinned production (european-parliament-mcp-server@1.3.20), 1 optional (worldbank-mcp@1.0.1), ~40 dev dependencies