Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | 13x 13x 13x 13x 13x 13x 2x 11x 3x 8x 3x 5x 13x 13x 13x | // SPDX-FileCopyrightText: 2024-2026 Hack23 AB
// SPDX-License-Identifier: Apache-2.0
/**
* @module MCP/ep/election-calendar
* @description Election calendar context for EP10 → EP11 transition analysis.
*/
import {
EP_NEXT_ELECTION_START,
EP_NEXT_ELECTION_END,
EP_CURRENT_TERM,
EP_NEXT_TERM,
} from '../../constants/config.js';
/**
* Election imminence tier derived from days-to-election.
* Matches the T-180 / T-90 / T-30 tiers defined in
* `analysis/methodologies/electoral-cycle-methodology.md`.
*/
export type ElectionImminentTier = 'NONE' | 'T-180' | 'T-90' | 'T-30';
/**
* Context about the upcoming EP election window, used by long-horizon article
* workflows (`news-quarter-ahead`, `news-year-ahead`, `news-term-outlook`,
* `news-election-cycle`) to calibrate electoral-cycle analysis depth.
*/
export interface ElectionCalendarContext {
/** Current or upcoming parliamentary term identifier */
termId: 'EP10' | 'EP11';
/** Start/end ISO dates of the next election window */
nextElectionWindow: { start: string; end: string };
/** Days until the election window starts (negative if window already started) */
daysToElection: number;
/** Imminence tier based on days-to-election */
electionImminentTier: ElectionImminentTier;
}
/**
* Compute the election calendar context for the EP10 → EP11 transition.
* Returns deterministic output for any reference date (defaults to `new Date()`).
*
* Tier mapping (per `analysis/methodologies/electoral-cycle-methodology.md`):
* - `daysToElection > 180` → `NONE`
* - `180 ≥ d > 90` → `T-180`
* - `90 ≥ d > 30` → `T-90`
* - `30 ≥ d` → `T-30`
*
* @param referenceDate - Date to compute from (default: now)
* @returns Election calendar context with term, window, days, and tier
*/
export function getElectionCalendarContext(referenceDate?: Date): ElectionCalendarContext {
const ref = referenceDate ?? new Date();
const electionStart = new Date(EP_NEXT_ELECTION_START + 'T00:00:00Z');
const diffMs = electionStart.getTime() - ref.getTime();
const millisecondsPerDay = 1000 * 60 * 60 * 24;
const daysToElection =
diffMs >= 0 ? Math.ceil(diffMs / millisecondsPerDay) : Math.floor(diffMs / millisecondsPerDay);
let electionImminentTier: ElectionImminentTier;
if (daysToElection > 180) {
electionImminentTier = 'NONE';
} else if (daysToElection > 90) {
electionImminentTier = 'T-180';
} else if (daysToElection > 30) {
electionImminentTier = 'T-90';
} else {
electionImminentTier = 'T-30';
}
const electionEnd = new Date(EP_NEXT_ELECTION_END + 'T23:59:59Z');
const termId = ref.getTime() > electionEnd.getTime() ? EP_NEXT_TERM : EP_CURRENT_TERM;
return {
termId,
nextElectionWindow: { start: EP_NEXT_ELECTION_START, end: EP_NEXT_ELECTION_END },
daysToElection,
electionImminentTier,
};
}
|