ARCHITECTURE.md
June 13, 2026 ยท View on GitHub
๐๏ธ EU Parliament Monitor โ Architecture
C4 Model Architecture for European Parliament Intelligence Platform
๐ System Context โข ๐ฆ Container View โข ๐ง Component Design
๐ 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 nosrc/utils/content-validator.ts/validate-articles.tsruntime validators. The repository still keepsscripts/validate-analysis-completeness.jsas 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>.mdfiles (Stages A โ B โ C โ D โ E in one session, covering 14 article types โ including the long-horizonquarter-ahead/year-aheadand electoralterm-outlook/election-cycleset added in 2026-Q2) +news-translate.md; the split-family workflows (news-<type>-analysis.md+news-<type>-article.md) and the manualnews-article-generator.mdhelper 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 insrc/utils/content-validator.tswere purged with the rest of the validator layer; enforcement moved to the Stage-C completeness review protocol in.github/prompts/03-analysis-completeness-gate.mdand the depth floors inanalysis/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
| Document | Focus | Description | Documentation Link |
|---|---|---|---|
| Architecture | ๐๏ธ Architecture | C4 model showing current system structure | View Source |
| Future Architecture | ๐๏ธ Architecture | C4 model showing future system structure | View Source |
| Mindmaps | ๐ง Concept | Current system component relationships | View Source |
| Future Mindmaps | ๐ง Concept | Future capability evolution | View Source |
| SWOT Analysis | ๐ผ Business | Current strategic assessment | View Source |
| Future SWOT Analysis | ๐ผ Business | Future strategic opportunities | View Source |
| Data Model | ๐ Data | Current data structures and relationships | View Source |
| Future Data Model | ๐ Data | Enhanced European Parliament data architecture | View Source |
| Flowcharts | ๐ Process | Current data processing workflows | View Source |
| Future Flowcharts | ๐ Process | Enhanced AI-driven workflows | View Source |
| State Diagrams | ๐ Behavior | Current system state transitions | View Source |
| Future State Diagrams | ๐ Behavior | Enhanced adaptive state transitions | View Source |
| Security Architecture | ๐ก๏ธ Security | Current security implementation | View Source |
| Future Security Architecture | ๐ก๏ธ Security | Security enhancement roadmap | View Source |
| Threat Model | ๐ฏ Security | STRIDE threat analysis | View Source |
| Classification | ๐ท๏ธ Governance | CIA classification & BCP | View Source |
| CRA Assessment | ๐ก๏ธ Compliance | Cyber Resilience Act | View Source |
| Workflows | โ๏ธ DevOps | CI/CD documentation | View Source |
| Future Workflows | ๐ DevOps | Planned CI/CD enhancements | View Source |
| Business Continuity Plan | ๐ Resilience | Recovery planning | View Source |
| Financial Security Plan | ๐ฐ Financial | Cost & security analysis | View Source |
| End-of-Life Strategy | ๐ฆ Lifecycle | Technology EOL planning | View Source |
| Unit Test Plan | ๐งช Testing | Unit testing strategy | View Source |
| E2E Test Plan | ๐ Testing | End-to-end testing | View Source |
| Performance Testing | โก Performance | Performance benchmarks | View Source |
| Security Policy | ๐ Security | Vulnerability reporting & security policy | View 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
| Policy | Description | Relevance to EU Parliament Monitor |
|---|---|---|
| Information Security Policy | Establishes organization-wide security governance and risk management framework | Defines overall security posture, risk assessment methodology, and management responsibilities for the project |
| Secure Development Policy | Defines secure coding standards, code review requirements, and SDLC security gates | Mandates security-first development practices: input validation, dependency scanning, SAST/DAST integration, secure CI/CD pipelines |
| Open Source Policy | Governs use, contribution, and licensing of open source software | Ensures compliance with Apache-2.0 License, dependency license compatibility, and transparent open source contribution practices |
| Classification Policy | Defines data classification scheme (Public, Internal, Confidential, Restricted) and handling requirements | All project content classified as PUBLIC; establishes data handling controls for any future sensitive data integration |
| AI Policy | Governs responsible AI usage, transparency, and human oversight requirements | Governs LLM usage for content generation: transparency requirements, human review workflows, bias mitigation, prompt injection protection |
| Access Control Policy | Defines authentication, authorization, least privilege, and privileged access management | Controls GitHub repository access, branch protection rules, secret management, and deployment permissions |
| Cryptography Policy | Establishes 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 viatscโ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 insrc/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_dispatchonly) โ 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>.mdarticle types (Stages A โ B โ C โ D โ E in one session, active-work budget 22โ28 min before the single safe-outputscreate_pull_requestcall, 60-mintimeout-minutescap;engine.mcp.session-timeoutis 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.ymlviagh aw compile --validate(pinnedGH_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 vianpm run generate-article -- --run <analysis-run-dir>for a single run ornpm run generate-article:allfor batch regen) walksmanifest.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) insrc/utils/content-validator.tswere 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 againstreference-quality-thresholds.json) - MCP Integration: Spawned as local child processes via stdio JSON-RPC at build time; inside agentic workflows via the
awmggateway athttp://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.ymlwith OIDC auth); GitHub Pages retained as documented fallback; npm package published toregistry.npmjs.org/euparliamentmonitorwith 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
| Element | Type | Description | Technology |
|---|---|---|---|
| European Citizen | User | Primary audience seeking EP news in native language | Web Browser |
| Journalist | User | Professional using site for research and story development | Web Browser |
| Political Researcher | User | Academic or analyst studying EP activities | Web Browser |
| Developer/Contributor | User | Maintainer improving system | Git, Node.js, VS Code |
| EU Parliament Monitor | System | Core static site generator | Node.js, TypeScript |
| GitHub | External System | Source control, CI/CD | GitHub Actions |
| EP MCP Server | External System | Structured EP data access | MCP Protocol, TypeScript |
| EP APIs | External System | Official data sources | REST APIs, JSON |
| LLM Service | External System | Content generation | API (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:
| Zone | Trust Level | Security Controls | Threat Model |
|---|---|---|---|
| Public Internet | Untrusted | HTTPS-only, planned CSP headers, static content only | DDoS, XSS attempts (mitigated by static architecture) |
| GitHub Infrastructure | Trusted | GitHub authentication, branch protection, optional signed commits, secret scanning | Supply chain attacks (mitigated by Dependabot, CodeQL) |
| AWS Hosting | Trusted | ACM certificate, HTTPS redirect, DDoS protection via CloudFront | Hosting infrastructure compromise (mitigated by AWS security controls, OIDC deploy auth) |
| External Services | Partially Trusted | API authentication, basic input parsing/shape validation; planned systematic sanitization/escaping and rate limiting | Data poisoning, API compromise (mitigated by validation, monitoring, planned hardening) |
Key Security Boundaries:
- User โ CloudFront: Read-only HTTPS access, no authentication required (public content)
- GitHub Actions โ External APIs: Authenticated API calls, input validation, error handling
- GitHub Actions โ AWS S3: Authenticated S3 sync + CloudFront invalidation, only static files deployed
- 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 Orchestrator | Claude Opus 4.8 (articles) / Claude Sonnet 4.6 (translate) + gh-aw v0.77.0 | Runs 15 agentic workflows (14 article + 1 translate); produces analysis artifacts | Triggers via cron / manual; commits one PR per article run |
| ๐ Prompt / Methodology / Template libraries | Markdown + JSON | Bounded-context prompts (10), methodologies (19), templates (60 top-level + 6 _partials/) | Read by every agentic workflow at start-of-session |
| ๐ง Analysis Runs | Markdown + JSON | Per-run intelligence tree under analysis/daily/<date>/<type>/ | Written by Stage B agents; consumed by Stage C and aggregator |
| ๐ข Aggregator (5 modules) | TypeScript | Reads manifest.json and Markdown artifacts; renders 14-language HTML deterministically | npm run generate-article -- --run <dir> |
| ๐ EP MCP Client | TypeScript | 60+ EP tools + voting fallback to EP Open Data Portal /api/v2/decision | Stage A data collection |
| ๐ฐ IMF / ๐ฑ World Bank Clients | TypeScript | Economic context (IMF) + non-economic indicators (WB) | Stage A wave-2 context |
| โ๏ธ Stage-C Review | Editorial agent + JSON thresholds | Per-artifact line floors + tradecraft signals (Admiralty / WEP / ICD-203) | Replaces the purged content-validator.ts runtime gate |
| ๐ News Indexes & Sitemap | TypeScript | 14-language index pages, sitemap.xml, hreflang alternates | npm run prebuild |
| ๐ฆ Static Site Output | HTML / CSS / JS / JSON / Markdown | Public deliverable: news pages + article.md source per run | Committed to main, deployed to S3 |
| ๐ GitHub Actions | CI/CD + gh-aw | 15 news + ~15 standard workflows | Daily news + on-PR validation |
| โ๏ธ CloudFront + S3 | CDN / object storage | Primary hosting via OIDC GithubWorkFlowRole | HTTPS + immutable asset cache |
Security Responsibilities per Container
| Container | Security responsibility | Implementation | Controls |
|---|---|---|---|
| ๐ค gh-aw Orchestrator | Sandboxed AWF runtime, Squid egress allowlist, capability-bounded safe outputs | Runs in GitHub-hosted ephemeral VMs; safe-outputs.create-pull-request.max: 1; step-security/harden-runner egress block | A.5.10, A.8.28 (ISO 27001), CIS 16 |
| ๐ข Aggregator | Deterministic MarkdownโHTML; explicit markdown-it plugin allowlist; clean-artifact.ts strips SPDX/banners; script-src 'self' CSP | No AI-authored HTML; vendored Mermaid/Chart.js/D3 under js/vendor/ | A.8.23, A.8.28 (ISO 27001), OWASP A03 |
| ๐ EP MCP Client | Local stdio JSON-RPC; per-request timeout + retry backoff; envelope validation; voting-records three-state fallback | safeCallTool() + callToolWithRetry() wrappers in ep-mcp-client.ts | A.8.24 (ISO 27001), CIS 16 |
| ๐ง Analysis Artifacts | Tradecraft grading (Admiralty A1โF6 + WEP) + provenance manifest | manifest.json cross-reference map; methodology-reflection.md audit | A.5.12 (ISO 27001) |
| ๐ฆ Static Files | Public-data only; integrity via Git + SLSA L3 attestation | All EP/IMF/WB content is public; SBOM, REUSE licence headers | A.5.10 (ISO 27001) |
| ๐ GitHub Actions | OIDC for AWS deploy; Secrets at job-scope; SHA-pinned third-party actions | GithubWorkFlowRole IAM with least privilege; harden-runner egress allowlist | A.8.3, CIS 6 |
| โ๏ธ CloudFront + S3 | HTTPS-only via ACM; bucket policy denies public ACLs; CloudFront cache-control by file class | Long-cache immutable assets, short-cache HTML | A.13.1, A.5.23 (ISO 27001) |
| Amazon CloudFront + S3 | HTTPS-only, CDN security, DDoS protection | Forces HTTPS redirect via ACM certificate, CloudFront with DDoS mitigation, HSTS headers (configured externally in CloudFront distribution) | A.8.24 (ISO 27001) |
| Git Repository | Access control, branch protection, signed commits | RBAC with least privilege, protected main branch, optional signed commits | CIS 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 pipeline | Discover 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>.html | markdown-it + markdown-it-anchor/-footnote/-attrs/-deflist | src/aggregator/{article-generator,analysis-aggregator,markdown-renderer,article-html,artifact-order,clean-artifact,article-metadata,progressive-disclosure}.ts |
| ๐ง Analysis artifacts | 60 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 Client | 60+ EP tools via stdio JSON-RPC; safeCallTool() + callToolWithRetry() wrappers; recess-mode detection ([1952,2100] year window); slow-feed warning downgrade for get_events_feed | european-parliament-mcp-server@1.3.20+ (PR #405 normalises political-group codes) | src/mcp/ep-mcp-client.ts |
| ๐ณ๏ธ EP Open Data fallback | Three-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-records | EP Open Data Portal | src/mcp/ep-open-data-client.ts (see getVotingRecordsWithFallback()) |
| ๐ฐ IMF Client | class 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_MS | None (REST) | src/mcp/imf-mcp-client.ts |
| ๐ฑ World Bank Client | WORLD_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 gate | Editorial 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.ts | Methodology library + per-artifact thresholds | .github/prompts/03-โฆ, analysis/methodologies/reference-quality-thresholds.json |
| ๐ Prior-Run Diff | Re-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 registry | Canonical 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 exist | JSONL registry | scripts/aggregator/forward-statements-registry.js, analysis/forward-statements/ |
| ๐ก๏ธ Shell-safety helper | Pre-audited bash helper for checkpoint-to-memory; replaces expansion-heavy inline workflow bash that the sandbox shell-safety filter would block | Bash | scripts/checkpoint-analysis-to-memory.sh |
| ๐ง Intelligence utilities | political-classification (7D), political-threat-assessment (5-framework), political-risk-assessment$ (5 \times 5 \text{L} \times \text{I}), $significance-scoring, article-quality-scorer | Types | src/utils/*.ts |
| ๐ News Indexes & Sitemap | Per-language news index pages, sitemap.xml, per-language sitemap_<lang>.html, hreflang alternates | Metadata, file-utils | src/generators/news-indexes.ts, src/generators/sitemap.ts |
| ๐ข Constants | ALL_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
| Pattern | Components Involved | Purpose | Implementation |
|---|---|---|---|
| Manifest-Driven Aggregation | article-generator.ts โ manifest/reader.ts โ analysis-aggregator.ts | Discover and aggregate analysis artifacts in canonical order | Reads manifest.json from run dir; resolves articleType (3-variant schema); filters to .md artifacts; emits Provenance block |
| 19-Section Canonical Order | artifact-order.ts โ analysis-aggregator.ts | Deterministic article structure matching intelligence-product layout | ARTIFACT_SECTIONS defines section IDs, titles, and artifact paths; unmatched artifacts go to "Supplementary Intelligence" bucket |
| 5-Tier Metadata Resolution | article-metadata.ts | Resolve <title> and <meta description> from multiple sources | manifest 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 Extraction | key-takeaways.ts โ analysis-aggregator.ts | Auto-extract 3โ7 bullet key takeaways | Harvests from intelligence/synthesis-summary.md + intelligence/intelligence-assessment.md; inserted after Executive Brief |
| MCP Connection Retry with Backoff | mcp-retry.ts โ mcp-connection.ts โ ep-mcp-client.ts | Handle transient MCP connection failures | callToolWithRetry() with exponential backoff; safeCallTool() catches errors and returns typed fallback payloads |
| Three-State Voting Fallback | ep-mcp-client.ts โ ep-open-data-client.ts | Ensure 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 Guide | reader-intelligence-guide.ts โ article-generator.ts | Prepend methodology transparency panel | Collapsible HTML panel prepended to article body; provides tradecraft context to readers |
| Run Discovery & Collision Grouping | runs/discover.ts โ runs/grouping.ts โ article-generator.ts | Batch mode (--all) discovers all valid runs | Walks 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 Component | Technology | Purpose | Configuration |
|---|---|---|---|
| GitHub Actions Runner | ubuntu-latest, Node.js 26 | Execute generation workflow | .github/workflows/news-*.lock.yml |
| Amazon CloudFront | AWS CDN | Serve static content globally | CloudFront distribution (deploy-s3.yml) |
| Amazon S3 | AWS Object Storage | Host static site files | S3 bucket (deploy-s3.yml) |
| Git Repository | GitHub Storage | Version control + content storage | public repository |
| Web Browser | Modern browsers | Render news articles | HTML5, CSS3, ES6+ |
| EP MCP Server | Local Node process | EP data access | Spawned locally via stdio JSON-RPC |
| LLM Service | External API | Content generation | API 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 |
|---|---|---|
๐จ breaking | news-breaking.md | Every 6 hours |
๐ฎ week-ahead | news-week-ahead.md | Fri 07:00 UTC |
๐ month-ahead | news-month-ahead.md | 1st of month 08:00 UTC |
๐ quarter-ahead | news-quarter-ahead.md | 1st of month 08:00 UTC |
๐ฐ๏ธ year-ahead | news-year-ahead.md | Quarterly (2 Jan/Apr/Jul/Oct) |
๐๏ธ term-outlook | news-term-outlook.md | Semi-annual (1 Jan & 1 Jul) |
๐ณ๏ธ election-cycle | news-election-cycle.md | Annual (1 Dec) + T-180/T-90/T-30 imminent triggers |
๐ week-in-review | news-week-in-review.md | Sat 09:00 UTC |
๐ month-in-review | news-month-in-review.md | 28th of month 10:00 UTC |
๐ quarter-in-review | news-quarter-in-review.md | 5th of month 08:00 UTC |
๐ year-in-review | news-year-in-review.md | Annual (15 Jan) |
๐๏ธ committee-reports | news-committee-reports.md | MonโFri 04:00 UTC |
๐ณ๏ธ motions | news-motions.md | MonโFri 06:00 UTC |
โ๏ธ propositions | news-propositions.md | Monโ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:
- AWF Squid firewall allowlist โ egress HTTP allowlist per workflow
- Sandboxed Docker with restricted shell โ
bash tool-call contract(every call requirescommand+description); shell expansion restrictions - Safe-output constraints โ
create-pull-requestwithmax-patch-size(default 1024 KB;news-translate.mdsets 10240 KB at top level for 14-language fan-out) - JSONL audit trail โ per-step structured logs
- Lock file compilation โ
.lock.ymlis the immutable executed artifact;.mdis 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):
.github/prompts/03-analysis-completeness-gate.mdโ protocol that the editorial agent runsanalysis/methodologies/reference-quality-thresholds.jsonโ per-artifact line floors- Dynamic file resolution pattern (must not hallucinate file names):
ls -t "news/${TODAY}-${TYPE}"*"-en.html" | head -1
๐ Technology Stack
Core Technologies
| Layer | Technology | Version | Purpose | Rationale |
|---|---|---|---|---|
| Runtime | Node.js | 26.x (engines: >=26); Node.js 26 nightly tested in CI | JavaScript execution environment | LTS release for stability and long-term support; ESM-native ("type": "module") |
| Language | TypeScript | 6.0.3 | Primary development language | Strict type safety; compiles from src/ โ scripts/ targeting ES2025, module: NodeNext |
| Package Manager | npm | 10.x | Dependency management | Native Node.js package manager, security audit integration |
| Testing | Vitest | 4.1.4 | Unit and integration testing | Fast, ESM-native; happy-dom env; happy-dom@20.9.0 |
| E2E Testing | Playwright | 1.59.1 | End-to-end browser testing | @axe-core/playwright@4.11.3 for WCAG 2.1 AA |
| Linting | ESLint | 10.2.1 | Code quality and security | Flat config; plugins: eslint-plugin-sonarjs@4.0.3, eslint-plugin-security@4.0.0, eslint-plugin-jsdoc@62.9.0 |
| Formatting | Prettier | 3.8.3 | Code formatting | Opinionated formatter, consistent code style |
| Visualization | Chart.js | 4.5.1 | Dashboard charts in articles | Vendored into js/vendor/ via npm run copy-vendor |
| Visualization | D3 | 7.9.0 | Advanced visualizations | Used for specific intelligence views |
| Documentation | TypeDoc | 0.28.19 | API documentation generation | Generates docs/ pages from TypeScript sources |
| HTML Validation | HTMLHint | 1.9.2 | HTML5 validation | Pre-commit + CI |
| Duplicate Check | jscpd | 4.0.9 | Copy-paste detection | Scheduled quality audits |
Technology Version Matrix
| Technology | Current Version | Minimum Version | End-of-Life | Update Policy |
|---|---|---|---|---|
| Node.js | 26.x (LTS) | 26.0.0 (engines: >=26) | ~Apr 2029 | Running on Node.js 26 LTS; Node.js 26 nightly tested in CI (test-and-report.yml) |
| npm | 10.x (latest) | 10.0.0 | Follows Node.js lifecycle | Auto-updated with Node.js |
| TypeScript | 6.0.3 | 6.0.0 | N/A | Update to latest minor within 14 days, major within 90 days |
| Vitest | 4.1.4 | 4.0.0 | N/A | Update to latest minor within 14 days, major within 60 days |
| Playwright | 1.59.1 | 1.55.0 | N/A | Update to latest minor within 14 days, major within 60 days |
| ESLint | 10.2.1 | 10.0.0 | N/A | Update to latest minor within 14 days, major within 90 days |
| Prettier | 3.8.3 | 3.0.0 | N/A | Update to latest minor within 14 days, major within 90 days |
| Chart.js | 4.5.1 | 4.0.0 | N/A | Vendored; update with copy-vendor script |
| D3 | 7.9.0 | 7.0.0 | N/A | Vendored; update with copy-vendor script |
| TypeDoc | 0.28.19 | 0.28.0 | N/A | Major within 60 days |
| european-parliament-mcp-server | 1.3.20 (pinned) | 1.3.20 | Per upstream | Track releases; repository workflows and package dependency pin european-parliament-mcp-server@1.3.20 for EP MCP data access |
| worldbank-mcp | 1.0.1 (optional) | 1.0.0 | Per upstream | Biannual WDI refresh cadence |
| gh-aw CLI | v0.77.0 (pinned GH_AW_VERSION) | v0.77.0 | Per upstream | Workflow-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+startDatewhencustom) and 7 fixed-window feed tools (limit/offsetonly โ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
| Tool | Purpose | Integration | Configuration |
|---|---|---|---|
| CodeQL | SAST scanning | GitHub Actions (weekly + PR) | .github/workflows/codeql.yml |
| Dependabot | Dependency vulnerability scanning | GitHub native (daily) | .github/dependabot.yml |
| npm audit | Dependency security check | Pre-commit + CI | package.json scripts |
| ESLint Security | Security-focused linting | Pre-commit + CI | eslint.config.js (security plugin) |
| HTMLHint | HTML validation | CI pipeline | .htmlhintrc |
| Husky | Git hooks | Pre-commit, pre-push | .husky/ directory |
| Playwright | Accessibility testing | E2E test suite | playwright.config.js (axe integration) |
Infrastructure
| Service | Purpose | Configuration | Cost |
|---|---|---|---|
| GitHub Actions | CI/CD automation | .github/workflows/ | Free (public repo) |
| AWS S3 | Static site hosting | S3 bucket + static website | Pay-per-use (storage, requests) |
| Amazon CloudFront | Content delivery | CloudFront distribution (S3) | Pay-per-use (data transfer, requests) |
| Git | Version control | Repository | Free (public repo) |
External Services
| Service | Purpose | Protocol | Authentication | Rate Limits | Cost Model |
|---|---|---|---|---|---|
| European Parliament MCP Server | EP data access | Local 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 generation | HTTPS/JSON | API key (required) | Varies by provider | Pay-per-token |
| GitHub API | Repository operations | REST/GraphQL | GitHub token | 5000 req/hr | Free (authenticated) |
Browser Support
| Browser | Minimum Version | Features Required | Testing Coverage |
|---|---|---|---|
| Chrome/Edge | 90+ | ES2020, CSS Grid, Flexbox | โ Playwright E2E (Chromium in CI) |
| Firefox | 88+ | ES2020, CSS Grid, Flexbox | ๐งช Manual regression (no Playwright CI) |
| Safari | 14+ | ES2020, CSS Grid, Flexbox | ๐งช Manual regression (no Playwright CI) |
| Mobile Chrome | 90+ | ES2020, Responsive Design | ๐งช Manual responsive testing |
| Mobile Safari | 14+ | 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โ Runstsc(TypeScript compilationsrc/โscripts/)npm run lintโ ESLint onsrc/npm run generate-article -- --run <dir>โ Renders a single analysis run to 14-language HTMLnpm run generate-article:allโ Batch-renders every discoverable analysis runnpm run generate-news-indexesโ Executesscripts/generators/news-indexes.js(prebuild hook)npm run generate-sitemapโ Executesscripts/generators/sitemap.js(prebuild hook โ also generates political-intelligence pages)npm run copy-vendorโ Vendorschart.js,d3, andmermaidESM bundles intojs/vendor/npm run validate-analysisโ Stage-C completeness validation against reference-quality-thresholds.jsonnpm run sync:templatesโ Syncs ANALYSIS-TEMPLATE-FRONTMATTER:v1 blocks into analysis templatesnpm run prior-run-diffโ Generates carry-forward plan for re-run improvementnpm run lint:promptsโ Validates gh-aw workflow prompt imports and banned patternsnpm 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 togglejs/article-runtime.jsโ Reading progress + theme togglejs/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
| Pattern | Max code lines | Enforcement |
|---|---|---|
src/**/*.ts (global) | 600 | ESLint max-lines + test/unit/source-file-size.test.js (raw lines) |
src/types/** | 400 | ESLint max-lines per-area override |
src/config/** | 400 | ESLint max-lines per-area override |
src/aggregator/clean/** | 400 | ESLint max-lines per-area override |
src/aggregator/run/** | 400 | ESLint max-lines per-area override |
src/aggregator/html/** | 400 | ESLint max-lines per-area override |
src/aggregator/generator/** | 400 | ESLint max-lines per-area override |
src/templates/sections/** | 400 | ESLint max-lines per-area override |
src/mcp/transport/** | 400 | ESLint 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:
| Level | Usage | Output | Retention |
|---|---|---|---|
| ERROR | Unrecoverable errors (API failures, file write errors) | console.error(), GitHub Actions logs | 90 days (GitHub) |
| WARN | Recoverable issues (MCP connection retry/backoff, MCP tool fallback, JSON.parse recovery) | console.warn(), GitHub Actions logs | 90 days (GitHub) |
| INFO | Normal operations (generation start/complete, article count) | console.log(), GitHub Actions logs | 90 days (GitHub) |
| DEBUG | Detailed diagnostics (API responses, intermediate data) | Disabled in production | Dev 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:
| Metric | Tool | Threshold | Alert |
|---|---|---|---|
| Build Success Rate | GitHub Actions | <95% over 7 days | Email to maintainers |
| Generation Duration | Workflow logs | >15 minutes | Warning annotation |
| Test Pass Rate | Vitest + Playwright | <100% | Block merge |
| Security Vulnerabilities | Dependabot + CodeQL | Any high/critical | Email + PR |
| Site Availability | AWS Health Dashboard | <99.9% | AWS Health event notification |
| Page Load Time | Lighthouse (manual runs) | >3 seconds | Warning 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 Category | Examples | Retry Strategy | Fallback | User Impact |
|---|---|---|---|---|
| Transient Network Errors | MCP connection failure during startup, LLM API rate limit | Exponential backoff (1s, 2s, 4s), max 3 retries for MCP connection establishment and LLM calls; individual MCP requests use a single fixed timeout with no retry | Use placeholder events or skip affected items (no cache) | Missing or placeholder content for affected items |
| Permanent API Errors | Invalid API key, malformed request | No retry | Skip article generation for affected language | Missing article for specific language |
| Data Validation Errors | Invalid EP data structure, missing required fields | No automatic regeneration loop | Skip invalid items (no cached-data fallback) | Missing content for invalid items |
| File System Errors | Disk full, permission denied | No retry | Fail workflow | Build failure (no deployment) |
| Content Generation Errors | LLM refusal, prompt injection detected | Single generation attempt (no automatic regeneration loop) | Insert placeholder events when content generation fails | Reduced content quality or placeholder content |
Error Propagation:
- Component Level: Catch and log errors, attempt recovery
- Service Level: Propagate if unrecoverable, aggregate errors for reporting
- 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:
| Aspect | Implementation | Example |
|---|---|---|
| Content Generation | Placeholder 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 Naming | Language suffix in filename | week-ahead-2026-02-17-en.html, week-ahead-2026-02-17-de.html |
| HTML lang Attribute | Set per page | <html lang="en">, <html lang="de"> |
| Navigation | Language-specific index pages | index.html, index-de.html |
| SEO | hreflang tags for alternate languages | <link rel="alternate" hreflang="de" href="..."> |
| Date Formatting | Locale-specific date formats | EN: "February 17, 2026", DE: "17. Februar 2026" |
| Character Encoding | UTF-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:
- Security: Static sites eliminate entire classes of vulnerabilities (SQL injection, XSS via server-side rendering, authentication bypass)
- Scalability: Static content scales infinitely via CDN with no server infrastructure
- Cost: Static hosting on AWS S3 + CloudFront is low-cost, no server infrastructure
- Maintainability: Simpler architecture with fewer moving parts
- 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:
- Cost: Low-cost static hosting within current traffic and budget constraints
- Integration: GitHub Actions CI/CD deploys to S3 and invalidates the CloudFront distribution
- Security: HTTPS via AWS Certificate Manager, TLS termination at CloudFront edge
- Reliability: AWS S3 and CloudFront SLAs provide high availability and durability
- 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:
- Abstraction: MCP Server provides unified interface to fragmented EP APIs
- Data Normalization: Consistent data structures across EP data sources
- Error Handling: Connection retry logic and graceful degradation
- Maintainability: API changes isolated to MCP Server, not news generator
- 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:
- Quality: Native generation produces natural, idiomatic language
- Cultural Adaptation: LLM can adapt content for cultural context per language
- Terminology: LLM trained on EP documents uses correct terminology
- Flexibility: Different article structures possible per language/culture
- 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:
- Type Safety: Strict mode catches errors at compile time, especially important for complex EP data structures and MCP client interfaces
- IDE Support: Full IntelliSense, refactoring, and navigation in VS Code
- Self-Documenting: TypeScript interfaces serve as living documentation for data models (ArticleCategory, LanguageCode, MCPToolResult, etc.)
- Build Pipeline:
tsccompilessrc/*.tsโscripts/*.js;rootDir: ./src,outDir: ./scripts,target: ES2025,module: NodeNext - 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-reviewdata 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:
- 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.
- Systematic: The window is deterministic and reproducible โ it always yields voting data regardless of EP publication lag variance (2โ6 weeks).
- 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.
- Article framing updated: The
WEEKLY_REVIEW_TITLESsubtitles (all 14 languages) now read "last full reporting week" instead of "past week" to accurately describe the shifted window to readers. - SEO metadata: The title date range already shows the exact
dateFromโdateTowindow, 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-reviewrun 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_TOvariables replaceLAST_WEEKinweek-in-reviewStage A bash blocks; other workflows still usingLAST_WEEKrequire separate migration if their reporting windows are changed.
Implementation:
src/aggregator/article-metadata.ts: NewderiveReportingWindowForWeekInReview()export computes D-36/D-8 from the article date;buildTemplateFallbackuses it forweek-in-review..github/workflows/news-week-in-review.md: Stage A setsDATE_FROM(D-36) andDATE_TO(D-8); all MCP tool calls use these variables;LAST_WEEKremoved.src/constants/language-articles.ts:WEEKLY_REVIEW_TITLESsubtitles 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
.mdfiles, 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), andanalysis/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 }whereanchoristoday/next-election/commission-wp/term-end.cadenceโ{ cron, description, triggerEvents? }โ supports auxiliary triggers likeelection-imminent-t180.primaryFeedsโ EP MCP tools that must be probed in Stage A.mandatoryArtifacts/optionalArtifactsโ relative paths underanalysis/daily/<date>/<slug>/.stageBudgetsโ{ A, B, C, D, E }minute ceilings (sum โค 50 โ drift-guardtest/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โ whentrue, 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:
- One change, one place. Adding a new horizon is now a four-step edit: new
ArticleCategoryenum value โ newARTICLE_HORIZONSentry โ per-language title generator insrc/constants/language-articles.tsโ newnews-<slug>.mdworkflow. Everything else (Stage-C gate, aggregator, scenario depth, forward-statements decay) follows automatically. - Compile-time safety. TypeScript's exhaustiveness checks force every consumer to handle every
ArticleCategoryenum value; missing a case in (e.g.) the article metadata fallback now fails the build. - Drift-guard.
test/unit/horizon-registry.test.jsasserts 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 theelectoralOverlayflag matches the artifact-list family.test/unit/horizon-snapshot.test.jssnapshots every horizon's resolved configuration so unintentional changes are flagged at PR time. - 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:
src/config/article-horizons.tsโ thin re-export barrel (split in Refactor 2/8, issue #2030; original 603-LOC module decomposed into the focused sub-modules below).src/config/horizons/types.tsโArticleHorizonConfig,StageBudgetConfig,CadenceConfig,DataWindow*types.src/config/horizons/registry.tsโARTICLE_HORIZONSconstant (pure data, no helpers; โค350 LOC).src/config/horizons/artifact-paths.tsโ co-located artifact-path constants and shared mandatory-list groups (PROSPECTIVE_MANDATORY,RETROSPECTIVE_MANDATORY, etc.).src/config/horizons/stage-budgets.tsโ shared*_BUDGETSshapes andPR_CALL_DEADLINE_BY_SLUG(42 / 45 / 47-min targets, per.github/prompts/02-analysis-protocol.mdยง3).src/config/horizons/forward-projection.tsโgetForwardStatementsHorizonDays,getScenarioMaxHorizonMonths,getForwardStatementsHorizonMap,getForwardProjectionSlugs.src/config/horizons/lookup.tsโgetHorizonConfig,getProspectiveSlugs,getElectoralOverlaySlugs,getMandatoryArtifacts.test/unit/horizon-registry.test.jsโ drift-guard tests (stage-budget sum, electoral-overlay invariants, slug round-trip).test/unit/horizon-registry-split.test.jsโ barrel-parity drift guard + new stage-budget / forward-projection assertions added in Refactor 2/8.test/unit/horizon-snapshot.test.jsโ golden-snapshot test (usenpx vitest run -uto regenerate after intentional registry edits).src/aggregator/article-metadata.tsโ consumesgetHorizonConfig(slug)for window resolution.scripts/aggregator/forward-statements-registry.jsโ consumesforwardStatementsHorizonDaysfor carry-forward decay..github/prompts/10-horizon-stage-helpers.mdโ prompt-side reference to the registry families and stage-C tripwires.
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
| Requirement | Target | Measurement | Current 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 minutes | GitHub Actions logs | โ 8-12 minutes |
| Article Generation (Single) | <30 seconds | Script 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
| Dimension | Current Capacity | Target Capacity | Scaling Strategy |
|---|---|---|---|
| Concurrent Users | Unlimited (static content) | Unlimited | CDN auto-scales |
| Daily Visitors | 10,000+ | 100,000+ | CDN bandwidth increase |
| Articles per Day | 14 (one per language) | 140 (ten per language) | Parallel generation, workflow optimization |
| Supported Languages | 14 | 24+ (expanded markets) | Add language configs, LLM prompts |
| Repository Size | 150 MB | 800 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
| Requirement | Target | Measurement | Consequence of Failure |
|---|---|---|---|
| Site Availability | 99.9% (AWS CloudFront/S3 SLA) | GitHub Status + AWS Health Dashboard | Users cannot access news |
| Build Success Rate | >98% | GitHub Actions logs | No new content deployed |
| MCP API Availability | >99% (best effort) | Health checks | Fallback to placeholder events (no cached/previous data) |
| LLM API Availability | >99.5% (provider SLA) | API logs | Generation fails, retry logic |
| Recovery Time Objective (RTO) | <15 minutes | Manual testing | Time to restore service after outage |
| Recovery Point Objective (RPO) | <24 hours | Git history | Maximum 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
| Requirement | Implementation | Verification | Compliance |
|---|---|---|---|
| HTTPS-Only | CloudFront enforces HTTPS redirect via ACM certificate | Manual testing | ISO 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 Repository | GitHub Secrets for API keys | Git history scan | ISO 27001 A.8.3 |
| Dependency Vulnerability Scanning | Dependabot daily scans | GitHub Security tab | CIS Control 10 |
| SAST (Static Application Security Testing) | CodeQL weekly + PR | GitHub Code Scanning | ISO 27001 A.8.28 |
| Access Control | GitHub RBAC, branch protection | Repository settings | CIS Control 6 |
| Audit Logging | GitHub audit logs, workflow logs | Logs API | ISO 27001 A.8.15 |
| Data Classification | All content PUBLIC | CLASSIFICATION.md | ISO 27001 A.5.10 |
| Incident Response | SECURITY.md procedures | Quarterly reviews | NIST 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)
| Criterion | Requirement | Implementation | Testing |
|---|---|---|---|
| Perceivable | Text alternatives, adaptable content, distinguishable | Semantic HTML5, alt text, contrast ratios | Playwright axe tests |
| Operable | Keyboard accessible, enough time, navigable, input modalities | Focus management, skip links, ARIA labels | Manual keyboard testing |
| Understandable | Readable, predictable, input assistance | lang attributes, consistent navigation, form labels | Lighthouse accessibility |
| Robust | Compatible with assistive technologies | Valid HTML5, ARIA roles | HTML 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
| Metric | Target | Current | Tool |
|---|---|---|---|
| Code Coverage | >80% lines | 82% | Vitest |
| Branch Coverage | >80% branches | 83% | Vitest |
| Cognitive Complexity | <15 per function | <10 average | ESLint sonarjs cognitive-complexity rule |
| Code Duplication | <3% | <2% | Manual review |
| Documentation Coverage | 100% public APIs | 95% | JSDoc, manual review |
| Build Time | <5 minutes (tests only) | 3-4 minutes | GitHub 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-serverfor 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
๐ Related Documentation
- Security Architecture - Detailed security implementation and threat model
- Future Architecture - Architectural evolution roadmap
- Data Model - Data structures and EP/IMF/WB contracts
- Workflows - All 15 agentic + ~15 standard workflows, AI-First 2-pass enforcement
- End-of-Life Strategy - Technology lifecycle & EOL planning
- Flowcharts - Detailed process workflows
- State Diagrams - System state transitions
- Mindmaps - Conceptual system relationships
- SWOT Analysis - Strategic analysis and positioning
- README.md - Getting started guide and features overview
๐ Related ISMS-PUBLIC Policies
- Information Security Policy
- Secure Development Policy
- Network Security Policy
- Cryptography Policy
- CLASSIFICATION