Agent skill

Electoral Analysis

by Hack23 in Hack23/cia

Election forecasting models, campaign analysis, coalition prediction, voter behavior analysis for Swedish elections

Apache-2.0Auto-check passedData & Analytics

Install Electoral Analysis

skills CLI
$ npx skills add Hack23/cia --skill electoral-analysis -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install Hack23/cia electoral-analysis --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/Hack23/cia.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/electoral-analysis .claude/skills/electoral-analysis && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
electoral-analysis
GitHub stars
239
Token cost
~6.5k tokens
SKILL.md length
456 words
Files
1
Skills in repo
78
Repo updated
First seen
Licence
Apache-2.0

At a glance

Election forecasting models, campaign analysis, coalition prediction, voter behavior analysis for Swedish elections

  • Works in 5 steps: Election Forecasting Models → Coalition Formation Prediction → Swing Voter Analysis → …
  • Tasks that involve Forecasting and time series
  • SKILL.md covers Purpose, When to Use This Skill, Swedish Electoral System Context and 1. Election Forecasting Models, plus 7 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Electoral Analysis is an agent skill from Hack23/cia. Election forecasting models, campaign analysis, coalition prediction, voter behavior analysis for Swedish elections

Its SKILL.md is about 6.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Data & Analytics, covering Forecasting and time series. The repository describes itself as: Citizen Intelligence Agency. Open-source intelligence platform analyzing Swedish political activities using AI and data visualization. Tracks politicians, government… The licence is Apache-2.0.

When your agent uses it

  • Tasks that involve Forecasting and time series

Example prompts

  • “/electoral-analysis”

Requirements

  • Python 3

Workflow steps

5 steps, taken from the step headings in SKILL.md.

  1. Election Forecasting Models
  2. Coalition Formation Prediction
  3. Swing Voter Analysis
  4. Campaign Effectiveness Analysis
  5. Threshold Watch (4% Electoral Threshold)

What it can do on your machine

Read from SKILL.md and the folder at commit bbed538. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are python, sql and mermaid).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • github.com
    • val.se
    • riksdagen.se
    • novus.se
    • kantarsifo.se
    • yougov.se
    • demoskop.se

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Electoral Analysis loads about 6.5k tokens when it runs. Until then it costs about 34 tokens; SKILL.md has 456 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~34
When it runs · the whole SKILL.md, loaded when a task matches
~6.5k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from Hack23/cia at commit bbed538, republished under its Apache-2.0 licence (© Hack23). 456 words, ~6,492 tokens.

