🏛️ Comprehensive Political Data Architecture for Democratic Transparency
🗄️ 50+ Years Historical Data · 15 CIA Data Subsystems · 14-Language Support
📋 Document Owner: CEO | 📄 Version: 1.3 | 📅 Last Updated: 2026-05-06 (UTC)
🔄 Review Cycle: Annual | ⏰ Next Review: 2027-05-06
🏢 Owner: Hack23 AB (Org.nr 5595347807) | 🏷️ Classification: Public
🆕 What changed since last review (v1.2 → v1.3, 2026-05-06):
- 🧠 Added §11 Analysis Artifact Data Model — typed
ArtifactDefinition[]for 23 artifacts across Families A (9 core synthesis), B (2 structural metadata), C (5 strategic extensions), D (7 electoral/domain), E (per-document, variable). Required-Mermaid and required-Pass-2 booleans sourced fromscripts/agentic/artifact-inventory.ts. Folder convention:analysis/daily/$ARTICLE_DATE/$SUBFOLDER/. Methodology-reflection JSON contract (ICD 203 audit) added.- 📰 Added §12 News Corpus Data Model — YAML frontmatter schema (defaults:
language='en',layout='article'fromscripts/render-lib/aggregator/frontmatter.ts), 14-language file naming convention,news/directory structure. Current footprint: 3,953.htmlfiles undernews/(verified 2026-05-06; up from the 2,669 reported at v1.1).- 🗂️ Added §13 Political Intelligence Catalog Data Structure —
scripts/political-intelligence/catalog.tsoutputs andscripts/political-intelligence/daily-streams.tsper-stream/per-day data shape, plus i18n leaves (artifact, methodology, page, stream, template).- 🌍 Formalised IMF cache model under §4:
analysis/data/imf/{indicator}/{country}.jsonpayload +.meta.jsonsidecar (provenance fields: provider, dataflow, indicator, vintage, retrieved_at). Documented SCB and World Bank cache patterns analogously, plus Riksbank, Statskontoret, RiR caches underanalysis/data/{provider}/.- 🏷️ Added
economicProvenanceblock schema (provider ∈ {imf,scb,worldbank,riksbank,statskontoret,rir}, dataflow, indicator, vintage, retrieved_at) and the rule that any economic data point in article frontmatter or body MUST carry one.- 📐 Updated §5 Schemas & Validation with typed subpath exports (
./cia/*,./dashboards/*,./shared/*,./ui/*) generated byscripts/generate-types-from-cia-schemas.ts, and root-level schemas (schemas/article-types.schema.json,schemas/pir-status.schema.json,schemas/rir-followups-schema.json).- 📚 Cross-referenced
ARCHITECTURE.mdv2.5 §C4 Component view for the analysis-gate (checks 1–9b,PASS2_MTIME_THRESHOLD_MS = 180_000) and the render-lib aggregator/cleaning/seo sub-trees.🆕 What changed since last review (v1.1 → v1.2, 2026-04-20):
- 📈 IMF Open Data added as a primary external data source (§4.1.5 below) alongside SCB and World Bank, per ADR 0001 (accepted 2026-04-20). Economic Data Contract v2.0 (effective 2026-04-20; v1 grace → 2026-05-31) extends the economic data-point schema with additive fields:
source.imf[],dataPoints[].provider,dataPoints[].projection(boolean),dataPoints[].projectionVintage(e.g.,"WEO-2026-04"). Pure-TypeScript clientscripts/imf-client.ts(no MCP — SBOM-covered via npm). Cache:analysis/data/imf/{indicator}/{country}.json+.meta.json.🆕 What changed since last review (v1.0 → v1.1, 2026-04-20):
- Factual correction: the
cia-data/tree currently materialises 15 subsystems (not 19 "products"):anomaly,coalition,committee,distribution,election,election-cycle,ministry,parties,party,percentile,politician,pre-election,risk,seasonal,voting. All headline counts, ToC entries, and inventory tables have been reconciled with the filesystem.- Added documentation of the npm-package data contract: typed subpath exports
./cia/*,./dashboards/*,./shared/*,./ui/*inpackage.jsonexpose TypeScript.d.tssurfaces generated fromschemas/viagenerate-types-from-cia-schemas.- Documented schema governance scripts under
scripts/:sync-cia-schemas,validate-against-cia-schemas,check-cia-schema-updates,generate-types-from-cia-schemas(all Node 26 native TypeScript modules).- Added news-corpus footprint: 2,669 files under
news/across 14 languages (EN, SV, DA, NB, DE, ES, FI, FR, HE RTL, AR RTL, JA, KO, NL, ZH). News metadata extracted byextract-news-metadatafeedsgenerate-rss,generate-sitemap,generate-sitemap-html, andgenerate-news-indexes.- Validation stack locked: ajv 8.18.0 (JSON Schema), happy-dom 20.9.0 (DOM test), Vitest 4.1.4 (unit), Cypress 15.14.0 (E2E). All validations invoked in
prebuild/ CI.- Aligned with Secure_Development_Policy §7 "Data Integrity", CLASSIFICATION, ISO 27001:2022 A.5.33/A.8.12, NIST CSF 2.0 PR.DS-1/PR.DS-6/ID.AM-3, CIS Controls v8.1 #3, GDPR Art. 5/32.
This document defines the data model for the Riksdagsmonitor platform, documenting entity relationships, data structures, CIA product schemas, and data quality metrics for Swedish Parliament political data spanning 50+ years.
| Document | Focus | Description |
|---|---|---|
| 🏛️ Architecture | 🏗️ C4 Models | System context, containers, components |
| 📊 Data Model | 📊 Data | Entity relationships and data dictionary |
| 🔄 Flowchart | 🔄 Processes | Business and data flow diagrams |
| 📈 State Diagram | 📈 States | System state transitions and lifecycles |
| 🧠 Mindmap | 🧠 Concepts | System conceptual relationships |
| 💼 SWOT | 💼 Strategy | Strategic analysis and positioning |
| 🛡️ Security Architecture | 🔒 Security | Current security controls and design |
| 🚀 Future Security | 🔮 Security | Planned security improvements |
| 🎯 Threat Model | 🎯 Threats | STRIDE/MITRE ATT&CK analysis |
| 🔧 Workflows | 🔧 DevOps | CI/CD automation and pipelines |
| 🛡️ CRA Assessment | ⚖️ Compliance | EU Cyber Resilience Act conformity |
| 🚀 Future Architecture | 🔮 Evolution | Architectural evolution roadmap |
| 📊 Future Data Model | 🔮 Data | Enhanced data architecture plans |
| 🔄 Future Flowchart | 🔮 Processes | Improved process workflows |
| 📈 Future State Diagram | 🔮 States | Advanced state management |
| 🧠 Future Mindmap | 🔮 Concepts | Capability expansion plans |
| 💼 Future SWOT | 🔮 Strategy | Future strategic opportunities |
Riksdagsmonitor maintains a comprehensive data architecture integrating 50+ years of Swedish Parliament data (1971-2026) with 15 data subsystems from the CIA platform, surfaced through the cia-data/ tree in this repository and re-exported as typed subpaths (./cia/*, ./dashboards/*, ./shared/*, ./ui/*) in the public riksdagsmonitor npm package (SLSA provenance attested). This document defines all data entities, relationships, schemas, pipelines, and integration patterns following Hack23 AB's ISMS standards (ISO 27001:2022, NIST CSF 2.0, CIS Controls v8.1, GDPR, NIS2).
Key Statistics:
- 2,494 Politicians (349 current MPs)
- 3.5M+ Voting Records across all parliaments
- 109,000+ Documents (motions, propositions, reports)
- 8 Political Parties + 40 historical parties
- 15 Committees with complete assignment tracking
- 20 Governments with 76 roles and 500 role members
- 14 Languages with full multi-language support
- 15 CIA Data Subsystems materialised under
cia-data/(anomaly, coalition, committee, distribution, election, election-cycle, ministry, parties, party, percentile, politician, pre-election, risk, seasonal, voting) with 50+ CSV data files
- Political Entities & Data Dictionary
- CIA Data Subsystems (15 Subsystems)
- Entity-Relationship Diagrams
- Data Sources
- Data Schemas & Validation
- Data Pipeline Architecture
- Multi-Language Data Architecture
- Performance & Caching
- C4 Model Integration
- ISMS Compliance
- Analysis Artifact Data Model
- News Corpus Data Model
- Political Intelligence Catalog Data Structure
Table: person_data
Records: 2,494 (349 active MPs)
Source: Swedish Riksdag API + CIA Platform
Update Frequency: Daily
| Field Name | Data Type | Key | Description | Source |
|---|---|---|---|---|
person_id |
VARCHAR(20) | PK | Unique person identifier (Swedish personal number format) | Riksdag API |
first_name |
VARCHAR(100) | Given name | Riksdag API | |
last_name |
VARCHAR(100) | Family name | Riksdag API | |
party |
VARCHAR(10) | FK | Party abbreviation (S, M, SD, C, V, MP, KD, L) | Riksdag API |
gender |
VARCHAR(10) | Gender classification | Riksdag API | |
born_year |
INTEGER | Year of birth | Riksdag API | |
status |
VARCHAR(50) | Current status (Tjänstgörande riksdagsledamot, Ledig, etc.) | Riksdag API | |
district |
VARCHAR(100) | Electoral district (valkrets) | Riksdag API | |
img_url |
VARCHAR(255) | Profile image URL | Riksdag API | |
last_activity_date |
TIMESTAMP | Last recorded activity | CIA Platform | |
total_votes |
INTEGER | Lifetime vote count | CIA Platform | |
total_documents |
INTEGER | Documents authored | CIA Platform | |
risk_score |
DECIMAL(5,2) | Risk assessment score (0-100) | CIA Platform | |
risk_level |
VARCHAR(20) | Risk classification (LOW, MEDIUM, HIGH, CRITICAL) | CIA Platform | |
annual_absence_rate |
DECIMAL(5,2) | Absence percentage (last 12 months) | CIA Platform | |
annual_rebel_rate |
DECIMAL(5,2) | Rebellion rate against party (last 12 months) | CIA Platform |
Indexes:
- Primary Key:
person_id - Foreign Key:
party→sweden_political_party.party_id - Index:
status,party,risk_level
Business Rules:
- Active MPs:
status = 'Tjänstgörande riksdagsledamot' - Risk threshold: HIGH when
risk_score >= 50 - Historical coverage: 1971-2026
Table: sweden_political_party
Records: 40 (12 riksdag parties, 28 historical)
Source: Swedish Riksdag API + Election Authority
Update Frequency: Monthly (on party changes)
| Field Name | Data Type | Key | Description | Source |
|---|---|---|---|---|
party_id |
VARCHAR(10) | PK | Party abbreviation (S, M, SD, C, V, MP, KD, L) | Riksdag API |
party_name |
VARCHAR(200) | Full party name (Swedish) | Riksdag API | |
party_name_en |
VARCHAR(200) | Full party name (English) | Translation | |
founded_year |
INTEGER | Year party was founded | Historical data | |
dissolved_year |
INTEGER | Year party dissolved (NULL if active) | Historical data | |
ideology |
VARCHAR(100) | Political ideology classification | Analysis | |
color |
VARCHAR(7) | Brand color (hex code) | Party branding | |
website |
VARCHAR(255) | Official website URL | Party data | |
riksdag_status |
VARCHAR(20) | Status (RIKSDAG, HISTORICAL, EXTRA_PARLIAMENTARY) | Analysis | |
total_members_current |
INTEGER | Current member count | CIA Platform | |
avg_win_rate |
DECIMAL(5,2) | Average vote win rate (%) | CIA Platform | |
avg_discipline_score |
DECIMAL(5,2) | Party discipline metric (%) | CIA Platform |
Indexes:
- Primary Key:
party_id - Index:
riksdag_status,founded_year
Business Rules:
- Active Riksdag Parties (8): S, M, SD, C, V, MP, KD, L
- Historical Parties: ny, pp, v(k), fp, etc. (32 parties)
- Threshold: Riksdag representation requires >= 4% vote share
Party Descriptions:
| Party | Full Name (Swedish) | Full Name (English) | Ideology |
|---|---|---|---|
| S | Socialdemokraterna | Social Democrats | Social democracy |
| M | Moderaterna | Moderate Party | Liberal conservatism |
| SD | Sverigedemokraterna | Sweden Democrats | National conservatism |
| C | Centerpartiet | Centre Party | Agrarian liberalism |
| V | Vänsterpartiet | Left Party | Democratic socialism |
| MP | Miljöpartiet | Green Party | Green politics |
| KD | Kristdemokraterna | Christian Democrats | Christian democracy |
| L | Liberalerna | Liberals | Social liberalism |
Table: committee_document_data
Records: 8,740 committee documents
Source: Swedish Riksdag API
Update Frequency: Daily
| Field Name | Data Type | Key | Description | Source |
|---|---|---|---|---|
committee_id |
VARCHAR(10) | PK | Committee abbreviation (AU, FiU, UU, etc.) | Riksdag API |
committee_name |
VARCHAR(200) | Full committee name (Swedish) | Riksdag API | |
committee_name_en |
VARCHAR(200) | Full committee name (English) | Translation | |
document_id |
VARCHAR(50) | FK | Document identifier | Riksdag API |
document_type |
VARCHAR(50) | Document type (bet, utskskr, etc.) | Riksdag API | |
published_date |
DATE | Publication date | Riksdag API | |
title |
TEXT | Document title | Riksdag API | |
summary |
TEXT | Document summary | Riksdag API | |
assigned_members |
JSON | Array of person_ids assigned | CIA Platform |
Indexes:
- Primary Key:
committee_id+document_id - Foreign Key:
document_id→document_data.document_id - Index:
published_date,document_type
Swedish Riksdag Committees (15):
| Code | Swedish Name | English Name | Jurisdiction |
|---|---|---|---|
| AU | Arbetsmarknadsutskottet | Labour Market Committee | Employment, labour law |
| CU | Civilutskottet | Civil Affairs Committee | Justice, civil law |
| FiU | Finansutskottet | Finance Committee | Budget, taxation |
| FöU | Försvarsutskottet | Defence Committee | Defence, military |
| JuU | Justitieutskottet | Justice Committee | Criminal law, courts |
| KU | Konstitutionsutskottet | Constitutional Committee | Constitution, governance |
| KrU | Kulturutskottet | Cultural Affairs Committee | Culture, media, religion |
| MJU | Miljö- och jordbruksutskottet | Environment and Agriculture Committee | Environment, farming |
| NU | Näringsutskottet | Industry and Trade Committee | Business, energy |
| SkU | Skatteutskottet | Tax Committee | Tax policy |
| SoU | Socialutskottet | Social Affairs Committee | Healthcare, welfare |
| SfU | Socialförsäkringsutskottet | Social Insurance Committee | Social insurance |
| TU | Trafikutskottet | Transport Committee | Infrastructure, transport |
| UU | Utrikesutskottet | Foreign Affairs Committee | Foreign policy, EU |
| UtbU | Utbildningsutskottet | Education Committee | Education, research |
Table: document_data
Records: 109,259
Source: Swedish Riksdag API
Update Frequency: Daily
| Field Name | Data Type | Key | Description | Source |
|---|---|---|---|---|
document_id |
VARCHAR(50) | PK | Unique document identifier (e.g., H901FiU1) | Riksdag API |
document_type |
VARCHAR(50) | Type code (mot, prop, bet, skr, etc.) | Riksdag API | |
document_number |
VARCHAR(20) | Sequential number within type | Riksdag API | |
rm |
VARCHAR(10) | Riksmöte (parliamentary year, e.g., 2024/25) | Riksdag API | |
title |
TEXT | Document title | Riksdag API | |
subtitle |
TEXT | Document subtitle | Riksdag API | |
published_date |
DATE | Publication date | Riksdag API | |
status |
VARCHAR(50) | Processing status | Riksdag API | |
organ |
VARCHAR(10) | Responsible committee | Riksdag API | |
authors |
JSON | Array of person_ids (authors) | Riksdag API | |
fulltext |
TEXT | Full document text (optional) | Riksdag API | |
attachments |
JSON | Array of attachment URLs | Riksdag API | |
related_documents |
JSON | Array of related document_ids | Riksdag API |
Indexes:
- Primary Key:
document_id - Index:
document_type,rm,published_date,organ - Full-text index:
title,subtitle,fulltext
Document Types:
| Type Code | Swedish Name | English Name | Count | Description |
|---|---|---|---|---|
| mot | Motion | Motion | 94,633 | MP-initiated proposals |
| prop | Proposition | Government Bill | 5,738 | Government proposals |
| bet | Betänkande | Committee Report | 58,231 | Committee decisions |
| skr | Skrivelse | Communication | ~2,000 | Government communications |
| ip | Interpellation | Interpellation | ~5,000 | Questions to ministers |
| fr | Fråga | Written Question | ~8,000 | Written questions |
| sou | Statens offentliga utredningar | Government Official Reports | External | Public investigations |
| ds | Departementsserien | Ministry Report Series | External | Ministry reports |
Table: vote_data
Records: 3,529,786
Source: Swedish Riksdag API
Update Frequency: Real-time (after votes)
| Field Name | Data Type | Key | Description | Source |
|---|---|---|---|---|
vote_id |
VARCHAR(50) | PK | Unique vote identifier | CIA Platform |
ballot_id |
VARCHAR(50) | FK | Ballot session identifier | Riksdag API |
person_id |
VARCHAR(20) | FK | Voter person_id | Riksdag API |
party |
VARCHAR(10) | FK | Party at time of vote | Riksdag API |
vote |
VARCHAR(20) | Vote cast (Ja, Nej, Avstår, Frånvarande) | Riksdag API | |
vote_date |
DATE | Date of vote | Riksdag API | |
vote_time |
TIME | Time of vote | Riksdag API | |
issue |
TEXT | Issue being voted on | Riksdag API | |
document_id |
VARCHAR(50) | FK | Related document | Riksdag API |
committee |
VARCHAR(10) | Responsible committee | Riksdag API | |
is_rebel_vote |
BOOLEAN | Vote against party line | CIA Platform | |
is_winning_vote |
BOOLEAN | Vote with majority | CIA Platform |
Indexes:
- Primary Key:
vote_id - Foreign Keys:
person_id,party,ballot_id,document_id - Index:
vote_date,party,is_rebel_vote
Vote Classifications:
- Ja (Yes): Approval vote
- Nej (No): Rejection vote
- Avstår (Abstain): Abstention
- Frånvarande (Absent): Not present
Metrics Derived:
- Win Rate:
(winning_votes / total_votes) * 100 - Rebel Rate:
(rebel_votes / total_votes) * 100 - Attendance Rate:
((total_votes - absent) / total_ballots) * 100
Table: government_body_data
Records: 6,520 (20 governments, 76 roles, 500 role members)
Source: Swedish Government + CIA Platform
Update Frequency: On government changes
| Field Name | Data Type | Key | Description | Source |
|---|---|---|---|---|
ministry_id |
VARCHAR(50) | PK | Ministry identifier | Government data |
ministry_name |
VARCHAR(200) | Ministry name (Swedish) | Government data | |
ministry_name_en |
VARCHAR(200) | Ministry name (English) | Translation | |
government_id |
VARCHAR(50) | FK | Government identifier | Government data |
start_date |
DATE | Ministry start date | Government data | |
end_date |
DATE | Ministry end date (NULL if current) | Government data | |
minister_person_id |
VARCHAR(20) | FK | Current/last minister | Government data |
party |
VARCHAR(10) | FK | Party affiliation | Government data |
portfolio |
VARCHAR(200) | Portfolio responsibilities | Government data | |
decision_count |
INTEGER | Total decisions made | CIA Platform | |
effectiveness_score |
DECIMAL(5,2) | Effectiveness metric (0-100) | CIA Platform | |
risk_level |
VARCHAR(20) | Risk classification | CIA Platform |
Indexes:
- Primary Key:
ministry_id - Foreign Keys:
government_id,minister_person_id,party - Index:
start_date,end_date,effectiveness_score
Current Swedish Ministries (11):
| Ministry | Swedish Name | Minister | Portfolio |
|---|---|---|---|
| Prime Minister's Office | Statsrådsberedningen | Prime Minister | Overall government coordination |
| Finance | Finansdepartementet | Finance Minister | Budget, taxes, economy |
| Foreign Affairs | Utrikesdepartementet | Foreign Minister | International relations |
| Defence | Försvarsdepartementet | Defence Minister | Military, security |
| Justice | Justitiedepartementet | Justice Minister | Courts, police, law |
| Interior | Inrikesdepartementet | Interior Minister | Migration, citizenship |
| Health & Social Affairs | Socialdepartementet | Social Affairs Minister | Healthcare, welfare |
| Employment | Arbetsmarknadsdepartementet | Employment Minister | Labour market |
| Education | Utbildningsdepartementet | Education Minister | Schools, universities |
| Environment | Miljödepartementet | Environment Minister | Climate, nature |
| Infrastructure | Infrastrukturdepartementet | Infrastructure Minister | Transport, housing |
Table: government_role_data
Records: 76 roles, 500 role members
Description: Cabinet positions and government appointments
| Field Name | Data Type | Key | Description | Source |
|---|---|---|---|---|
role_id |
VARCHAR(50) | PK | Role identifier | Government data |
role_name |
VARCHAR(200) | Role title (Swedish) | Government data | |
role_name_en |
VARCHAR(200) | Role title (English) | Translation | |
role_type |
VARCHAR(50) | Type (Minister, State Secretary, etc.) | Government data | |
ministry_id |
VARCHAR(50) | FK | Parent ministry | Government data |
person_id |
VARCHAR(20) | FK | Current holder | Government data |
start_date |
DATE | Role assignment start | Government data | |
end_date |
DATE | Role assignment end (NULL if current) | Government data | |
party |
VARCHAR(10) | FK | Party affiliation | Government data |
Role Types:
- Minister (Minister): Cabinet minister
- Statssekreterare (State Secretary): Senior civil servant
- Politiskt sakkunnig (Political Advisor): Political staff
- Pressekreterare (Press Secretary): Communications
Note (v1.1, 2026-04-20): The prior framing of "19 Products" has been retired. The current repository materialises 15 subsystems under
cia-data/, each backed by one or more CSV extracts and (where applicable) a JSON Schema underschemas/. The subsystem list below is the canonical, filesystem-verified inventory.
Product Name: Riksdag Intelligence Overview
Purpose: Comprehensive snapshot of parliamentary activity
Data Files:
view_riksdagen_politician_sample.csvview_riksdagen_party_summary_sample.csvcia-data/production-stats.json
Key Fields & Metrics:
- Total active MPs (349)
- Total votes cast (3.5M+)
- Total documents (109K+)
- Party representation breakdown
- Committee assignments
- Risk score distribution
Dashboard Integration: Homepage overview section
Update Frequency: Daily (03:00 CET)
Product Name: Party Performance & Effectiveness Analytics
Purpose: Longitudinal party analysis (1990-2026, 37 years)
Data Files:
cia-data/party/view_party_effectiveness_trends_sample.csvcia-data/party/view_party_performance_metrics_sample.csvcia-data/party/distribution_party_effectiveness_trends.csvcia-data/party/distribution_party_momentum.csv
Key Fields & Metrics:
| Field | Type | Description |
|---|---|---|
party |
VARCHAR(10) | Party abbreviation |
year |
INTEGER | Analysis year |
performance_score |
DECIMAL(5,2) | Overall performance (0-100) |
win_rate |
DECIMAL(5,2) | Vote success rate (%) |
discipline_score |
DECIMAL(5,2) | Party unity metric (%) |
document_productivity |
INTEGER | Documents produced |
avg_attendance |
DECIMAL(5,2) | Attendance rate (%) |
effectiveness_trend |
VARCHAR(20) | Trend direction (IMPROVING, DECLINING, STABLE) |
momentum_percentile |
DECIMAL(5,2) | Performance percentile (0-100) |
Dashboard Integration: Party Performance Dashboard (index.html)
Visualizations:
- Effectiveness trends timeline (Chart.js)
- Party comparison scatter plot (D3.js)
- Momentum indicator gauges
- Coalition alignment matrix
Update Frequency: Daily
Product Name: Ministry Performance Scorecards
Purpose: Cabinet-level analysis and ministry effectiveness
Data Files:
cia-data/ministry/distribution_ministry_effectiveness.csvcia-data/ministry/distribution_ministry_decision_impact.csvcia-data/ministry/distribution_ministry_productivity_matrix.csvcia-data/ministry/distribution_ministry_risk_levels.csv
Key Fields & Metrics:
| Field | Type | Description |
|---|---|---|
ministry_name |
VARCHAR(200) | Ministry name |
minister |
VARCHAR(200) | Current minister |
party |
VARCHAR(10) | Party affiliation |
decision_count |
INTEGER | Decisions made |
effectiveness_score |
DECIMAL(5,2) | Effectiveness metric (0-100) |
impact_score |
DECIMAL(5,2) | Decision impact (0-100) |
risk_level |
VARCHAR(20) | Risk classification |
productivity_matrix |
VARCHAR(50) | Productivity classification |
Dashboard Integration: Ministry Dashboard (placeholder)
Visualizations:
- Ministry effectiveness radar chart
- Decision impact heatmap (D3.js)
- Risk level gauge chart
- Productivity matrix scatter plot
Update Frequency: Weekly
Product Name: Historical Patterns & Trend Forecasting
Purpose: Election cycle intelligence (1994-2034, 9 cycles)
Data Files:
cia-data/election-cycle/view_election_cycle_comparative_analysis_sample.csv(1,110 records, 153KB)cia-data/election-cycle/view_election_cycle_decision_intelligence_sample.csv(414 records, 59KB)cia-data/election-cycle/view_election_cycle_predictive_intelligence_sample.csv(41 records, 3.9KB)cia-data/election-cycle/view_election_cycle_temporal_trends_sample.csv(74 records, 8.7KB)
Key Fields & Metrics:
| Field | Type | Description |
|---|---|---|
election_cycle_id |
VARCHAR(20) | Cycle identifier (e.g., "2022-2026") |
cycle_year |
INTEGER | Cycle number |
calendar_year |
INTEGER | Specific year |
semester |
VARCHAR(20) | Time period (annual, spring, autumn) |
party |
VARCHAR(10) | Party abbreviation |
performance_score |
DECIMAL(5,2) | Performance metric (0-100) |
decision_effectiveness |
VARCHAR(50) | Effectiveness category |
risk_forecast_category |
VARCHAR(50) | Risk forecast level |
forecast_confidence |
VARCHAR(20) | Confidence level (low, moderate, high) |
Dashboard Integration: Election Cycle Dashboard (js/election-cycle-dashboard.js)
Visualizations:
- Multi-cycle performance timeline (Chart.js)
- Party tier distribution (D3.js)
- Risk forecast scatter chart
- Temporal trends multi-axis chart
Update Frequency: Daily
Product Name: Politician Influence Network Analysis
Purpose: Identify MPs with highest influence scores
Data Files:
cia-data/politician/view_riksdagen_politician_influence_metrics_sample.csv
Key Fields:
person_id,first_name,last_name,partyinfluence_score(0-100)network_centrality(0-1)committee_influencecross_party_connections
Ranking Criteria: Influence score (descending)
Dashboard Integration: Politician Dashboard
Update Frequency: Weekly
Product Name: Legislative Output Analysis
Purpose: Rank MPs by document production
Data Files:
cia-data/politician/view_riksdagen_politician_sample.csv
Key Fields:
person_id,first_name,last_name,partydocuments_last_year(INTEGER)total_documents(INTEGER)document_types(JSON array)
Ranking Criteria: Documents produced (last 12 months, descending)
Dashboard Integration: Politician Dashboard
Update Frequency: Daily
Product Name: Voting Pattern Outlier Detection
Purpose: Identify MPs with highest rebellion rates
Data Files:
cia-data/politician/view_politician_behavioral_trends_sample.csv
Key Fields:
person_id,first_name,last_name,partyannual_rebel_rate(0-100%)controversial_votes(count)party_discipline_deviation
Ranking Criteria: Rebel rate (descending)
Dashboard Integration: Politician Dashboard
Update Frequency: Daily
Product Name: Attendance Tracking
Purpose: Monitor MP absences
Data Files:
cia-data/politician/view_politician_risk_summary_sample.csv
Key Fields:
person_id,first_name,last_name,partyannual_absence_rate(0-100%)absenteeism_violations(count)attendance_trend
Ranking Criteria: Absence rate (descending)
Dashboard Integration: Politician Dashboard
Update Frequency: Daily
Product Name: Cross-Party Voting Analysis
Purpose: Identify MPs who frequently vote against party line
Data Files:
cia-data/voting/distribution_voting_anomaly_classification.csv
Key Fields:
person_id,party,rebel_vote_countparty_line_deviation_pctcoalition_alignment_score
Ranking Criteria: Rebel votes (descending)
Dashboard Integration: Politician Dashboard
Update Frequency: Daily
Product Name: Cross-Party Collaboration Patterns
Purpose: Identify MPs facilitating coalition work
Data Files:
cia-data/coalition/distribution_coalition_alignment.csv
Key Fields:
person_id,coalition_scorecross_party_proposalsbridge_connections
Ranking Criteria: Coalition score (descending)
Dashboard Integration: Coalition Dashboard
Update Frequency: Weekly
Product Name: Emerging Political Figures
Purpose: Identify rapidly advancing politicians
Data Files:
cia-data/politician/view_politician_behavioral_trends_sample.csv
Key Fields:
person_id,career_trajectorymomentum_scoremedia_mentions_growthinfluence_acceleration
Ranking Criteria: Momentum score (descending)
Dashboard Integration: Politician Dashboard
Update Frequency: Weekly
Product Name: MPs at Election Risk
Purpose: Predict electoral vulnerability
Data Files:
cia-data/politician/view_politician_risk_summary_sample.csv
Key Fields:
person_id,risk_score(0-100)risk_level(LOW, MEDIUM, HIGH, CRITICAL)risk_factors(JSON array)
Ranking Criteria: Risk score (descending)
Dashboard Integration: Politician Dashboard
Update Frequency: Daily
Product Name: Rule Violation Tracking
Purpose: Monitor transparency and ethics violations
Data Files:
cia-data/politician/view_politician_risk_summary_sample.csv
Key Fields:
person_id,total_violationsviolation_types(effectiveness, discipline, productivity, collaboration)latest_violation_date
Ranking Criteria: Total violations (descending)
Dashboard Integration: Politician Dashboard
Update Frequency: Daily
Product Name: Public Visibility Index
Purpose: Track media mentions and public visibility
Data Files:
- External media analysis (placeholder)
Key Fields:
person_id,media_mentions_countvisibility_score(0-100)sentiment_average(-1 to +1)
Ranking Criteria: Media mentions (descending)
Dashboard Integration: Placeholder
Update Frequency: Weekly
Product Name: Committee Influence Mapping
Purpose: Visualize committee assignments and influence
Data Files:
cia-data/committee/view_riksdagen_committee_decisions.csvcia-data/committee/distribution_annual_committee_documents.csv
Key Fields & Metrics:
committee_id,committee_nameassigned_members(JSON array)document_count,decision_countproductivity_score(0-100)influence_centrality(0-1)
Dashboard Integration: Committee Dashboard
Visualizations:
- Network graph (D3.js force-directed)
- Assignment matrix heatmap
- Productivity comparison bar chart
Update Frequency: Weekly
Product Name: Career Trajectory Tracking
Purpose: Analyze politician career paths and milestones
Data Files:
cia-data/politician/view_riksdagen_politician_experience_summary_sample.csvcia-data/politician/distribution_experience_levels.csv
Key Fields & Metrics:
person_id,career_start_date,total_yearsroles_held(JSON array)committee_assignments_historygovernment_positionsexperience_level(JUNIOR, INTERMEDIATE, SENIOR, VETERAN)
Dashboard Integration: Politician Dashboard
Visualizations:
- Career timeline (Gantt chart)
- Experience distribution (Chart.js)
- Role progression flowchart
Update Frequency: Monthly
Product Name: 50+ Years of Party Evolution
Purpose: Historical party performance (1971-2026)
Data Files:
cia-data/party/view_party_effectiveness_trends_sample.csvcia-data/party/distribution_annual_party_votes.csvcia-data/party/distribution_annual_party_members.csv
Key Fields & Metrics:
party,year,decademember_count,vote_countavg_win_rate,avg_disciplineelectoral_success_ratehistorical_trend(RISING, DECLINING, STABLE)
Dashboard Integration: Party Performance Dashboard
Visualizations:
- 50-year timeline (Chart.js)
- Party evolution heatmap (D3.js)
- Electoral cycle comparison
Update Frequency: Daily
Product Name: Quarterly Parliamentary Activity Analysis
Purpose: Identify seasonal trends and patterns (2002-2025)
Data Files:
cia-data/seasonal/view_riksdagen_seasonal_activity_patterns_sample.csv(85 records)
Key Fields & Metrics:
year,quarter,is_election_year,election_cycletotal_ballots,active_politicians,attendance_ratedocuments_produced,decisions_madeq_baseline_ballots,q_baseline_docs,q_baseline_attendanceballot_z_score,doc_z_score,attendance_z_scorebase_activity_classification(LOW, MODERATE, HIGH, VERY_HIGH)seasonal_pattern_classificationcross_year_quarter_avg_ballots,cross_year_z_scoreqoq_ballot_change_pct,activity_quartile_cycle
Dashboard Integration: Seasonal Activity Patterns Dashboard
Visualizations:
- Quarterly heatmap (D3.js)
- Time series chart (Chart.js)
- Z-score distribution
- Cross-year comparison
Update Frequency: Quarterly
Product Name: Statistical Outlier Identification
Purpose: Real-time anomaly detection (2002-2026, 41 quarters)
Data Files:
cia-data/seasonal/view_riksdagen_seasonal_anomaly_detection_sample.csv(41 records)
Key Fields & Metrics:
year,quarter,is_election_year,parliamentary_periodtotal_ballots,active_politicians,attendance_rate,documents_producedq_baseline_ballots,q_baseline_docs,q_baseline_attendanceq_stddev_ballots,q_stddev_docs,q_stddev_attendanceballot_z_score,doc_z_score,attendance_z_scoreactivity_classification(NORMAL, UNUSUALLY_LOW, UNUSUALLY_HIGH)anomaly_type(Ballot, Document, Attendance, Mixed)anomaly_direction(UNUSUALLY_HIGH, UNUSUALLY_LOW, NORMAL)max_z_score,anomaly_severity(LOW, MODERATE, HIGH, CRITICAL)quarter_label(Q1_JAN_MAR, Q2_APR_JUN, Q3_JUL_SEP, Q4_OCT_DEC)
Anomaly Detection Criteria:
|Z| < 1.5: LOW severity (within normal range)1.5 ≤ |Z| < 2.0: MODERATE severity2.0 ≤ |Z| < 2.5: HIGH severity|Z| ≥ 2.5: CRITICAL severity
Historical Findings (41 quarters):
- 8 CRITICAL anomalies (Z ≥ 2.5)
- 2 HIGH anomalies (2.0 ≤ Z < 2.5)
- 12 MODERATE anomalies (1.5 ≤ Z < 2.0)
- 19 LOW/NORMAL (normal activity)
- Most extreme: 2006 Q1 document anomaly (Z = +10.97)
Dashboard Integration: Anomaly Detection Dashboard (index.html, inline script)
Visualizations:
- Anomaly timeline (Chart.js)
- Z-score distribution (histogram)
- Severity classification (pie chart)
- Heatmap (D3.js)
- Alert panel (critical anomalies)
Update Frequency: Quarterly
erDiagram
POLITICIAN ||--o{ VOTE : casts
POLITICIAN }o--|| PARTY : belongs_to
POLITICIAN }o--o{ COMMITTEE : assigned_to
POLITICIAN ||--o{ DOCUMENT : authors
POLITICIAN }o--o{ GOVERNMENT_ROLE : holds
PARTY ||--o{ POLITICIAN : has_members
PARTY ||--o{ MINISTRY : controls
DOCUMENT ||--o{ VOTE : triggers
DOCUMENT }o--|| COMMITTEE : processed_by
DOCUMENT }o--o{ DOCUMENT : references
COMMITTEE ||--o{ DOCUMENT : produces
COMMITTEE }o--o{ POLITICIAN : includes
MINISTRY ||--o{ GOVERNMENT_ROLE : contains
MINISTRY }o--|| PARTY : led_by
MINISTRY ||--o{ DOCUMENT : issues
GOVERNMENT_ROLE }o--|| POLITICIAN : held_by
GOVERNMENT_ROLE }o--|| MINISTRY : part_of
POLITICIAN {
varchar person_id PK
varchar first_name
varchar last_name
varchar party FK
varchar status
decimal risk_score
varchar risk_level
}
PARTY {
varchar party_id PK
varchar party_name
integer founded_year
varchar riksdag_status
decimal avg_win_rate
}
COMMITTEE {
varchar committee_id PK
varchar committee_name
integer total_documents
decimal productivity_score
}
DOCUMENT {
varchar document_id PK
varchar document_type
date published_date
varchar status
varchar organ FK
}
VOTE {
varchar vote_id PK
varchar ballot_id FK
varchar person_id FK
varchar party FK
varchar vote
date vote_date
boolean is_rebel_vote
}
MINISTRY {
varchar ministry_id PK
varchar ministry_name
varchar minister_person_id FK
varchar party FK
decimal effectiveness_score
varchar risk_level
}
GOVERNMENT_ROLE {
varchar role_id PK
varchar role_name
varchar role_type
varchar ministry_id FK
varchar person_id FK
date start_date
date end_date
}
erDiagram
BALLOT ||--o{ VOTE : contains
BALLOT }o--|| DOCUMENT : relates_to
BALLOT }o--|| COMMITTEE : from
VOTE }o--|| POLITICIAN : cast_by
VOTE }o--|| PARTY : party_vote
VOTE }o--|| BALLOT : in_ballot
BALLOT {
varchar ballot_id PK
date ballot_date
time ballot_time
varchar issue
varchar document_id FK
varchar committee FK
integer total_votes
integer yes_count
integer no_count
integer abstain_count
integer absent_count
}
VOTE {
varchar vote_id PK
varchar ballot_id FK
varchar person_id FK
varchar party FK
varchar vote
boolean is_rebel_vote
boolean is_winning_vote
}
POLITICIAN {
varchar person_id PK
varchar party FK
integer total_votes
decimal annual_rebel_rate
decimal annual_absence_rate
}
PARTY {
varchar party_id PK
decimal avg_win_rate
decimal avg_discipline_score
}
erDiagram
DOCUMENT ||--o{ DOCUMENT_AUTHOR : has
DOCUMENT ||--o{ DOCUMENT_ATTACHMENT : includes
DOCUMENT ||--o{ DOCUMENT_REFERENCE : references
DOCUMENT ||--o{ COMMITTEE_DECISION : leads_to
DOCUMENT }o--|| COMMITTEE : assigned_to
DOCUMENT_AUTHOR }o--|| POLITICIAN : authored_by
DOCUMENT_AUTHOR }o--|| DOCUMENT : for_document
COMMITTEE_DECISION }o--|| DOCUMENT : about
COMMITTEE_DECISION }o--|| COMMITTEE : made_by
DOCUMENT {
varchar document_id PK
varchar document_type
varchar rm
date published_date
varchar status
varchar organ FK
text title
text subtitle
text fulltext
}
DOCUMENT_AUTHOR {
varchar document_id PK_FK
varchar person_id PK_FK
integer author_order
}
DOCUMENT_ATTACHMENT {
varchar attachment_id PK
varchar document_id FK
varchar filename
varchar url
integer size_bytes
}
DOCUMENT_REFERENCE {
varchar source_doc_id PK_FK
varchar target_doc_id PK_FK
varchar reference_type
}
COMMITTEE_DECISION {
varchar decision_id PK
varchar document_id FK
varchar committee_id FK
date decision_date
varchar decision_outcome
text decision_text
}
COMMITTEE {
varchar committee_id PK
varchar committee_name
integer total_documents
}
POLITICIAN {
varchar person_id PK
integer total_documents
}
erDiagram
GOVERNMENT ||--o{ MINISTRY : contains
GOVERNMENT }o--|| PARTY : led_by
MINISTRY ||--o{ GOVERNMENT_ROLE : has_roles
MINISTRY }o--|| POLITICIAN : headed_by
MINISTRY }o--|| PARTY : party_affiliation
GOVERNMENT_ROLE }o--|| POLITICIAN : held_by
GOVERNMENT_ROLE }o--|| MINISTRY : part_of
GOVERNMENT {
varchar government_id PK
varchar government_name
date start_date
date end_date
varchar prime_minister_id FK
varchar leading_party FK
varchar coalition_parties
}
MINISTRY {
varchar ministry_id PK
varchar government_id FK
varchar ministry_name
varchar minister_person_id FK
varchar party FK
integer decision_count
decimal effectiveness_score
varchar risk_level
}
GOVERNMENT_ROLE {
varchar role_id PK
varchar role_name
varchar role_type
varchar ministry_id FK
varchar person_id FK
varchar party FK
date start_date
date end_date
}
POLITICIAN {
varchar person_id PK
varchar party FK
}
PARTY {
varchar party_id PK
varchar party_name
}
erDiagram
POLITICIAN ||--o{ RULE_VIOLATION : has
POLITICIAN ||--|| RISK_ASSESSMENT : assessed_by
PARTY ||--o{ POLITICIAN : contains
PARTY ||--|| PARTY_RISK_METRICS : has_metrics
MINISTRY ||--|| MINISTRY_RISK_METRICS : has_metrics
RULE_VIOLATION {
varchar violation_id PK
varchar person_id FK
date violation_date
varchar violation_type
text violation_description
varchar severity
}
RISK_ASSESSMENT {
varchar person_id PK_FK
decimal risk_score
varchar risk_level
integer total_violations
decimal annual_absence_rate
decimal annual_rebel_rate
date last_updated
text risk_assessment_text
}
PARTY_RISK_METRICS {
varchar party_id PK_FK
integer members_at_risk
decimal avg_party_risk_score
integer total_party_violations
varchar party_risk_level
}
MINISTRY_RISK_METRICS {
varchar ministry_id PK_FK
varchar risk_level
integer risk_violations
decimal risk_score
date last_assessment
}
POLITICIAN {
varchar person_id PK
varchar party FK
decimal risk_score
varchar risk_level
}
PARTY {
varchar party_id PK
}
MINISTRY {
varchar ministry_id PK
}
| Relationship | Cardinality | Description |
|---|---|---|
| Politician → Party | Many-to-One | Each politician belongs to one party |
| Politician → Vote | One-to-Many | Each politician casts many votes |
| Politician → Document | One-to-Many | Each politician authors many documents |
| Politician → Committee | Many-to-Many | Politicians assigned to multiple committees |
| Politician → Government Role | Many-to-Many | Politicians can hold multiple roles over time |
| Party → Politician | One-to-Many | Each party has many members |
| Party → Ministry | One-to-Many | Each party can control multiple ministries |
| Committee → Document | One-to-Many | Each committee produces many documents |
| Committee → Politician | Many-to-Many | Committees have multiple members |
| Document → Vote | One-to-Many | Each document can trigger multiple votes |
| Document → Committee | Many-to-One | Each document processed by one committee |
| Document → Document | Many-to-Many | Documents reference other documents |
| Ministry → Government Role | One-to-Many | Each ministry has multiple roles |
| Ministry → Party | Many-to-One | Each ministry led by one party |
URL: https://data.riksdagen.se/
Type: REST API + Open Data Portal
Authentication: None (public data)
Data Coverage: 1971-present (50+ years)
Endpoints:
/personlista/- MPs and politicians/dokument/- Parliamentary documents/votering/- Voting records/utskott/- Committee information/anforande/- Chamber speeches
Update Frequency:
- Real-time: Votes, speeches
- Daily: Documents, committee reports
- On change: MP assignments, party membership
Data Completeness: 98.5% (estimated)
Reliability: 99.9% uptime (government infrastructure)
Integration Method: CIA Platform batch processing + real-time updates
URL: https://val.se/
Type: Open Data Portal
Authentication: None (public data)
Data Coverage: 1911-present (electoral results)
Data Products:
- Election results (Riksdag, kommun, region)
- Voter turnout statistics
- Electoral district boundaries
- Candidate lists
Update Frequency: Post-election (every 4 years + by-elections)
Data Format: CSV, Excel, JSON
Reliability: Official government source
Integration Method: Manual download + CIA Platform import
URL: https://www.esv.se/psidata/
Type: PSI Data Portal (Public Sector Information)
Authentication: None (public data)
Data Coverage: 2000-present (budget data)
Data Products:
- Government budget (Statsbudget)
- Ministry spending (Utgiftsområden)
- Agency budgets
- Financial forecasts
Update Frequency: Annual (budget cycle) + quarterly reports
Data Format: CSV, Excel
Reliability: Official government financial data
Integration Method: Manual download + CIA Platform import
URL: https://data.worldbank.org/
Type: REST API + Open Data Portal
Authentication: None (public data)
Data Coverage: 1960-present (country indicators)
Data Products:
- GDP per capita
- Government effectiveness indicators
- Education and health metrics
- Democracy indices
Update Frequency: Annual
Data Format: JSON, CSV, XML
Reliability: International organization standard
Integration Method: CIA Platform API client
URL: https://data.imf.org/ (documentation) • www.imf.org/external/datamapper/api/v1 (Datamapper JSON) • api.imf.org/external/sdmx/3.0 (SDMX 3.0)
Type: REST (Datamapper JSON v1) + SDMX 3.0
Authentication: None (public data)
Data Classification: Public (same as SCB / World Bank)
Data Coverage: 1980-present (macro); annual + quarterly + monthly depending on dataset; projections to ~2031
Data Products:
- WEO (World Economic Outlook) — NGDP_RPCH (real GDP growth), PCPIPCH (CPI inflation), LUR (unemployment), GGXWDG_NGDP (gross debt / GDP), BCA_NGDPD (current account / GDP), …
- Fiscal Monitor (FM) — fiscal balance, primary balance, expenditure composition
- IFS (International Financial Statistics) — monetary, FX, balance-of-payments series
- MFS (Monetary & Financial Statistics) — policy rate, money-market rates
- GFS_COFOG — committee-aligned government spending by function
- DOTS (Direction of Trade Statistics) — bilateral trade flows
- ~155 SDMX databases in total
Update Frequency:
- WEO: April and October each year (projections refreshed at each vintage)
- Fiscal Monitor: April and October
- IFS: monthly
- MFS: monthly
- Projections published at T+5 years per vintage
Data Format: JSON (Datamapper), SDMX 3.0 JSON / XML
Reliability: ~99.5% availability; international-organization standard
Integration Method: Pure-TypeScript client scripts/imf-client.ts (sibling of scripts/world-bank-client.ts and scripts/scb-client.ts) — not an MCP server (ADR 0001 rationale: npm-SBOM coverage, no Python / uvx / third-party MCP). Invoked by agentic workflows via the bash tool and imported directly by build-time scripts.
Schema/Validation: DatamapperResponse shape in imf-client.ts (numeric-finite check, year parse-guard); SDMX 3.0 schema validation for structural metadata.
Caching: analysis/data/imf/{indicator}/{country}.json + sidecar .meta.json (mcpTool: imf-ts-client, projectionVintage: "WEO-2026-04", fetch timestamp).
Rate-limit handling: ~10 req / 5 s, 3× exponential back-off (1s → 2s → 4s), multi-country batching via Datamapper compare.
Allowlisted egress hosts: data.imf.org, api.imf.org, www.imf.org.
Supporting docs (referenced, not duplicated): analysis/imf/README.md, analysis/imf/indicator-policy-mapping.md, analysis/imf/use-cases.md, docs/adr/0001-adopt-imf-data-alongside-world-bank.md, .github/aw/ECONOMIC_DATA_CONTRACT.md.
IMF cache model (formal):
analysis/data/imf/{indicator}/{country}.json # Datamapper payload
analysis/data/imf/{indicator}/{country}.meta.json # provenance sidecar
{indicator}.json payload (subset, normalised by imf-client.ts):
{
"indicator": "NGDP_RPCH",
"country": "SWE",
"values": [
{ "year": 2024, "value": 0.7, "projection": false },
{ "year": 2025, "value": 1.6, "projection": false },
{ "year": 2026, "value": 2.1, "projection": true }
]
}.meta.json sidecar (always co-written):
{
"provider": "imf",
"mcpTool": "imf-ts-client",
"dataflow": "WEO",
"indicator": "NGDP_RPCH",
"country": "SWE",
"vintage": "WEO-2026-04",
"retrieved_at": "2026-05-06T03:14:22Z",
"sourceUrl": "https://www.imf.org/external/datamapper/api/v1/NGDP_RPCH/SWE",
"transport": "datamapper-v1"
}SCB cache model (sibling pattern):
analysis/data/scb/{table_id}/{query_hash}.json
analysis/data/scb/{table_id}/{query_hash}.meta.json # provider="scb", dataflow=PxWeb table_id, retrieved_at
World Bank cache model (sibling pattern):
analysis/data/world-bank/{indicator}/{country}.json
analysis/data/world-bank/{indicator}/{country}.meta.json # provider="worldbank", dataflow="WGI"|"WDI", retrieved_at
Riksbank cache model:
analysis/data/riksbank/{series}/{period}.json
analysis/data/riksbank/{series}/{period}.meta.json # provider="riksbank", dataflow="MonetaryPolicy"|"FX", retrieved_at
Statskontoret cache model (cross-referenced from §"Statskontoret Data Model Extension" below):
analysis/data/statskontoret/{dataset}/{artifact}.json
analysis/data/statskontoret/{dataset}/{artifact}.meta.json # provider="statskontoret", retrieved_at
RiR (Riksrevisionen) cache model:
analysis/data/rir/{followup_id}.json
analysis/data/rir/{followup_id}.meta.json # provider="rir", retrieved_at
Common economicProvenance block — every economic data point that flows into article frontmatter or body MUST carry an economicProvenance block. Schema:
{
"economicProvenance": {
"provider": "imf | scb | worldbank | riksbank | statskontoret | rir",
"dataflow": "string (e.g. WEO, FM, WGI, WDI, PxWeb table_id, MonetaryPolicy)",
"indicator": "string (provider-native indicator code)",
"vintage": "string (e.g. WEO-2026-04, PxWeb-2026Q1, WGI-2025)",
"retrieved_at": "string (ISO 8601 UTC timestamp)"
}
}The Economic Data Contract enforces vintage discipline: any vintage older than 6 months MUST carry an explicit annotation in the article footnotes. See .github/aw/ECONOMIC_DATA_CONTRACT.md for the full rules and banned-phrase list.
URL: https://www.hack23.com/cia
Type: Java/Spring Boot application (backend data processing)
Authentication: Public read access, admin for updates
Data Coverage: Aggregated data from all sources above
Purpose:
- Data aggregation and normalization
- Intelligence product generation
- Risk assessment calculations
- Historical trend analysis
Update Frequency: Daily batch processing (03:00 CET)
Output Format: CSV exports, JSON statistics
Reliability: 99% uptime (self-hosted)
Integration Method: Direct CSV export consumption by Riksdagsmonitor
| Source | Type | Coverage | Frequency | Reliability | Integration |
|---|---|---|---|---|---|
| Riksdag API | REST API | 1971-present | Real-time/Daily | 99.9% | CIA batch + real-time |
| Election Authority | Open Data | 1911-present | Post-election | 99.9% | Manual + CIA import |
| Financial Authority | PSI Portal | 2000-present | Annual/Quarterly | 99.9% | Manual + CIA import |
| World Bank | REST API | 1960-present | Annual | 99.5% | CIA API client |
| IMF Open Data | REST (Datamapper JSON v1) + SDMX 3.0 | 1980-present (macro); projections to ~2031 | WEO Apr/Oct, FM Apr/Oct, IFS/MFS monthly | ~99.5% | Pure-TypeScript client scripts/imf-client.ts (no MCP) |
| CIA Platform | Backend App | Aggregated | Daily (03:00 CET) | 99% | CSV export |
| Metric | Target | Current | Method |
|---|---|---|---|
| Completeness | 95%+ | 98.5% | Field population analysis |
| Accuracy | 99%+ | 99.2% | Cross-source validation |
| Timeliness | <24 hours | <12 hours | Update lag monitoring |
| Consistency | 100% | 99.8% | Schema validation |
| Validity | 100% | 99.9% | Data type checks |
Quality Assurance:
- Automated validation against JSON schemas
- Cross-reference checks between sources
- Anomaly detection on data imports
- Manual spot-checking of critical data
- Version control of all data files
Location: /schemas/cia/ (CIA upstream-mirrored) and /schemas/ (root, repo-owned)
Purpose: Validate CIA platform data exports + repo-owned article-type / PIR / RiR contracts
Standard: JSON Schema Draft 2020-12
Validator: ajv 8.18.0 (invoked from scripts/validate-against-cia-schemas.ts and prebuild chain)
| Schema file | Purpose | Consumed by |
|---|---|---|
schemas/article-types.schema.json |
Article-type registry (validates analysis/article-types.json) |
scripts/horizon-context.ts, news pipeline |
schemas/pir-status.schema.json |
Priority Intelligence Requirement status | PIR roll-forward + horizon-pir-rollforward template |
schemas/rir-followups-schema.json |
Riksrevisionen follow-up payload | scripts/rir-followups-client.ts, scripts/fetch-rir-followups.ts |
The public riksdagsmonitor npm package (v0.8.76, "type": "module") exposes typed .d.ts surfaces under four subpaths, all generated by scripts/generate-types-from-cia-schemas.ts from the schemas above:
| Subpath | Contents |
|---|---|
./ |
Root barrel |
./shared, ./shared/* |
Shared utilities (src/browser/shared/) including typed DashboardGlobals for Chart.js / D3 / PapaParse globals (no any) |
./cia/* |
CIA-data typed views generated from schemas/cia/ |
./dashboards/* |
Dashboard renderers (src/browser/dashboards/) |
./ui/* |
Reusable UI primitives (src/browser/ui/) |
"sideEffects" is restricted to ./dist/lib/shared/register-globals.js and ./src/browser/cia-entry.ts so downstream consumers can tree-shake everything else.
| Schema File | Purpose | Entities Validated |
|---|---|---|
party-performance.schema.json |
Party effectiveness metrics | Party performance data |
politician-profile.schema.json |
Politician profiles | Politician data |
Schema Example (party-performance.schema.json):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://riksdagsmonitor.com/schemas/cia/party-performance.schema.json",
"title": "Party Performance Schema",
"description": "Validates party effectiveness and performance metrics",
"type": "object",
"properties": {
"party": {
"type": "string",
"enum": ["S", "M", "SD", "C", "V", "MP", "KD", "L"]
},
"year": {
"type": "integer",
"minimum": 1971,
"maximum": 2026
},
"performance_score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"win_rate": {
"type": "number",
"minimum": 0,
"maximum": 100
}
},
"required": ["party", "year", "performance_score"]
}Location: /cia-data/
Format: UTF-8 encoded CSV with header row
Delimiter: Comma (,)
Quote Character: Double quote (")
Line Ending: LF (\n)
Header Row Requirements:
- First row must contain field names
- Field names: lowercase with underscores (snake_case)
- No spaces or special characters (except underscore)
Data Type Conventions:
- Dates:
YYYY-MM-DD(ISO 8601) - Timestamps:
YYYY-MM-DD HH:MM:SS(ISO 8601) - Decimals: Dot separator (
.), 2 decimal places - Booleans:
true,false(lowercase) - NULL values: Empty field (no quotes)
Example (politician risk summary):
person_id,first_name,last_name,party,status,risk_score,risk_level
0665485817222,Daniel,Bäckström,C,Tjänstgörande riksdagsledamot,50.00,HIGH
0836001490919,Ulrika,Heie,C,Tjänstgörande riksdagsledamot,38.00,MEDIUMFile: /cia-data/production-stats.json
Purpose: Daily statistics from CIA platform
Update: Daily at 03:00 CET via GitHub Actions
Schema Structure:
{
"metadata": {
"source_url": "string (URL)",
"last_updated": "string (ISO 8601 timestamp)",
"extraction_time": "string (ISO 8601 timestamp)",
"generated_at": "string (ISO 8601 timestamp)",
"version": "string (semantic version)"
},
"counts": {
"total_persons": "integer",
"total_votes": "integer",
"total_documents": "integer",
"total_committee_documents": "integer",
"total_rule_violations": "integer",
"total_political_parties": "integer",
"total_governments": "integer",
"total_government_roles": "integer",
"total_government_role_members": "integer"
},
"tables": {
"success": [
{
"name": "string (table name)",
"count": "integer (row count)"
}
],
"empty": ["string (table name)"]
}
}Validation Rules:
metadata.last_updatedmust be within 48 hourscounts.*must be non-negative integerstables.success[].countmust be positivemetadata.versionmust follow semantic versioning
File: /cia-data/data-manifest.json
Purpose: Track all CSV files, checksums, and field descriptions
Update: On data file changes
Schema Structure:
{
"version": "1.0.0",
"last_updated": "2026-02-15",
"files": [
{
"path": "politician/view_politician_risk_summary_sample.csv",
"size_bytes": 69755,
"checksum_sha256": "abc123...",
"record_count": 349,
"fields": [
{
"name": "person_id",
"type": "string",
"description": "Unique person identifier",
"required": true,
"example": "0665485817222"
}
]
}
]
}The CIA-schema lifecycle is owned by four scripts under scripts/, executed in order. All four are pure Node ≥26 TypeScript (no transpile step, native loader):
flowchart LR
A[scripts/sync-cia-schemas.ts<br/>pulls upstream into schemas/cia/]
B[scripts/validate-against-cia-schemas.ts<br/>ajv 8.18.0 — runtime + CI]
C[scripts/check-cia-schema-updates.ts<br/>diff vs. last sync, exit 1 on drift]
D[scripts/generate-types-from-cia-schemas.ts<br/>emits .d.ts for ./cia/* subpath]
A --> B
A --> C
B --> D
C --> D
Workflow: .github/workflows/validate-data.yml (if implemented)
Trigger: On push to cia-data/ directory
Steps:
- Validate JSON files against schemas
- Check CSV headers and data types
- Verify production-stats.json freshness
- Run integrity checks (foreign key validation)
- Generate validation report
Exit Codes:
0: All validations passed1: Schema validation failed2: Data integrity check failed3: Freshness check failed
Location: Dashboard JavaScript files
Method: Papa Parse CSV validation
Timing: On data load
Validation Checks:
- Header row presence
- Required field presence
- Data type validation (dates, numbers)
- Range validation (scores 0-100)
- Enum validation (party codes, risk levels)
Error Handling:
- Invalid data: Skip row, log warning
- Missing file: Fallback to remote URL
- Parse error: Display user-friendly message
Versioning Scheme: Semantic Versioning (MAJOR.MINOR.PATCH)
- MAJOR: Breaking changes (field removal, type change)
- MINOR: Additive changes (new fields, optional)
- PATCH: Documentation updates, bug fixes
Compatibility:
- Dashboards support current MAJOR version
- Backward compatibility for 1 MINOR version
- Deprecation notice: 6 months before MAJOR change
Schema Evolution Process:
- Propose schema change (GitHub issue)
- Update schema file with new version
- Test with sample data
- Update dashboard code (if breaking change)
- Deploy schema, then data
- Deprecation notice for old schema (if applicable)
graph TB
subgraph "External Sources"
Riksdag[Riksdag API<br/>data.riksdagen.se]
Election[Election Authority<br/>val.se]
Finance[Financial Authority<br/>esv.se]
WorldBank[World Bank<br/>data.worldbank.org]
IMF[IMF<br/>data.imf.org / api.imf.org<br/>WEO + SDMX 3.0]
end
subgraph "CIA Platform (Backend)"
ETL[ETL Processes<br/>Spring Batch Jobs]
DB[(PostgreSQL<br/>Production Database)]
Analytics[Analytics Engine<br/>Risk Assessment]
Export[CSV Export<br/>Sample Data]
end
subgraph "Riksdagsmonitor (Frontend)"
GitHub[GitHub Repository<br/>cia-data/]
LocalCache[Browser LocalStorage<br/>1-24 hour TTL]
Dashboard[Interactive Dashboards<br/>Chart.js + D3.js]
end
subgraph "Content Delivery"
CloudFront[AWS CloudFront<br/>Primary CDN]
S3[S3 Storage<br/>Multi-region]
GitHubPages[GitHub Pages<br/>DR Hosting]
end
Riksdag -->|REST API| ETL
Election -->|CSV Download| ETL
Finance -->|CSV Download| ETL
WorldBank -->|REST API| ETL
IMF -->|Datamapper JSON + SDMX 3.0<br/>pure-TS client, no MCP| ETL
ETL --> DB
DB --> Analytics
Analytics --> DB
DB --> Export
Export -->|Daily 03:00 CET| GitHub
GitHub -->|Deploy| CloudFront
GitHub -->|Deploy| GitHubPages
CloudFront -->|HTTPS/TLS 1.3| Dashboard
GitHubPages -.->|DR Failover| Dashboard
Dashboard -->|Load Data| LocalCache
LocalCache -->|Cache Hit| Dashboard
LocalCache -->|Cache Miss| GitHub
style Riksdag fill:#ff9800,color:#000000
style ETL fill:#4caf50,color:#000000
style DB fill:#2196f3,color:#ffffff
style Export fill:#9c27b0,color:#ffffff
style GitHub fill:#ff9800,color:#000000
style CloudFront fill:#4caf50,color:#000000
style Dashboard fill:#00bcd4,color:#000000
Workflow: .github/workflows/update-cia-stats.yml
Schedule: Daily at 03:00 CET (02:00 UTC)
Trigger: cron: '0 2 * * *'
Steps:
- Fetch Production Stats
curl https://raw.githubusercontent.com/Hack23/cia/master/service.data.impl/sample-data/extraction_summary_report.csv
- Parse CSV (Papa Parse)
- Generate JSON (
cia-data/production-stats.json) - Update Website Files (inject stats into HTML)
- Git Commit (automated commit with stats)
- Deploy (push to main → triggers deployment)
Error Handling:
- Network failure: Retry 3 times with exponential backoff
- Parse error: Log error, skip update, alert maintainer
- Stale data: Accept if <48 hours old, alert if older
Workflow: Weekly schema validation check
Trigger: cron: '0 0 * * 0' (Sundays at midnight)
Steps:
- Fetch CIA Schemas (from CIA repo)
- Compare with Local Schemas (
schemas/cia/) - Detect Changes (field additions, type changes)
- Generate Report (Markdown diff)
- Create Issue (if changes detected)
Change Types:
- Additive: New optional fields (auto-accept)
- Deprecation: Fields marked deprecated (6-month notice)
- Breaking: Field removal or type change (manual review)
Location: Browser LocalStorage
Scope: Per-origin (https://riksdagsmonitor.com)
Capacity: ~10MB per origin (browser-dependent)
Cache Key Pattern:
riksdagsmonitor_cache_{data_type}_{language}
Example:
localStorage.setItem('riksdagsmonitor_cache_politician_risk_en', JSON.stringify({
timestamp: Date.now(),
ttl: 3600000, // 1 hour in milliseconds
data: csvData
}));TTL (Time-To-Live):
- Real-time data (votes): 5 minutes
- Daily data (documents): 1 hour
- Weekly data (risk assessments): 24 hours
- Monthly data (historical trends): 7 days
Cache Invalidation:
- Expiration: Automatic when TTL exceeded
- Manual: User refresh action (Ctrl+R)
- Version: On schema version change
CDN: GitHub Pages built-in CDN
Cache-Control Headers: Set by GitHub Pages
Default TTL: 10 minutes
Cacheable Assets:
- HTML pages: 10 minutes
- CSS files: 1 hour
- JavaScript files: 1 hour
- CSV data files: 10 minutes
- Images: 1 day
Cache Busting:
- Method: Git commit SHA in deployment
- Pattern: Files served from main branch HEAD
- Invalidation: Automatic on new deployment
CDN: AWS CloudFront (primary)
Edge Locations: 600+ globally
Default TTL: 86400 seconds (24 hours)
Cache Behaviors:
Path Pattern TTL Cache-Control
/ 3600s public, max-age=3600
*.html 3600s public, max-age=3600
*.css 86400s public, max-age=86400
*.js 86400s public, max-age=86400
/cia-data/*.csv 3600s public, max-age=3600
/cia-data/*.json 3600s public, max-age=3600
Invalidation:
- Manual: AWS CLI invalidation command
- Automatic: On deployment (via GitHub Actions)
- Pattern:
/*(all files) - Cost: First 1,000 invalidations/month free
Implementation: JavaScript function in dashboards
Freshness Criteria:
function isDataFresh(timestamp, ttl) {
const now = Date.now();
const age = now - timestamp;
return age < ttl;
}
const FRESHNESS_TTL = {
realtime: 5 * 60 * 1000, // 5 minutes
daily: 60 * 60 * 1000, // 1 hour
weekly: 24 * 60 * 60 * 1000, // 24 hours
monthly: 7 * 24 * 60 * 60 * 1000 // 7 days
};Fallback Strategy:
- Check LocalStorage (cache)
- If fresh: Use cached data
- If stale: Fetch from GitHub (cia-data/)
- If GitHub fails: Fallback to remote URL
- If all fail: Display error message
graph LR
A[Collection<br/>CIA Platform] --> B[Validation<br/>Schema Checks]
B --> C[Storage<br/>GitHub + S3]
C --> D[Presentation<br/>Dashboards]
B -->|Invalid Data| E[Error Log]
E -->|Alert| F[Maintainer]
C -->|Cache| G[LocalStorage]
G -->|Serve| D
style A fill:#ff9800,color:#000000
style B fill:#4caf50,color:#000000
style C fill:#2196f3,color:#ffffff
style D fill:#9c27b0,color:#ffffff
style E fill:#f44336,color:#ffffff
Pipeline Stages:
-
Collection (CIA Platform)
- Source: Riksdag API, Election Authority, etc.
- Frequency: Real-time to daily
- Output: PostgreSQL database
-
Validation (CIA Platform + GitHub Actions)
- Schema validation (JSON Schema)
- Data integrity checks (foreign keys)
- Completeness checks (required fields)
- Output: Validated CSV files
-
Storage (GitHub + AWS)
- Primary: GitHub repository (
cia-data/) - Secondary: AWS S3 multi-region
- Backup: Git history (immutable)
- Primary: GitHub repository (
-
Presentation (Riksdagsmonitor)
- Load: LocalStorage cache → GitHub → Remote
- Parse: Papa Parse CSV parser
- Render: Chart.js + D3.js visualizations
Riksdagsmonitor supports 14 languages with full data model localization:
| Code | Language | Native Name | Script | RTL | Status |
|---|---|---|---|---|---|
| en | English | English | Latin | No | ✅ Active |
| sv | Swedish | Svenska | Latin | No | ✅ Active |
| da | Danish | Dansk | Latin | No | ✅ Active |
| no | Norwegian | Norsk | Latin | No | ✅ Active |
| fi | Finnish | Suomi | Latin | No | ✅ Active |
| de | German | Deutsch | Latin | No | ✅ Active |
| fr | French | Français | Latin | No | ✅ Active |
| es | Spanish | Español | Latin | No | ✅ Active |
| nl | Dutch | Nederlands | Latin | No | ✅ Active |
| ar | Arabic | العربية | Arabic | Yes | ✅ Active |
| he | Hebrew | עברית | Hebrew | Yes | ✅ Active |
| ja | Japanese | 日本語 | Japanese | No | ✅ Active |
| ko | Korean | 한국어 | Hangul | No | ✅ Active |
| zh | Chinese | 中文 | Chinese | No | ✅ Active |
Politicians (person_data):
- Names: Original Swedish (no translation)
- Party: Translated party abbreviation context
- Status: Translated status values
Parties (sweden_political_party):
{
"party_id": "S",
"translations": {
"en": "Social Democrats",
"sv": "Socialdemokraterna",
"de": "Sozialdemokraten",
"fr": "Sociaux-démocrates",
"es": "Socialdemócratas",
"ar": "الديمقراطيون الاجتماعيون",
"ja": "社会民主党",
"zh": "社会民主党"
}
}Committees (committee_document_data):
{
"committee_id": "AU",
"translations": {
"en": "Labour Market Committee",
"sv": "Arbetsmarknadsutskottet",
"de": "Ausschuss für Arbeitsmarkt",
"fr": "Commission du marché du travail",
"es": "Comisión del Mercado Laboral",
"ar": "لجنة سوق العمل",
"ja": "労働市場委員会",
"zh": "劳动市场委员会"
}
}Document Types:
{
"document_type": "mot",
"translations": {
"en": "Motion (MP-initiated proposal)",
"sv": "Motion",
"de": "Antrag",
"fr": "Motion",
"es": "Moción",
"ar": "اقتراح",
"ja": "動議",
"zh": "动议"
}
}Dashboard Titles:
- Stored in HTML
<title>tags per language - Pattern:
index_{lang}.html
Chart Labels:
- Translated in JavaScript dashboard code
- Language detection:
document.documentElement.lang
Data Classification Values:
{
"risk_level": {
"LOW": {
"en": "Low Risk",
"sv": "Låg risk",
"de": "Geringes Risiko",
"ar": "مخاطر منخفضة"
},
"MEDIUM": {
"en": "Medium Risk",
"sv": "Medelhög risk",
"de": "Mittleres Risiko",
"ar": "مخاطر متوسطة"
},
"HIGH": {
"en": "High Risk",
"sv": "Hög risk",
"de": "Hohes Risiko",
"ar": "مخاطر عالية"
}
}
}Affected Languages: Arabic (ar), Hebrew (he)
HTML Structure:
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Riksdagsmonitor - مراقب البرلمان السويدي</title>
</head>
<body>
<!-- Content flows right-to-left -->
</body>
</html>CSS Adaptations:
[dir="rtl"] .dashboard {
text-align: right;
direction: rtl;
}
[dir="rtl"] .chart-legend {
float: left; /* Reversed from LTR */
}Data Implications:
- Text fields: Unicode support required
- Sorting: Locale-aware sorting
- Rendering: Browser handles RTL layout
Pattern: index_{lang}.html
Files (14 languages):
index.html (English - default)
index_sv.html (Swedish)
index_da.html (Danish)
index_no.html (Norwegian)
index_fi.html (Finnish)
index_de.html (German)
index_fr.html (French)
index_es.html (Spanish)
index_nl.html (Dutch)
index_ar.html (Arabic - RTL)
index_he.html (Hebrew - RTL)
index_ja.html (Japanese)
index_ko.html (Korean)
index_zh.html (Chinese)
Sitemap Files:
sitemap.xml (Master sitemap)
sitemap.html (English - default)
sitemap_sv.html (Swedish)
sitemap_ar.html (Arabic)
...
Purpose: Signal alternate language versions to search engines
Implementation:
<head>
<link rel="alternate" hreflang="en" href="https://riksdagsmonitor.com/index.html" />
<link rel="alternate" hreflang="sv" href="https://riksdagsmonitor.com/index_sv.html" />
<link rel="alternate" hreflang="da" href="https://riksdagsmonitor.com/index_da.html" />
<link rel="alternate" hreflang="no" href="https://riksdagsmonitor.com/index_no.html" />
<link rel="alternate" hreflang="fi" href="https://riksdagsmonitor.com/index_fi.html" />
<link rel="alternate" hreflang="de" href="https://riksdagsmonitor.com/index_de.html" />
<link rel="alternate" hreflang="fr" href="https://riksdagsmonitor.com/index_fr.html" />
<link rel="alternate" hreflang="es" href="https://riksdagsmonitor.com/index_es.html" />
<link rel="alternate" hreflang="nl" href="https://riksdagsmonitor.com/index_nl.html" />
<link rel="alternate" hreflang="ar" href="https://riksdagsmonitor.com/index_ar.html" />
<link rel="alternate" hreflang="he" href="https://riksdagsmonitor.com/index_he.html" />
<link rel="alternate" hreflang="ja" href="https://riksdagsmonitor.com/index_ja.html" />
<link rel="alternate" hreflang="ko" href="https://riksdagsmonitor.com/index_ko.html" />
<link rel="alternate" hreflang="zh" href="https://riksdagsmonitor.com/index_zh.html" />
<link rel="alternate" hreflang="x-default" href="https://riksdagsmonitor.com/index.html" />
</head>Benefits:
- Improved SEO for international audiences
- Correct language version served by search engines
- Reduced duplicate content penalties
Method: Manual language selection (no auto-detect)
Navigation:
- Language picker in website header
- Flags or language names as buttons
- Preserves dashboard state on language change
URL Pattern:
https://riksdagsmonitor.com/ → English (default)
https://riksdagsmonitor.com/index_sv.html → Swedish
https://riksdagsmonitor.com/index_ar.html → Arabic
Data Loading:
- Same CSV data files for all languages
- Translation layer in JavaScript dashboard code
- Locale-specific number/date formatting
Strategy: Client-side caching with TTL-based expiration
Implementation:
class DataCache {
constructor(ttl = 3600000) { // 1 hour default
this.ttl = ttl;
}
set(key, data) {
const item = {
data: data,
timestamp: Date.now(),
ttl: this.ttl
};
localStorage.setItem(key, JSON.stringify(item));
}
get(key) {
const item = JSON.parse(localStorage.getItem(key));
if (!item) return null;
const age = Date.now() - item.timestamp;
if (age > item.ttl) {
localStorage.removeItem(key);
return null;
}
return item.data;
}
}Cache Keys:
riksdagsmonitor_cache_politician_risk_en
riksdagsmonitor_cache_party_performance_sv
riksdagsmonitor_cache_seasonal_patterns_de
riksdagsmonitor_cache_election_cycle_fr
TTL Configuration:
- Real-time: 5 minutes (votes, live updates)
- Daily: 1 hour (documents, statistics)
- Weekly: 24 hours (risk assessments, trends)
- Historical: 7 days (longitudinal analysis)
GitHub Pages CDN: Built-in global CDN
Cache-Control Headers: Managed by GitHub
Effective Caching:
Cache-Control: max-age=600 (10 minutes)
Benefits:
- Global edge caching
- Reduced origin requests
- Faster load times (50-200ms typical)
Limitations:
- No custom cache headers
- No manual invalidation
- 10-minute minimum TTL
Freshness Criteria:
const FRESHNESS_POLICY = {
production_stats: {
ttl: 24 * 60 * 60 * 1000, // 24 hours
acceptable_age: 48 * 60 * 60 * 1000 // 48 hours max
},
politician_risk: {
ttl: 60 * 60 * 1000, // 1 hour
acceptable_age: 24 * 60 * 60 * 1000 // 24 hours max
},
seasonal_patterns: {
ttl: 7 * 24 * 60 * 60 * 1000, // 7 days
acceptable_age: 30 * 24 * 60 * 60 * 1000 // 30 days max
}
};Stale Data Handling:
- Display age indicator: "Data updated 2 hours ago"
- Warning for old data: "Data may be outdated (>24 hours old)"
- Error for very old data: "Data stale (>48 hours), refresh needed"
Implementation: Load data on-demand per dashboard
Strategy:
// Load only when dashboard becomes visible
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadDashboardData(entry.target.dataset.dashboard);
}
});
});
document.querySelectorAll('.dashboard').forEach(el => {
observer.observe(el);
});Benefits:
- Reduced initial page load time
- Lower bandwidth consumption
- Better user experience (faster FCP)
Build System: Vite 7 with ES modules
Split Points:
js/election-cycle-dashboard.js (46KB)
js/party-dashboard.js (TBD)
js/seasonal-patterns-dashboard.js (TBD)
scripts/committees-dashboard.js (39KB)
scripts/coalition-dashboard.js (33KB)
Loading Strategy:
// Dynamic import per dashboard
if (document.getElementById('election-cycle-dashboard')) {
import('./js/election-cycle-dashboard.js')
.then(module => module.init())
.catch(err => console.error('Failed to load dashboard', err));
}Benefits:
- Smaller initial bundle size
- Faster time to interactive (TTI)
- Better Core Web Vitals scores
| Metric | Target | Current | Dashboard |
|---|---|---|---|
| First Contentful Paint (FCP) | <1.5s | <1s | ✅ Passing |
| Largest Contentful Paint (LCP) | <2.5s | <2s | ✅ Passing |
| Time to Interactive (TTI) | <3s | <2s | ✅ Passing |
| Cumulative Layout Shift (CLS) | <0.1 | <0.05 | ✅ Passing |
| Total Blocking Time (TBT) | <200ms | <150ms | ✅ Passing |
Measurement Tools:
- Lighthouse CI (automated)
- WebPageTest (manual)
- Chrome DevTools Performance panel
File: /budget.json
Resource Size Budgets:
{
"document": "105KB", // HTML pages
"stylesheet": "370KB", // CSS files
"script": "300KB", // JavaScript bundles
"image": "500KB", // Images
"font": "100KB", // Web fonts
"total": "1200KB" // Total page weight
}Performance Budgets:
{
"first-contentful-paint": "5100ms",
"largest-contentful-paint": "5400ms",
"interactive": "5400ms",
"cumulative-layout-shift": "0.1",
"total-blocking-time": "200ms"
}Enforcement: GitHub Actions workflow fails on budget violations
graph TB
subgraph "External Data Sources"
Riksdag[Swedish Riksdag API<br/>data.riksdagen.se<br/>2.5M votes, 109K docs]
Election[Election Authority<br/>val.se<br/>Electoral results]
Finance[Financial Authority<br/>esv.se<br/>Budget data]
WorldBank[World Bank<br/>data.worldbank.org<br/>Country indicators]
IMF[IMF<br/>data.imf.org / api.imf.org<br/>WEO + Fiscal Monitor + IFS<br/>macro/fiscal + T+5 projections]
end
subgraph "Riksdagsmonitor System"
System[Data Model<br/>2,494 politicians<br/>40 parties<br/>15 committees]
end
subgraph "CIA Platform (External)"
CIA[CIA Backend<br/>Data aggregation<br/>Risk assessment]
end
subgraph "Users"
Analyst[Political Analysts]
Journalist[Journalists]
Citizen[Citizens]
Researcher[Researchers]
end
Riksdag -->|REST API| CIA
Election -->|CSV Data| CIA
Finance -->|CSV Data| CIA
WorldBank -->|REST API| CIA
IMF -->|Pure-TS client<br/>Datamapper JSON + SDMX 3.0<br/>no MCP| CIA
CIA -->|CSV Exports| System
CIA -->|Daily Statistics| System
System -->|Visualizations| Analyst
System -->|Visualizations| Journalist
System -->|Visualizations| Citizen
System -->|Visualizations| Researcher
style Riksdag fill:#ff9800,color:#000000
style Election fill:#ff9800,color:#000000
style Finance fill:#ff9800,color:#000000
style WorldBank fill:#ff9800,color:#000000
style IMF fill:#00897b,color:#ffffff
style CIA fill:#9c27b0,color:#ffffff
style System fill:#4caf50,color:#000000
style Analyst fill:#2196f3,color:#ffffff
graph TB
subgraph "Data Storage Layer"
GitHub[GitHub Repository<br/>cia-data/<br/>50+ CSV files]
S3[AWS S3<br/>Multi-region<br/>Primary storage]
LocalStorage[Browser LocalStorage<br/>Client-side cache<br/>~10MB capacity]
end
subgraph "Data Processing Layer"
CIA_DB[(CIA Platform<br/>PostgreSQL Database<br/>Production data)]
Export[CSV Export Engine<br/>Sample data generator]
Stats[Statistics Generator<br/>production-stats.json]
end
subgraph "Validation Layer"
Schemas[JSON Schemas<br/>2 schema files]
Validator[Schema Validator<br/>GitHub Actions]
end
subgraph "Presentation Layer"
Dashboards[Interactive Dashboards<br/>Chart.js + D3.js<br/>5 active dashboards]
end
CIA_DB --> Export
CIA_DB --> Stats
Export --> GitHub
Stats --> GitHub
GitHub --> Validator
Schemas --> Validator
Validator -->|Valid| S3
S3 --> Dashboards
GitHub --> Dashboards
Dashboards --> LocalStorage
LocalStorage --> Dashboards
style CIA_DB fill:#2196f3,color:#ffffff
style GitHub fill:#ff9800,color:#000000
style S3 fill:#4caf50,color:#000000
style Dashboards fill:#9c27b0,color:#ffffff
style Validator fill:#f44336,color:#ffffff
This DATA_MODEL.md complements ARCHITECTURE.md:
ARCHITECTURE.md Focus:
- System architecture (infrastructure, deployment)
- Component interactions (CDN, hosting, CI/CD)
- Security architecture (HTTPS, CSP, access control)
DATA_MODEL.md Focus (this document):
- Data entities and relationships
- Data schemas and validation
- Data pipelines and caching
- Multi-language data structure
Cross-References:
- ARCHITECTURE.md Section 5 → DATA_MODEL.md Section 4 (Data Sources)
- ARCHITECTURE.md Section 3.1 → DATA_MODEL.md Section 3 (ERD Integration)
- ARCHITECTURE.md Section 8 → DATA_MODEL.md Section 8 (Performance)
Annex A.8 - Asset Management
| Control | Implementation | Evidence |
|---|---|---|
| A.8.1 | Asset inventory | cia-data/data-manifest.json |
| A.8.2 | Information classification | Section 1 (Public data classification) |
| A.8.3 | Media handling | Git version control, S3 versioning |
Compliance Level: ✅ FULLY COMPLIANT
PR.DS - Data Security
| Function | Implementation | Location |
|---|---|---|
| PR.DS-1 | Data-at-rest protection | S3 encryption, Git history |
| PR.DS-2 | Data-in-transit protection | HTTPS/TLS 1.3 |
| PR.DS-3 | Asset management | Section 4 (Data Sources) |
| PR.DS-5 | Protection against leakage | No confidential data, all public |
| PR.DS-6 | Integrity checking | JSON Schema validation, checksums |
Compliance Level: ✅ FULLY COMPLIANT
Control 3 - Data Protection
| Subcontrol | Implementation | Status |
|---|---|---|
| 3.1 | Data inventory | Section 1 (Entity dictionary) |
| 3.2 | Data classification | Section 1 (Public classification) |
| 3.3 | Data retention | Git history, S3 versioning |
| 3.6 | Data encryption | HTTPS/TLS 1.3 |
| 3.12 | Data integrity | JSON Schema validation |
Compliance Level: ✅ FULLY COMPLIANT
Personal Data Processing:
Data Categories:
- Public Officials: MP names, roles, voting records
- Legal Basis: Article 6(1)(e) - Public interest
- Scope: Public figures acting in official capacity only
Data Subject Rights:
- Right to Access: All data publicly accessible
- Right to Rectification: Source data from official government APIs
- Right to Erasure: Not applicable (public interest exception)
- Right to Object: Not applicable (public interest exception)
Privacy-by-Design:
- No special category data (Article 9)
- No data about private individuals
- No user tracking or analytics
- No cookies or personal identifiers
Compliance Level: ✅ FULLY COMPLIANT (Public Interest Processing)
| Control | Implementation | Status |
|---|---|---|
| Access Control | Public data, no restrictions | ✅ |
| Encryption (Transit) | HTTPS/TLS 1.3 | ✅ |
| Encryption (Rest) | S3 server-side encryption | ✅ |
| Integrity | JSON Schema validation, Git history | ✅ |
| Availability | Multi-region S3, GitHub Pages DR | ✅ |
| Backup | Git version control, S3 versioning | ✅ |
| Audit Trail | Git commit history, S3 access logs | ✅ |
The newsroom's analytic input layer is a typed ArtifactDefinition[] owned by scripts/agentic/artifact-inventory.ts. The analysis-gate (scripts/agentic/analysis-gate.ts, checks 1–9b, PASS2_MTIME_THRESHOLD_MS = 180_000) admits or rejects an article-day folder before any rendering. Folder convention:
analysis/daily/$ARTICLE_DATE/$SUBFOLDER/
README.md
executive-brief.md
synthesis-summary.md
significance-scoring.md
classification-results.md
swot-analysis.md
risk-assessment.md
threat-analysis.md
stakeholder-perspectives.md
data-download-manifest.md
cross-reference-map.md
scenario-analysis.md
comparative-international.md
devils-advocate.md
intelligence-assessment.md
methodology-reflection.md
election-2026-analysis.md
voter-segmentation.md
coalition-mathematics.md
historical-parallels.md
media-framing-analysis.md
implementation-feasibility.md
forward-indicators.md
documents/{dok_id}-analysis.md # Family E, one per source document (variable count)
| # | Filename | requiresMermaid | requiresPass2 | Description |
|---|---|---|---|---|
| 1 | README.md |
❌ | ✅ | Folder README with index links |
| 2 | executive-brief.md |
✅ | ✅ | BLUF + 3 decisions supported |
| 3 | synthesis-summary.md |
✅ | ✅ | Lead-story decision + DIW ranking |
| 4 | significance-scoring.md |
✅ | ✅ | DIW scores + sensitivity analysis |
| 5 | classification-results.md |
✅ | ✅ | 7-dimension classification |
| 6 | swot-analysis.md |
✅ | ✅ | S/W/O/T with evidence + TOWS matrix |
| 7 | risk-assessment.md |
✅ | ✅ | Risk matrix + mitigation strategies |
| 8 | threat-analysis.md |
✅ | ✅ | STRIDE-style threat assessment |
| 9 | stakeholder-perspectives.md |
✅ | ✅ | Stakeholder mapping + positions |
| # | Filename | requiresMermaid | requiresPass2 | Description |
|---|---|---|---|---|
| 1 | data-download-manifest.md |
❌ | ❌ | Download manifest with dok_ids |
| 2 | cross-reference-map.md |
✅ | ✅ | Cross-reference map |
| # | Filename | requiresMermaid | requiresPass2 | Description |
|---|---|---|---|---|
| 1 | scenario-analysis.md |
❌ | ✅ | ≥3 distinct scenarios |
| 2 | comparative-international.md |
❌ | ✅ | Comparator set + rows |
| 3 | devils-advocate.md |
❌ | ✅ | ≥3 competing hypotheses (ACH) |
| 4 | intelligence-assessment.md |
❌ | ✅ | ≥3 Key Judgments with confidence |
| 5 | methodology-reflection.md |
❌ | ✅ | ICD 203 audit / improvements |
| # | Filename | requiresMermaid | requiresPass2 | Description |
|---|---|---|---|---|
| 1 | election-2026-analysis.md |
✅ | ✅ | Election 2026 analysis |
| 2 | voter-segmentation.md |
✅ | ✅ | Voter segmentation analysis |
| 3 | coalition-mathematics.md |
✅ | ✅ | Seat-count / vote-breakdown table |
| 4 | historical-parallels.md |
✅ | ✅ | Historical parallels analysis |
| 5 | media-framing-analysis.md |
✅ | ✅ | Media framing analysis |
| 6 | implementation-feasibility.md |
✅ | ✅ | Implementation feasibility |
| 7 | forward-indicators.md |
✅ | ✅ | ≥10 dated forward indicators |
| Filename pattern | requiresMermaid | requiresPass2 | Description |
|---|---|---|---|
documents/{dok_id}-analysis.md |
❌ (per source) | ✅ | One file per source document, named after the Riksdag/Regeringen dok_id |
methodology-reflection.md is the only Family-C artifact with both a markdown body and a machine-readable JSON contract block (validated by scripts/validate-methodology-reflection.ts):
{
"icd203": {
"describesQualityOfInformation": "string",
"expressesUncertainties": "string",
"distinguishesUnderlyingFromAssumptions": "string",
"incorporatesAlternatives": "string",
"demonstratesRelevance": "string",
"usesLogicalArgumentation": "string",
"exhibitsConsistency": "string",
"accuratelyDescribesPriorAnalysis": "string"
},
"improvements": [
{ "area": "string", "issue": "string", "fix": "string" }
],
"selfScore": { "icd203": "0..1", "completeness": "0..1" }
}The eight icd203.* fields correspond 1-to-1 with ICD 203 analytic standards. Missing or empty fields cause analysis-gate check 9b to fail.
Every requiresPass2: true artifact MUST exhibit mtimeMs ≥ birthtimeMs + 180_000 (≥ 180 s wall-clock between creation and last modification). The threshold lives in scripts/agentic/analysis-gate.ts as PASS2_MTIME_THRESHOLD_MS = 180_000. Operational interpretation: AI-FIRST 2-pass authoring — Pass 1 drafts the artifact, Pass 2 (≥3 minutes later) re-reads and tightens every section. Same-minute writes are a structural failure, not a stylistic preference.
The published news corpus lives under news/ and currently materialises 3,953 .html files (verified find news -type f -name "*.html" | wc -l on 2026-05-06). Each article has 14 language siblings (or fewer if news-translate has not yet completed) plus a canonical article.md upstream of HTML rendering.
news/{ARTICLE_DATE}-{SUBFOLDER}-{LANG}.html
Where {LANG} ∈ en, sv, da, no (= NB), fi, de, es, fr, nl, ar (RTL), he (RTL), ja, ko, zh. EN and SV are produced directly by the agentic newsroom; the remaining 12 are produced out-of-band by the news-translate workflow.
Defaults are emitted by scripts/render-lib/aggregator/frontmatter.ts (language ?? 'en', layout ?? 'article').
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
title |
string | ✅ | — | Article title (sanitised, ≤120 chars) |
language |
string (BCP-47-ish, lower-case) | ❌ | 'en' |
One of the 14 supported codes |
layout |
string | ❌ | 'article' |
Render-lib layout selector |
date |
string (YYYY-MM-DD) |
✅ | — | Article date (matches $ARTICLE_DATE) |
subfolder |
string | ✅ | — | Matches $SUBFOLDER under analysis/daily/$ARTICLE_DATE/ |
articleType |
string | ✅ | — | One of the registered article-types in analysis/article-types.json |
horizon |
string | ✅ | — | One of T+72h, T+7d, T+30d, T+90d, T+365d, T+1460d, election |
economicProvenance[] |
array of economicProvenance blocks |
❌ | [] |
See §4 — required for any economic data point used |
scripts/render-lib/aggregator/cleaning/ strips before HTML rendering:
| Cleaning step | Module | Purpose |
|---|---|---|
admin-bylines/ |
n/a | Removes "Author: agentic-newsroom-bot" admin lines |
deduplication/ |
n/a | Collapses duplicate H1s and repeated section headers |
heading-demotion/ |
n/a | Demotes unintended H1s in concatenated artifacts to H2 |
link-rewriting/ |
n/a | Rewrites relative links to https://github.com/Hack23/riksdagsmonitor/blob/main/… |
pass-two/ |
n/a | Strips ## Pass 2 … self-audit sections (analysis-gate keeps them; rendered article does not) |
process-meta/ |
n/a | Strips YAML/HTML metadata that should not surface in body |
structural/ |
n/a | Final structural sweep (whitespace, ordering) |
scripts/render-lib/aggregator/seo/ produces description.ts and title.ts — the canonical OpenGraph + JSON-LD NewsArticle headline / description fields used by every rendered article.
The political-intelligence catalog is the single source of truth for which articles, streams, and methodology references render on political-intelligence.html and its 13 language siblings. It is produced by the scripts/political-intelligence/ bounded context.
scripts/political-intelligence/
catalog.ts # Builds the master catalog JSON (articles × streams × horizons)
daily-streams.ts # Per-stream per-day groupings (input to grid renderer)
index.ts # Barrel
i18n/
artifact-i18n.ts # 14-language strings for artifact names
methodology-i18n.ts # 14-language strings for methodology names
page-translations.ts # Page-chrome translations
stream-i18n.ts # 14-language stream display names
template-i18n.ts # 14-language template names
render/
daily-day.ts # Per-day card renderer
grid.ts # Stream × horizon grid renderer
page.ts # Top-level page composer
style.ts # CSS leaf
interface PICatalogEntry {
readonly date: string; // YYYY-MM-DD
readonly subfolder: string; // matches $SUBFOLDER under analysis/daily/$DATE/
readonly articleType: string; // registered in analysis/article-types.json
readonly horizon:
| 'T+72h' | 'T+7d' | 'T+30d' | 'T+90d'
| 'T+365d' | 'T+1460d' | 'election';
readonly stream: string; // catalog stream key (resolved via stream-i18n)
readonly languages: readonly string[]; // codes of available rendered HTML siblings
readonly artifactsPresent: readonly string[]; // subset of the 23-artifact inventory
readonly methodologiesUsed: readonly string[]; // subset of the 18-methodology set
readonly templatesUsed: readonly string[]; // subset of the 39-template set
}daily-streams.ts groups PICatalogEntry[] first by date, then by stream, producing the grid that the renderer uses. Streams currently in production: daily, weekly-review, monthly-review, quarterly-outlook, annual-outlook, election-cycle, mandate-period. Each stream maps deterministically to one or more horizon bands (see ARCHITECTURE.md §"Horizon stratification").
All five i18n/*.ts leaves export typed string maps keyed by the 14 supported language codes. The catalog payload is language-agnostic; localisation happens at render time inside scripts/political-intelligence/render/page.ts.
- ARCHITECTURE.md - System architecture and infrastructure
- README.md - Project overview and features
- SECURITY_ARCHITECTURE.md - Security controls
- THREAT_MODEL.md - STRIDE threat analysis
- TRANSLATION_GUIDE.md - Multi-language standards
- Hack23 ISMS - Information Security Management System
- Hack23 Public ISMS - Public security policies
- Secure Development Policy
- Classification Framework
- 🏛️ Architecture - C4 system architecture models
- 🔄 Flowchart - Data pipelines and workflow processes
- 📈 State Diagram - System state transitions
- 💼 SWOT - Strategic analysis and positioning
- 🎯 Threat Model - Security threat analysis
- 🔐 Security Architecture - Current security controls
- 🚀 Future Data Model - Future data architecture roadmap
- 🛠️ Secure Development Policy - Hack23 SDLC requirements
- 🏷️ Classification Framework - Data classification standards
- CIA Platform - Citizen Intelligence Agency backend
- CIA Data Specs - Export specifications
- Swedish Riksdag API - Official parliament API
- JSON Schema - Schema validation standard
📋 Document Control:
✅ Approved by: James Pether Sörling, CEO
📤 Distribution: Public
🏷️ Classification:
📅 Effective Date: 2026-05-06
⏰ Next Review: 2027-05-06
🎯 Framework Compliance:
Statskontoret adds a public Swedish-administration data domain under the economic/public-administration context layer.
| Entity | Key fields | Storage / source |
|---|---|---|
StatskontoretSourceDefinition |
key, title, url, cadence, coverage, primaryUse |
Static catalogue in scripts/statskontoret-client.ts; mirrored by analysis/statskontoret/indicators-inventory.json. |
StatskontoretDownloadLink |
source, sourcePage, url, resourceType, documentType, fileType, fileName, year, month, status, updatedAt |
Derived from Statskontoret HTML pages by extractStatskontoretDownloadLinks. |
StatskontoretWorkbook / StatskontoretSheet |
sheet name and row arrays | Parsed locally from XLSX ZIP parts. |
StatskontoretHeadcountRow |
year, department, headcount, authorityCount |
Derived from Myndighetsförteckning rows. |
analysis/data/statskontoret/{dataset}/{artifact}.json
analysis/data/statskontoret/{dataset}/{artifact}.meta.json
Sidecar metadata includes fetchedAt, mcpTool: statskontoret-ts-client, dataset, and artifact. The provider decision matrix in analysis/statskontoret/indicators-inventory.json maps government-body headcount and central-government budget outturn claims to Statskontoret, while macro/fiscal projections remain IMF-first.
| 🌐 Platforms | 📦 Open-Source Projects | 🛡️ Governance & Standards |
|---|---|---|
|
🗳️ Riksdagsmonitor — Swedish Parliament intelligence 🇪🇺 EU Parliament Monitor — European coverage 🕵️ Citizen Intelligence Agency — political-data engine 🌐 Hack23 AB — corporate site 📰 Hack23 Blog — engineering & policy 💼 Hack23 on LinkedIn |
🗳️ Hack23/riksdagsmonitor 🕵️ Hack23/cia 🇪🇺 Hack23/euparliamentmonitor 🔌 Hack23/european-parliament-mcp ✅ Hack23/cia-compliance-manager 🥋 Hack23/black-trigram 🏠 Hack23/homepage |
🛡️ Hack23 ISMS-PUBLIC — public ISMS 🔒 Information Security Policy 🤖 AI Policy 🧪 Secure Development Policy 🎯 Threat Modeling Policy 🏷️ Classification Framework |
🗳️ Empower citizens · 🔍 Strengthen democratic accountability · 🕵️ Illuminate the political process
© 2008–2026 Hack23 AB (Org.nr 559534-7807) · Maintainer: James Pether Sörling, CISSP CISM