feat: add election calendar with CII scoring integration#221
feat: add election calendar with CII scoring integration#221Jacob-Strokus wants to merge 2 commits intokoala73:mainfrom
Conversation
- Add src/config/elections.ts with 25 global elections for 2026-2027 - Integrate election proximity boost into Country Instability Index scoring - Election day: +15 pts, imminent (1-7d): +10, elevated (7-30d): +5 - Significance multipliers: high=1.0, medium=0.6, low=0.3 - Post-election uncertainty period (3 days): +8 pts - Display election badges in CII panel with days-until countdown - Add buildElectionBadge() using h() DOM helper (compatible with i18n refactor) - Export election utilities from config/index.ts - Add .cii-election-badge styles
|
Someone is attempting to deploy a commit to the Elie Team on Vercel. A member of the Team first needs to authorize it. |
|
auth pls |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
koala73
left a comment
There was a problem hiding this comment.
Thanks for the initiative on this — election proximity as a CII scoring signal is a great idea and the integration architecture (config → scoring → badge) is clean. However, the election data needs significant corrections before we can merge.
Findings
P1 - High
Most election dates are inconsistent with IFES/official election cycles as of February 2026. This creates incorrect instability boosts and rank distortion.
| PR entry | Validation result |
|---|---|
elections.ts:55 (Mexico 2026-06-07) |
IFES shows 2024 national election and 3-year/6-year legislative terms — 2026 is off-cycle (next legislative cycle is 2027 for deputies). |
elections.ts:63 (Philippines 2026-05-11) |
IFES shows legislative election held 2025-05-12 — 2026 is off-cycle. |
elections.ts:47 (Iran presidential 2026-06-18) |
IFES shows presidential election held 2024-07-05 and 4-year presidential term — 2026 is off-cycle. |
elections.ts:151 (Germany 2027-09-26) |
Bundestag election was held 2025-02-23 and term is 4 years — regular cycle points to 2029, not 2027. |
elections.ts:183 (India 2027-05-01) |
IFES shows 2024 Lok Sabha election and 5-year lower-house terms — 2027 is off-cycle absent early dissolution. |
elections.ts:175 (South Korea presidential 2027-03-09) |
IFES shows presidential election held 2025-06-03 with 5-year term — 2027 is off-cycle. |
elections.ts:217 (Norway 2027-09-13) |
IFES shows parliamentary election held 2025-09-08 and 4-year terms — 2027 is off-cycle. |
elections.ts:241 (Liberia 2027-10-12) |
IFES shows 2023 presidential election and 6-year presidential terms — 2027 is off-cycle. |
elections.ts:249 (Chile 2027-11-21) |
IFES shows presidential election held in 2025 and 4-year term — 2027 is off-cycle. |
elections.ts:71 (Colombia 2026-03-08) |
✅ Consistent (IFES confirms 2026-03-08 legislative election). |
elections.ts:99 (US 2026-11-03) |
✅ Consistent (IFES confirms 2026-11-03 midterm date). |
elections.ts:81 (Brazil 2026-10-04) |
✅ Consistent (IFES confirms 2026-10-04). |
Timezone correctness bug: new Date('YYYY-MM-DD') is parsed at UTC midnight, then day distance uses local now with Math.floor. In US time zones this can become previous-day local time and produce early/late boosts (off-by-one day).
P2 - Medium
elections.ts:321applies a post-election boost for -3 days, butelections.ts:371(getElectionProximity) never returns a post-election proximity state. Result: score can be boosted while UI badge disappears, making the score jump non-explainable to users.- No tests were added for new election scoring/time boundaries (especially timezone/day-window edges), despite new ranking logic in
country-instability.ts:651.
P3 - Low
CIIPanel.ts:66hardcodes badge labels (TODAY,Xd) instead of usingt()i18n like the rest of the panel.
Recommendation
The architecture and CII integration are solid — please:
- Rebuild the election calendar using IFES Election Guide as the source of truth
- Fix the UTC timezone bug in
getDaysUntilElection() - Add a
post-electionproximity state to match the scoring boost - Add i18n keys for badge labels
Sources
- IFES Election Guide (Mexico)
- IFES Election Guide (Philippines)
- IFES Election Guide (Iran)
- IFES Election Guide (Germany)
- German Federal Returning Officer (Bundestag 2025)
- IFES Election Guide (India)
- IFES Election Guide (South Korea)
- IFES Election Guide (Norway)
- IFES Election Guide (Liberia)
- IFES Election Guide (Chile)
- IFES Election Guide (Colombia)
- IFES Election Guide (US)
- IFES Election Guide (Brazil)
- UK Electoral Commission
P1 - High Priority: - Remove off-cycle elections: Iran 2026-06-18, Mexico 2026-06-07, Philippines 2026-05-11 - Fix dates per IFES Election Guide: - Germany: 2027-09-26 → 2029-02-23 (4y term from 2025) - South Korea presidential: 2027-03-09 → 2030-06-03 (5y term) - India: 2027-05-01 → 2029-05-01 (5y term from 2024) - Norway: 2027-09-13 → 2029-09-10 (4y term) - Liberia: 2027-10-12 → 2029-10-09 (6y term) - Chile: 2027-11-21 → 2029-11-18 (4y term) - Fix UTC timezone bug in getDaysUntilElection() to prevent off-by-one errors P2 - Medium Priority: - Add 'post-election' proximity state to ElectionProximity type - getElectionProximity() now returns post-election for days -1 to -3 - Matches existing postElection scoring boost in getElectionCIIBoost() P3 - Low Priority: - Replace hardcoded badge labels with i18n keys via t() - Add components.cii.election keys to all 16 locale files (en, fr, es, ar, ja, de, it, pt, ru, zh, pl, nl, sv, tr, th, vi)
Jacob-Strokus
left a comment
There was a problem hiding this comment.
Pushed changes to correct election calendar data
P1 - High Priority: - Remove off-cycle elections: Iran 2026-06-18, Mexico 2026-06-07, Philippines 2026-05-11 - Fix dates per IFES Election Guide: - Germany: 2027-09-26 → 2029-02-23 (4y term from 2025) - South Korea presidential: 2027-03-09 → 2030-06-03 (5y term) - India: 2027-05-01 → 2029-05-01 (5y term from 2024) - Norway: 2027-09-13 → 2029-09-10 (4y term) - Liberia: 2027-10-12 → 2029-10-09 (6y term) - Chile: 2027-11-21 → 2029-11-18 (4y term) - Fix UTC timezone bug in getDaysUntilElection() to prevent off-by-one errors P2 - Medium Priority: - Add 'post-election' proximity state to ElectionProximity type - getElectionProximity() now returns post-election for days -1 to -3 - Matches existing postElection scoring boost in getElectionCIIBoost() P3 - Low Priority: - Replace hardcoded badge labels with i18n keys via t() - Add components.cii.election keys to all 16 locale files (en, fr, es, ar, ja, de, it, pt, ru, zh, pl, nl, sv, tr, th, vi)
koala73
left a comment
There was a problem hiding this comment.
Follow-up Review — Fix Commit 2cd6f1b
Thanks for the quick turnaround. The 4 items from the original review are addressed:
| Original Item | Status |
|---|---|
| Off-cycle elections (Iran/Mexico/Philippines) | ✅ Removed |
| UTC timezone bug | ✅ Fixed with Date.UTC() normalization |
Missing post-election proximity state |
✅ Added to type + getElectionProximity() |
| Hardcoded badge labels (i18n) | ✅ Now uses t() across all 16 locales |
However, verifying the remaining election dates against IFES/official sources reveals 6 more incorrect entries and 2 mislabeled ones:
P1 — Wrong dates (must fix)
| Entry | PR Date | Reality | Issue |
|---|---|---|---|
| Australia | 2026-05-21 parliamentary |
Last federal election: May 3, 2025 (3yr max term) | Next is ~2028. No 2026 federal election exists. Remove. |
| Japan | 2026-07-25 parliamentary ("Upper House") |
Last HC election: July 20, 2025 (3yr cycle) | Next HC is ~2028. A snap Lower House election was held Feb 8, 2026 — different chamber. Remove or correct to Lower House 2026-02-08 if retroactive entries are wanted. |
| Georgia | 2026-10-26 presidential |
Georgia switched to indirect presidential elections in 2024 (electoral college). Next: ~2029. | Oct 26 was the 2024 parliamentary election date, not a 2026 event. Remove. |
| Rwanda | 2027-07-15 presidential |
Last presidential: July 15, 2024 (5yr term per 2015 amendment) | Next is 2029, not 2027. Remove or correct to 2029. |
| Iran legislative | 2027-03-01 |
Last Majlis election: March 1, 2024 (4yr term) | Next is ~2028, not 2027. Correct to 2028. |
| UK | 2027-12-17 parliamentary |
Last election: July 4, 2024. Latest possible: Aug 15, 2029. | No evidence supports a 2027 election. Speculative date with no source. Remove or add caveat that this is a guess. |
P2 — Mislabeled (should fix)
| Entry | Issue | Fix |
|---|---|---|
Czech Republic 2026-10-15 |
Labeled as parliamentary but the Chamber of Deputies was elected in 2025 (4yr term → ~2029). October 2026 has Senate + municipal elections (1/3 Senate renewal). |
Change type to 'local' or add a 'senate' type, and update notes. |
Kazakhstan 2026-03-19 |
March 15, 2026 is a constitutional referendum, not a parliamentary election. If the referendum passes, parliament dissolves July 1 and new parliamentary elections follow ~Aug 2026. | Either change to referendum date (Mar 15) with correct type, or change to the expected parliamentary election (~Aug 2026). |
P2 — Still missing (from original review)
- No tests for election scoring/timezone/day-window boundary logic. Given 9 of 25 original entries had wrong dates, automated validation would catch future drift.
Summary
| Status | Count |
|---|---|
| Original issues fixed | 4/4 ✅ |
| New wrong dates found | 6 |
| Mislabeled entries | 2 |
| Tests still missing | 1 |
The corrected entries (Germany 2029, SK presidential 2030, India 2029, Norway 2029, Liberia 2029, Chile 2029) all verified correctly — good work on those. The 2026 entries that survived from the original commit are the ones that need attention now.
Recommendation: Cross-reference every remaining entry against IFES Election Guide before the next push.
koala73
left a comment
There was a problem hiding this comment.
Final Review — PR #221
Thanks for fixing the original issues quickly — the UTC timezone fix, post-election proximity state, i18n badge labels, and removal of the 3 off-cycle elections are all solid. The architecture (config → scoring → badge) remains clean.
However, the following date/label issues must be resolved before merge:
P1 — Blocking (affects Tier1 CII scoring)
These are Tier1 countries where incorrect dates will trigger election boosts prematurely via getElectionCIIBoost() in country-instability.ts:648:
| # | File | Issue |
|---|---|---|
| 1 | elections.ts:172 |
UK hardcoded to 2027-12-17 ("expected by December 2027"), but UK Electoral Commission states only "no later than 15 August 2029." No evidence supports a 2027 election. Will trigger GB election boost ~2 years too early. Remove or move to 2029 with speculative caveat. |
| 2 | elections.ts:236 |
Iran legislative set to 2027-03-01, but IFES shows the last Majlis election was 2024. Iran Constitution Art. 63 sets a 4-year term → next ~2028. Correct to 2028. |
P2 — Should Fix (incorrect data, lower scoring impact)
| # | File | Issue | Fix |
|---|---|---|---|
| 1 | elections.ts:68 |
Australia 2026-05-21 — APH Library places the next federal election in the 2027–2028 window. No 2026 election exists. |
Remove or correct to ~2028. |
| 2 | elections.ts:86-88 |
Japan 2026-07-25 labeled "Upper House" — IFES shows 2026 was House of Representatives (snap), while HC was 2025 (next HC ~2028). |
Remove or correct chamber/date. |
| 3 | elections.ts:109-110 |
Georgia 2026-10-26 as direct presidential — IFES confirms president is indirectly elected by electoral college (since 2024), 5-year term → next ~2029. |
Remove. |
| 4 | elections.ts:212 |
Rwanda 2027-07-15 presidential — IFES shows 2024 election with 5-year term → next ~2029. |
Remove or correct to 2029. |
| 5 | elections.ts:101-102 |
Czech Republic 2026-10-15 labeled parliamentary — IFES identifies the 2026 event as Senate (1/3 renewal), not full parliamentary. |
Change type to 'local' and update notes. |
| 6 | elections.ts:117-118 |
Kazakhstan 2026-03-19 labeled parliamentary — IFES lists this as a constitutional referendum (March 15). Parliamentary elections would follow ~Aug 2026 if passed. |
Change to referendum with correct date, or move to expected parliamentary date. |
Assumptions
- Severity is based on scoring impact: only Tier1 countries (US, GB, FR, DE, etc.) meaningfully affect the election boost path in
country-instability.ts:648. - Where sources provide cycle/term constraints rather than a declared polling day, corrections are inferred and should be marked tentative in the data notes.
Overall the feature is well-designed and close to mergeable. Once the dates above are corrected against the linked IFES sources, this is good to go.
Summary
Type of change
Affected areas
/api/*)Checklist
api/rss-proxy.jsallowlist (if adding feeds)npm run typecheck)Screenshots