Download SKILL.mdSave it as .claude/skills/electoral-analysis/SKILL.md (or your agent's skills folder).
name
electoral-analysis
description
Election forecasting models, campaign analysis, coalition prediction, voter behavior analysis for Swedish elections
license
Apache-2.0

Electoral Analysis Skill

Purpose

This skill provides comprehensive methodologies for analyzing Swedish electoral dynamics, forecasting election outcomes, predicting coalition formations, and assessing campaign effectiveness. It integrates statistical modeling, polling analysis, and historical trend analysis to produce high-confidence intelligence products for democratic accountability assessment.

When to Use This Skill

Apply this skill when:

  • ✅ Forecasting election outcomes (seat projections, vote shares)
  • ✅ Analyzing polling trends and calculating poll aggregates
  • ✅ Predicting coalition formation post-election
  • ✅ Assessing swing voter behavior and electoral volatility
  • ✅ Evaluating campaign effectiveness and messaging impact
  • ✅ Calculating electoral system effects (proportional representation, thresholds)
  • ✅ Identifying marginal constituencies and competitive races

Do NOT use for:

  • ❌ Individual voter predictions (violates privacy, no granular data)
  • ❌ Local/municipal elections (different dynamics, separate models)
  • ❌ EU Parliament elections (different party configurations)

Swedish Electoral System Context

Key Electoral Characteristics
mermaid
graph TB
    subgraph "Electoral Framework"
        A[349 Riksdag Seats]
        A --> B[310 Constituency Seats<br/>29 constituencies]
        A --> C[39 Leveling Seats<br/>National proportionality]
    end
    
    subgraph "Allocation Rules"
        D[Modified Sainte-Laguë]
        E[4% National Threshold]
        F[12% Constituency Threshold]
        
        D --> G[Seat Distribution]
        E --> G
        F --> G
    end
    
    subgraph "Electoral Cycle"
        H[4-Year Fixed Term]
        I[September Elections]
        J[Sunday Voting]
        
        H & I & J --> K[Election Day 2026<br/>September 13]
    end
    
    subgraph "Forecasting Inputs"
        L[Historical Results<br/>1970-2022]
        M[Opinion Polls<br/>Monthly tracking]
        N[Demographic Shifts]
        O[Campaign Events]
        
        L & M & N & O --> P[Election Model]
    end
    
    P --> Q[Seat Projections]
    P --> R[Coalition Scenarios]
    
    style A fill:#e1f5ff
    style D fill:#ffeb99
    style P fill:#ffe6cc
    style Q fill:#ccffcc
    style R fill:#ccffcc

1. Election Forecasting Models

Polling Aggregation & Trend Estimation

Purpose: Combine multiple polls to estimate current vote intention with confidence intervals.

python
import pandas as pd
import numpy as np
from scipy import stats
from datetime import datetime, timedelta

class SwedishElectionForecaster:
    """
    Electoral forecasting for Swedish Riksdag elections
    Supports: Predictive Intelligence Framework
    """
    
    def __init__(self, db_connection):
        self.db = db_connection
        self.parties = ['S', 'M', 'SD', 'C', 'V', 'KD', 'L', 'MP']
        self.threshold = 4.0  # Electoral threshold
        
    def aggregate_polls_weighted(self, lookback_days=90):
        """
        Weighted poll aggregation using recency and sample size
        
        Data Source: External polling data (Novus, Sifo, YouGov, Demoskop)
        Intelligence Product: Current vote intention estimates
        """
        
        # Query: Fetch recent polls
        query = f"""
        SELECT 
            poll_date,
            polling_company,
            sample_size,
            party,
            percentage
        FROM opinion_polls
        WHERE poll_date >= CURRENT_DATE - INTERVAL '{lookback_days} days'
        ORDER BY poll_date DESC
        """
        
        df = pd.read_sql(query, self.db)
        
        # Calculate weights
        df['days_ago'] = (pd.Timestamp.now() - pd.to_datetime(df['poll_date'])).dt.days
        df['recency_weight'] = np.exp(-df['days_ago'] / 30)  # Exponential decay, half-life 30 days
        df['sample_weight'] = np.sqrt(df['sample_size']) / 1000  # Sample size adjustment
        df['total_weight'] = df['recency_weight'] * df['sample_weight']
        
        # Weighted average by party
        aggregated = df.groupby('party').apply(
            lambda x: np.average(x['percentage'], weights=x['total_weight'])
        ).to_dict()
        
        # Calculate standard errors
        standard_errors = df.groupby('party').apply(
            lambda x: np.sqrt(np.average((x['percentage'] - aggregated[x.name])**2, weights=x['total_weight']))
        ).to_dict()
        
        # 95% confidence intervals
        confidence_intervals = {
            party: {
                'estimate': aggregated[party],
                'lower_95': aggregated[party] - 1.96 * standard_errors[party],
                'upper_95': aggregated[party] + 1.96 * standard_errors[party]
            }
            for party in self.parties
        }
        
        return confidence_intervals
    
    def structural_forecast_model(self, election_date):
        """
        Structural model combining polls, fundamentals, and historical patterns
        
        Model Components:
        1. Current polling average (weighted 50%)
        2. Economic indicators (weighted 25%)
        3. Incumbency advantage/disadvantage (weighted 15%)
        4. Campaign effects (weighted 10%)
        """
        
        # Component 1: Polling average
        polls = self.aggregate_polls_weighted()
        
        # Component 2: Economic fundamentals
        query = """
        SELECT 
            indicator_name,
            value,
            year
        FROM world_bank_data
        WHERE country_code = 'SWE'
            AND indicator_name IN ('GDP growth', 'Unemployment rate', 'Inflation')
            AND year = EXTRACT(YEAR FROM CURRENT_DATE) - 1
        """
        
        economic_df = pd.read_sql(query, self.db)
        economic_score = self.calculate_economic_vote(economic_df)
        
        # Component 3: Incumbency factor
        query_incumbent = """
        SELECT 
            party,
            in_government,
            government_duration_years
        FROM current_government_status
        """
        
        incumbency_df = pd.read_sql(query_incumbent, self.db)
        incumbency_effects = self.calculate_incumbency_penalty(incumbency_df)
        
        # Component 4: Campaign effects (closer to election = more weight on polls)
        days_until_election = (election_date - datetime.now()).days
        campaign_factor = 1.0 if days_until_election < 30 else 0.5  # Polls more reliable near election
        
        # Combine components
        forecasts = {}
        for party in self.parties:
            poll_component = polls[party]['estimate'] * 0.5 * campaign_factor
            economic_component = economic_score.get(party, 0) * 0.25
            incumbency_component = incumbency_effects.get(party, 0) * 0.15
            
            forecast = poll_component + economic_component + incumbency_component
            
            # Ensure non-negative and sums to 100%
            forecasts[party] = max(0, forecast)
        
        # Normalize to 100%
        total = sum(forecasts.values())
        forecasts = {party: (vote / total) * 100 for party, vote in forecasts.items()}
        
        return forecasts
    
    def calculate_economic_vote(self, economic_df):
        """
        Model economic voting: Good economy benefits incumbents
        
        Formula: ΔVote = β₁*GDP_growth + β₂*Unemployment_change + β₃*Inflation
        Coefficients based on Swedish electoral research
        """
        
        gdp_growth = economic_df[economic_df['indicator_name'] == 'GDP growth']['value'].iloc[0]
        unemployment = economic_df[economic_df['indicator_name'] == 'Unemployment rate']['value'].iloc[0]
        inflation = economic_df[economic_df['indicator_name'] == 'Inflation']['value'].iloc[0]
        
        # Economic vote model (simplified coefficients)
        economic_advantage = (0.5 * gdp_growth) - (0.3 * unemployment) - (0.2 * inflation)
        
        # Query: Which parties are in government
        query = "SELECT party FROM current_government_status WHERE in_government = TRUE"
        incumbent_parties = pd.read_sql(query, self.db)['party'].tolist()
        
        # Allocate economic vote to incumbents
        economic_scores = {}
        for party in self.parties:
            if party in incumbent_parties:
                economic_scores[party] = economic_advantage / len(incumbent_parties)
            else:
                economic_scores[party] = 0
        
        return economic_scores
    
    def calculate_incumbency_penalty(self, incumbency_df):
        """
        Model incumbency fatigue: Long-serving governments lose support
        
        Penalty = -0.5% per year in government (capped at -5%)
        """
        
        penalties = {}
        for _, row in incumbency_df.iterrows():
            if row['in_government']:
                penalty = min(-0.5 * row['government_duration_years'], -5.0)
                penalties[row['party']] = penalty
            else:
                penalties[row['party']] = 0
        
        return penalties
Seat Projection Algorithm

Purpose: Convert vote share forecasts to seat allocations using Modified Sainte-Laguë method.

python
def project_riksdag_seats(self, vote_shares):
    """
    Project Riksdag seat distribution from vote share forecasts
    
    Method: Modified Sainte-Laguë with 4% threshold
    Output: 349 seats allocated across parties
    """
    
    # Apply 4% threshold
    qualified_parties = {
        party: vote for party, vote in vote_shares.items()
        if vote >= self.threshold
    }
    
    if len(qualified_parties) == 0:
        raise ValueError("No parties exceed 4% threshold")
    
    # Allocate 349 seats
    seats_allocated = {party: 0 for party in qualified_parties}
    
    for seat_num in range(349):
        # Calculate quotient for each party
        quotients = {}
        for party, vote_pct in qualified_parties.items():
            if seats_allocated[party] == 0:
                divisor = 1.4  # First seat divisor (modified Sainte-Laguë)
            else:
                divisor = 2 * seats_allocated[party] + 1
            
            quotients[party] = vote_pct / divisor
        
        # Award seat to party with highest quotient
        winning_party = max(quotients, key=quotients.get)
        seats_allocated[winning_party] += 1
    
    return seats_allocated

def monte_carlo_seat_simulation(self, vote_forecasts, n_simulations=10000):
    """
    Monte Carlo simulation for seat projection confidence intervals
    
    Method: Sample from vote share distributions, calculate seats
    Output: Probability distribution of seat outcomes
    """
    
    seat_simulations = {party: [] for party in self.parties}
    
    for _ in range(n_simulations):
        # Sample vote shares from normal distributions (using forecast uncertainties)
        sampled_votes = {}
        for party in self.parties:
            mean = vote_forecasts[party]['estimate']
            std = (vote_forecasts[party]['upper_95'] - vote_forecasts[party]['lower_95']) / (2 * 1.96)
            
            # Sample and ensure non-negative
            sampled_votes[party] = max(0, np.random.normal(mean, std))
        
        # Normalize to 100%
        total = sum(sampled_votes.values())
        sampled_votes = {party: (vote / total) * 100 for party, vote in sampled_votes.items()}
        
        # Calculate seats for this sample
        try:
            seats = self.project_riksdag_seats(sampled_votes)
            for party in self.parties:
                seat_simulations[party].append(seats.get(party, 0))
        except ValueError:
            # Skip if no parties exceed threshold (rare edge case)
            continue
    
    # Calculate statistics
    seat_projections = {}
    for party in self.parties:
        sims = seat_simulations[party]
        seat_projections[party] = {
            'median': int(np.median(sims)),
            'mean': np.mean(sims),
            'lower_95': int(np.percentile(sims, 2.5)),
            'upper_95': int(np.percentile(sims, 97.5)),
            'probability_in_riksdag': sum(s > 0 for s in sims) / len(sims)
        }
    
    return seat_projections

2. Coalition Formation Prediction

Purpose: Forecast which coalition is most likely to form government post-election.

python
class CoalitionPredictor:
    """
    Coalition formation analysis using game theory and historical patterns
    Supports: Decision Intelligence Framework
    """
    
    def __init__(self, db_connection):
        self.db = db_connection
    
    def enumerate_viable_coalitions(self, seat_projections):
        """
        Generate all mathematically viable coalition combinations
        
        Criteria:
        1. Total seats ≥ 175 (majority)
        2. Ideologically compatible parties
        3. No historical vetoes (e.g., no party wants coalition with SD except M/KD)
        """
        
        from itertools import combinations
        
        parties = list(seat_projections.keys())
        viable_coalitions = []
        
        # Define compatibility matrix based on Swedish political reality
        incompatible_pairs = [
            ('S', 'M'),   # Polar opposites
            ('S', 'SD'),  # S refuses SD cooperation
            ('V', 'M'),   # Ideological incompatibility
            ('V', 'KD'),  # Ideological incompatibility
            ('MP', 'SD'), # Ideological incompatibility
            ('L', 'V'),   # Ideological distance
        ]
        
        # Iterate through all possible combinations
        for r in range(1, len(parties) + 1):
            for combo in combinations(parties, r):
                total_seats = sum(seat_projections[p]['median'] for p in combo)
                
                # Check majority threshold
                if total_seats >= 175:
                    # Check compatibility
                    compatible = True
                    for p1, p2 in combinations(combo, 2):
                        if (p1, p2) in incompatible_pairs or (p2, p1) in incompatible_pairs:
                            compatible = False
                            break
                    
                    if compatible:
                        viable_coalitions.append({
                            'parties': combo,
                            'total_seats': total_seats,
                            'size': len(combo)
                        })
        
        return viable_coalitions
    
    def calculate_coalition_stability(self, coalition_parties):
        """
        Assess coalition stability using voting alignment history
        
        Data Source: view_riksdagen_party_coalition_agreeableness
        Output: Stability score 0-100
        """
        
        query = f"""
        SELECT 
            p1.party as party_a,
            p2.party as party_b,
            AVG(CASE WHEN p1.party_position = p2.party_position THEN 1.0 ELSE 0.0 END) as alignment_rate
        FROM view_riksdagen_party_ballot_support_annual_summary p1
        JOIN view_riksdagen_party_ballot_support_annual_summary p2 
            ON p1.ballot_id = p2.ballot_id 
            AND p1.party < p2.party
        WHERE p1.party IN {tuple(coalition_parties)}
            AND p2.party IN {tuple(coalition_parties)}
            AND p1.vote_date >= CURRENT_DATE - INTERVAL '4 years'
        GROUP BY p1.party, p2.party
        """
        
        alignment_df = pd.read_sql(query, self.db)
        
        # Average pairwise alignment
        stability_score = alignment_df['alignment_rate'].mean() * 100
        
        return stability_score
    
    def predict_coalition_probability(self, viable_coalitions):
        """
        Assign formation probability to each viable coalition
        
        Factors:
        1. Seat surplus (more seats = more stable)
        2. Coalition size (fewer parties = easier negotiation)
        3. Historical stability (voting alignment)
        4. Ideological cohesion
        """
        
        coalition_scores = []
        
        for coalition in viable_coalitions:
            parties = coalition['parties']
            seats = coalition['total_seats']
            size = coalition['size']
            
            # Factor 1: Seat surplus (above 175)
            seat_surplus = seats - 175
            seat_score = min(seat_surplus / 50, 1.0) * 30  # Max 30 points
            
            # Factor 2: Coalition size (fewer is better)
            size_score = max(0, 30 - (size - 1) * 10)  # 30 for single party, 20 for 2 parties, etc.
            
            # Factor 3: Historical stability
            stability = self.calculate_coalition_stability(parties)
            stability_score = stability * 0.3  # Max 30 points
            
            # Factor 4: Ideological cohesion (simplified)
            # Center-right bloc: M, KD, L, C = high cohesion
            # Left bloc: S, V, MP = high cohesion
            if set(parties).issubset({'M', 'KD', 'L', 'C'}):
                ideology_score = 10
            elif set(parties).issubset({'S', 'V', 'MP'}):
                ideology_score = 10
            else:
                ideology_score = 5  # Mixed bloc
            
            total_score = seat_score + size_score + stability_score + ideology_score
            
            coalition_scores.append({
                'coalition': ' + '.join(parties),
                'parties': parties,
                'seats': seats,
                'probability_score': total_score,
                'factors': {
                    'seat_surplus': seat_score,
                    'size_penalty': size_score,
                    'stability': stability_score,
                    'ideology': ideology_score
                }
            })
        
        # Normalize scores to probabilities
        total_score = sum(c['probability_score'] for c in coalition_scores)
        for coalition in coalition_scores:
            coalition['formation_probability'] = (coalition['probability_score'] / total_score) * 100
        
        # Sort by probability
        coalition_scores.sort(key=lambda x: x['formation_probability'], reverse=True)
        
        return coalition_scores

3. Swing Voter Analysis

Purpose: Identify and model voters likely to switch parties between elections.

sql
-- Swing District Analysis: Identify constituencies with high volatility
WITH election_volatility AS (
    SELECT 
        constituency_name,
        election_year,
        party_name,
        percentage,
        ABS(percentage - LAG(percentage) OVER (
            PARTITION BY constituency_name, party_name 
            ORDER BY election_year
        )) as vote_swing
    FROM constituency_election_results
    WHERE election_year >= 2010
),
constituency_volatility_score AS (
    SELECT 
        constituency_name,
        AVG(vote_swing) as avg_swing,
        MAX(vote_swing) as max_swing,
        STDDEV(vote_swing) as swing_volatility
    FROM election_volatility
    WHERE vote_swing IS NOT NULL
    GROUP BY constituency_name
)
SELECT 
    constituency_name,
    ROUND(avg_swing, 2) as avg_swing_pct,
    ROUND(max_swing, 2) as max_swing_pct,
    ROUND(swing_volatility, 2) as volatility,
    CASE 
        WHEN avg_swing > 5.0 THEN 'HIGH VOLATILITY - Swing District'
        WHEN avg_swing > 3.0 THEN 'MODERATE VOLATILITY'
        ELSE 'LOW VOLATILITY - Safe District'
    END as district_classification
FROM constituency_volatility_score
ORDER BY avg_swing DESC
LIMIT 20;

4. Campaign Effectiveness Analysis

Purpose: Measure impact of campaign events on polling and vote intention.

python
def analyze_campaign_event_impact(self, event_date, event_description):
    """
    Interrupted time series analysis for campaign event impact
    
    Method: Compare polling trend before/after event
    Example Events: Leader debates, scandals, policy announcements
    """
    
    # Query: Polling data 60 days before and after event
    query = f"""
    SELECT 
        poll_date,
        party,
        percentage
    FROM opinion_polls
    WHERE poll_date BETWEEN '{event_date - timedelta(days=60)}' 
                        AND '{event_date + timedelta(days=60)}'
    ORDER BY poll_date
    """
    
    df = pd.read_sql(query, self.db)
    
    # Create intervention variable
    df['post_event'] = (df['poll_date'] > event_date).astype(int)
    df['days_since_start'] = (df['poll_date'] - df['poll_date'].min()).dt.days
    
    impact_results = {}
    
    for party in df['party'].unique():
        party_df = df[df['party'] == party].copy()
        
        # Fit regression: Vote% ~ Time + Post_Event + Time*Post_Event
        from sklearn.linear_model import LinearRegression
        
        X = party_df[['days_since_start', 'post_event']]
        X['interaction'] = X['days_since_start'] * X['post_event']
        y = party_df['percentage']
        
        model = LinearRegression()
        model.fit(X, y)
        
        # Extract coefficients
        time_trend = model.coef_[0]
        event_impact = model.coef_[1]
        trend_change = model.coef_[2]
        
        impact_results[party] = {
            'event': event_description,
            'immediate_impact': event_impact,  # Jump in support at event
            'trend_change': trend_change,      # Change in trend slope
            'statistical_significance': self._calculate_p_value(model, X, y)
        }
    
    return impact_results

5. Threshold Watch (4% Electoral Threshold)

Purpose: Monitor parties at risk of falling below 4% threshold.

sql
-- Threshold Risk Analysis: Parties near 4% cutoff
WITH recent_polls AS (
    SELECT 
        party,
        poll_date,
        percentage,
        ROW_NUMBER() OVER (PARTITION BY party ORDER BY poll_date DESC) as recency_rank
    FROM opinion_polls
    WHERE poll_date >= CURRENT_DATE - INTERVAL '90 days'
),
threshold_analysis AS (
    SELECT 
        party,
        AVG(percentage) as avg_support,
        STDDEV(percentage) as support_volatility,
        MIN(percentage) as min_support,
        MAX(percentage) as max_support,
        COUNT(*) as poll_count
    FROM recent_polls
    WHERE recency_rank <= 10  -- Last 10 polls per party
    GROUP BY party
)
SELECT 
    party,
    ROUND(avg_support, 2) as current_support,
    ROUND(support_volatility, 2) as volatility,
    ROUND(min_support, 2) as lowest_poll,
    ROUND(max_support, 2) as highest_poll,
    CASE 
        WHEN avg_support < 4.0 THEN '🔴 BELOW THRESHOLD - No seats'
        WHEN avg_support < 4.5 THEN '🟠 CRITICAL RISK - Within margin of error'
        WHEN avg_support < 5.0 THEN '🟡 MODERATE RISK - Close to threshold'
        ELSE '🟢 SAFE - Above threshold'
    END as threshold_risk,
    -- Probability of exceeding threshold (normal distribution assumption)
    ROUND(
        100 * (1 - stats.norm.cdf(4.0, avg_support, support_volatility)), 
        1
    ) as probability_exceeds_threshold
FROM threshold_analysis
WHERE avg_support <= 6.0  -- Focus on at-risk parties
ORDER BY avg_support ASC;

ISMS Compliance Mapping

ISO 27001:2022 Controls

A.5.9 - Inventory of Information and Other Associated Assets

  • Electoral data sources cataloged and classified
  • Polling methodology documented for transparency

A.5.33 - Protection of Records

  • Historical election results maintained with integrity
  • Version control for forecast models
Show full SKILL.md (193 more words)Show less
NIST CSF 2.0 Functions

IDENTIFY (ID)

  • ID.RA-1: Electoral volatility risks identified through swing analysis
  • ID.RA-2: Threat intelligence on foreign election interference integrated

DETECT (DE)

  • DE.AE-3: Event data aggregated and correlated (polling anomalies, manipulation)
CIS Controls v8.1

CIS Control 3: Data Protection

  • 3.1: Establish data inventory (electoral data, polling sources)
  • 3.12: Segment data processing and storage based on classification

CIS Control 12: Network Infrastructure Management

  • 12.4: Deny unauthorized communication over network (protect polling data feeds)

Hack23 ISMS Policy References

Data Classification Policy

Privacy Policy

AI Policy

Threat Modeling

References

Official Documentation:

CIA Platform Documentation:

Academic Sources:

  • "Forecasting Elections" - Nate Silver (FiveThirtyEight methodology)
  • "The Signal and the Noise" - Nate Silver (Bayesian forecasting)
  • "Election Forecasting in Sweden" - Swedish National Election Studies
  • "Modified Sainte-Laguë Method" - Electoral Studies Journal

Polling Organizations:

© Hack23, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .github/skills/electoral-analysis of Hack23/cia.

Open the folder on GitHubat commit bbed538

Compare with similar skills

Electoral Analysis next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Electoral Analysis compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Electoral Analysis this skillHack23/cia239—~6.5kAutomated safety check: PassApache-2.0
TimesFM Forecastinggoogle-research/timesfm34k—~4.7kAutomated safety check: PassApache-2.0
Timesfm ForecastingzLanqing/codex-claude-academic-skills4.6k6 repos~7.5kAutomated safety check: NotesApache-2.0
StatsmodelszLanqing/codex-claude-academic-skills4.6k16 repos~4.9kAutomated safety check: PassBSD-3-Clause
Alphaear Predictorninehills/skills2812 repos~531Automated safety check: PassNone
Find Hypertable Candidatestimescale/pg-aiguide1.9k1 repos~2.6kAutomated safety check: PassApache-2.0

Similar skills

  • TimesFM Forecasting

    google-research/timesfm

    Forecasts any univariate time series zero-shot with Google's TimesFM model, returning point forecasts and calibrated prediction intervals without training.

    34k GitHub stars~4.7k tokensUpdated 8 days ago
    Data & AnalyticsAuto-check passed
  • Timesfm Forecasting

    zLanqing/codex-claude-academic-skills

    Zero-shot time series forecasting with Google's TimesFM foundation model.

    4.6k GitHub starsUsed in 6 repos~7.5k tokens
    Data & AnalyticsAuto-check: notes
  • Statsmodels

    zLanqing/codex-claude-academic-skills

    Statistical models library for Python. An agent skill from zLanqing/codex-claude-academic-skills.

    4.6k GitHub starsUsed in 16 repos~4.9k tokens
    Data & AnalyticsAuto-check passed
  • Alphaear Predictor

    ninehills/skills

    Market prediction skill using Kronos. An agent skill from ninehills/skills.

    281 GitHub starsUsed in 2 repos~531 tokens
    Data & AnalyticsAuto-check passed
  • Find Hypertable Candidates

    timescale/pg-aiguide

    A skill your agent uses to analyze an existing PostgreSQL database and identify which tables should be converted to Timescale/TimescaleDB hypertables.

    1.9k GitHub starsUsed in 1 repo~2.6k tokens
    Data & AnalyticsAuto-check passed
  • Pensieve Search

    arkohut/pensieve

    Search the user's local Pensieve screenshot archive by text, app, or time range.

    1.4k GitHub stars~8.2k tokensUpdated 2 mo ago
    Data & AnalyticsAuto-check passed

More from Hack23/cia

All 78 skills in this repo
  • WCAG 2.1 AA compliance, ARIA attributes, keyboard navigation, screen reader optimization for accessible political data platforms

    239 GitHub stars~2.7k tokensUpdated today
    Auto-check passed
  • Advanced chart types, D3.js/Vaadin Charts patterns, political data visualization, time series analysis

    239 GitHub stars~1.5k tokensUpdated today
    Auto-check passed
  • AI Governance

    Hack23/cia

    AI governance, EU AI Act compliance, OWASP LLM security, responsible AI practices for GitHub Copilot agents

    239 GitHub stars~1.4k tokensUpdated today
    Auto-check passed
  • API Integration

    Hack23/cia

    External API integration patterns, retry logic, circuit breakers, caching, rate limiting for government data APIs

    239 GitHub stars~1.9k tokensUpdated today
    Auto-check passed
  • AWS CloudWatch metrics, alarms, dashboards, log insights, and application monitoring for the CIA platform

    239 GitHub stars~1.9k tokensUpdated today
    Auto-check passed
  • AWS security best practices, VPC security, IAM, KMS, CloudTrail, GuardDuty for CIA platform deployment

    239 GitHub stars~2.3k tokensUpdated today
    Auto-check passed

Questions about Electoral Analysis

What does Electoral Analysis do?

Election forecasting models, campaign analysis, coalition prediction, voter behavior analysis for Swedish elections. Electoral Analysis is an agent skill from Hack23/cia.

When should I use Electoral Analysis?

Electoral Analysis fits situations like: tasks that involve Forecasting and time series.

How do I install Electoral Analysis in Claude Code?

Run `npx skills add Hack23/cia --skill electoral-analysis -a claude-code`. Or copy the skill folder (.github/skills/electoral-analysis in Hack23/cia) into .claude/skills/electoral-analysis in your project. Claude Code loads it when a task matches its description.

How do I install Electoral Analysis in Codex?

Run `npx skills add Hack23/cia --skill electoral-analysis -a codex`. Or copy the skill folder (.github/skills/electoral-analysis in Hack23/cia) into .agents/skills/electoral-analysis in your project. Codex loads it when a task matches its description.

Can I use Electoral Analysis in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Hack23/cia --skill electoral-analysis -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/electoral-analysis, .gemini/skills/electoral-analysis, .github/skills/electoral-analysis and .opencode/skills/electoral-analysis in your project.

What does Electoral Analysis need to run?

SKILL.md names no scripts, command-line tools or credentials: Electoral Analysis is instructions for the agent only. Our summary lists: Python 3.

Does Electoral Analysis access the network?

SKILL.md names 7 domains. As links in the text: github.com, val.se, riksdagen.se, novus.se, kantarsifo.se, yougov.se and demoskop.se. This is read from the text; nothing was executed.

Is Electoral Analysis safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Electoral Analysis use?

Electoral Analysis is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Electoral Analysis use?

About 6.5k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Electoral Analysis?

Skills that share tags, products or a category with Electoral Analysis: TimesFM Forecasting (google-research/timesfm, 34k stars), Timesfm Forecasting (zLanqing/codex-claude-academic-skills, 4.6k stars), Statsmodels (zLanqing/codex-claude-academic-skills, 4.6k stars) and Alphaear Predictor (ninehills/skills, 281 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Electoral Analysis?

Hack23 (a GitHub organization) maintains it in Hack23/cia, which has 239 GitHub stars. The repository holds 78 skills in this directory. The repository was last updated on October 7, 2026.

Source: Hack23/cia on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.