Goblin House
Area: EO Capture Score Methodology (eo_capture_scoring)
Filed: 2026-04-20T05:50:43.999Z
Source: External LLM via /handoff/congress
Refine the formula that turns donor concentration + voting-against-constituent + silence + contradiction signals into the 0-100 capture score visible on /congress. The output is a methodology spec + a recommended weight vector + 3-5 worked examples.
The Capture Score is a deterministic metric representing the degree to which an Elected Official’s (EO) legislative behavior, public stance, and financial dependencies align with private interests over constituent needs. It is computed as a weighted aggregate of four signal-specific subscores.
All inputs must be grounded in the database as either facts or connections with the appropriate confidence level.
| Signal | Description | Primary Source Requirement |
|---|---|---|
| Donor Concentration (DC) | Ratio of top-sector PAC/Major Donor funding to total receipts. | FEC Bulk Data / OpenSecrets Filings. |
| Voting Alignment (VAC) | Frequency of votes that contradict the Constituency Baseline. | Congress.gov Roll Call Records. |
| Conspicuous Silence (CS) | Absence of stance on high-impact regulatory changes within the EO's committee remit. | Official Press Releases / Senate/House Record. |
| Documented Contradictions (CX) | Direct pivots between platform claims and legislative actions. | Official Transcripts vs. Vote Records. |
Measures the "Capture Floor" based on the dominance of the Top 3 Donor Sectors. $$S_{dc} = \min\left(100, \frac{\text{Sum of Top 3 Sectors}}{\text{Total Individual + PAC Receipts}} \times 200\right)$$ Note: A 50% concentration in just three sectors triggers a 100 subscore.
Measures the frequency of votes identified as against_constituent in the voting_records table.
$$S_{vac} = \frac{\text{Count(Votes where category = 'against_constituent')}}{\text{Total Analyzed Votes}} \times 100$$
Combines silences and contradictions (CX), weighted by severity.
$$S_{fric} = \min\left(100, (\text{Count}(CS) \times 10) + (\text{Count}(CX_{high}) \times 25) + (\text{Count}(CX_{med}) \times 15)\right)$$
The final score ($CS_{final}$) is a weighted sum of the subscores:
Tier Cutoffs:
capture-score.ts/**
* REFINEMENT: Ensure all inputs are escHtml() in the view.
* Logic uses Drizzle to aggregate connections for dc calculation.
*/
export async function calculateCaptureScore(entityId: string): Promise<number> {
const dc = await getDonorConcentration(entityId); // DC Subscore
const vac = await getVotingFriction(entityId); // VAC Subscore
const fric = await getStanceFriction(entityId); // CS + CX Subscore
const finalScore = (dc * 0.35) + (vac * 0.35) + (fric * 0.30);
return Math.round(finalScore);
}