Vol. 2026 · Methodology · Editorial
MethodologyCivic tech that won't show its work isn't worth trusting. Four decisions shape what you see when you use CivicRadar: how we score relevance, which bills we drop before you ever see them, the rule that governs whether your message reads as a stakeholder or an ally, and the verification standard every advocacy organization on this site has to meet. We publish all four below, with links to the source files where each decision lives in code.
§1 · RelevanceWhen you pick identities and issues on the front page, those choices become a set of public tags. For each bill OpenStates returns, we look for whole-word matches between each tag's keywords and the bill's title, subjects, and abstract. Whole-word matching is deliberate — it stops "ICE" from matching "police" or "ace" from matching "peace." A bill that hits more keywords from the same tag scores higher, with diminishing returns so keyword-stuffing can't dominate the rankings.
Tag matches are the base score. Five public bonus categories then weight the result toward bills that are actually movable:
- Where the bill is in its legislative path. A bill that just cleared committee is more contactable than one introduced six months ago and never moved.
- Whether advocacy organizations have publicly weighed in. A bill that Lambda Legal, the ACLU, or Equality Florida is tracking carries clearer constituent stakes than one no named org has touched.
- How recently the bill moved.Today's movement ranks above a bill that's been sitting still since March, with a smooth decay over time.
- Whether it's in your state. State legislators represent ~120,000 people each — your call carries real weight. Federal bills are ranked too, but one rung lower at the same tag-match level.
- How many co-sponsors signed on. A bill with no co-sponsors rarely gets a floor vote. A bill with many is worth your time.
The numeric weights that turn those signals into a final score are tuned against feedback from the constituencies CivicRadar was built for. We keep the weights in private config — open-sourcing them would invite bad actors to game the rankings — but the algorithm shape is in the open and the source is below.
The public tag inventory
Every CivicRadar user picks from this set. Identity tags describe lived experience; issue tags describe the things you care about regardless of whether they affect you personally. The voice rule in §3 turns on which list a user's selections come from.
Identity tags (24)
- lgbtq
- immigrant
- medicaid
- renter
- housing_insecure
- disability
- poc
- undocumented
- low_income
- student
- union
- reproductive_age
- religious_minority
- senior
- parent
- worker_w2
- veteran
- homeowner
- caregiver
- adoptive_or_foster_parent
- formerly_incarcerated
- tribal_indigenous
- survivor_dv_sa
- mental_health
Issue tags (23)
- lgbtq_rights
- immigration_policy
- healthcare_medicaid
- housing_homelessness
- disability_rights
- racial_justice
- reproductive_rights
- education_student_debt
- labor_workers_rights
- environmental_justice
- religious_freedom
- gun_violence_prevention
- senior_services
- child_welfare
- veterans_services
- adoption_foster_care
- animal_welfare
- criminal_justice_reform
- drug_policy_reform
- voting_rights
- climate
- privacy_digital_rights
- tax_budget_policy
→ Read the source: lib/relevance.ts · lib/identity-keywords.ts
§2 · What we excludeOpenStates returns a lot of things that look like legislation but don't change law — proclamations naming a day, memorials honoring someone who died, resolutions expressing a sentiment. They have no constituent action potential. Calling your rep about a proclamation feels like civic engagement and isn't. We filter them out server-side, before scoring, so a vague sentiment can't crowd out a real housing bill in your results.
We keep
- bill
- constitutional_amendment
We drop
- resolution
- concurrent_resolution
- joint_resolution
- proclamation
- memorial
→ Read the source: lib/openstates.ts · SPEC §4c
§3 · The voice ruleWhen you tell us your identities and issues, we hand them to a Claude Haiku model with one rule that's stricter than anything else in our prompt. It's quoted verbatim from the spec below.
If <user_identity>contains entries, the user is a DIRECT STAKEHOLDER. Lead with their personal stake. Phrasings like "as someone affected by..." or "this bill matters to me because..." are appropriate.
If <user_identity> is EMPTY and only <user_issues> has entries, the user is an ALLY/ADVOCATE, not a personal stakeholder. NEVER write phrases like "as someone directly affected" or "this bill affects me personally." Instead, frame the message as a concerned constituent who cares about the issue. Examples: "as a Texan who cares about...", "I'm writing because I believe...".
— SPEC §8a, system prompt rule 3
Why this matters: an ally who's drafted a letter claiming to be directly affected — when they aren't — is a failure mode that makes the whole tool look fake to the legislative staffer who reads it. The rule prevents the model from "helpfully" saying things you never claimed. You can edit any draft before you send. Your draft never touches our servers.
→ Read the source: SPEC §8a · lib/claude.ts
§4 · Source rulesCivicRadar publishes positions from 90 advocacy organizations across 30+ states and federal — Lambda Legal, the ACLU, Equality Federation state affiliates, Planned Parenthood state arms, League of Conservation Voters chapters, and 85 others. None of them are partners of CivicRadar. We have no relationship with them and no consent arrangement. We cite their public positions the same way a journalist would, with attribution and a link to the original source. The rules below define what counts as a citable source.
Acceptable sources
- Org action-center / take-action pages
- Org press releases
- Org official scorecards, indexes, and reports
- Org-authored public documents with a stable URL
- Org official social posts — only when linked via an archive URL (archive.org, archive.today). Live social URLs can disappear.
- Legislative testimony PDFs where the org is the author
Not acceptable
- Staff emails, DMs, or private conversations
- Inferred positions ("everyone knows org X opposes this")
- Linkless paraphrases
- Content behind logins or paywalls
- Anything a third party can't independently verify by clicking the link
If a staffer at the named org audited the file, every row must be self-verifying. No row may depend on CivicRadar's interpretation.
— SPEC §6a, the functional test
We don't enforce that rule on the honor system. Every position file in our dataset is checked by an audit script that resolves the bill ID against OpenStates, validates the source URL, and flags any row that fails. Mismatches and broken links never ship to production.
→ Read the source: SPEC §6a · scripts/audit-advocacy-positions.ts
AccountabilityIf a bill in your results doesn't belong there, if a position is wrong, if the voice rule failed on a draft — those are bugs, and we want to know. File an issue on GitHub or write to us. The four decisions above are the ones we're willing to defend in public; the day we can't defend one of them is the day we change it.
→ File an issue on GitHub