Vol. 2026 · Methodology · Editorial

CivicRadar
Methodology

How CivicRadar finds the bills with your name on them.

Civic 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 · Relevance

We count tag matches, then weight them by what makes a bill actionable.

When 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 exclude

We drop ceremonial items that offer no real constituent action.

OpenStates 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 rule

The message we draft for you depends on whether you're a stakeholder or an ally.

When 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 rules

Every advocacy position links to a public document. If a third party can't click and verify, we don't publish it.

CivicRadar 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

Accountability

We publish this so the work can be held to it.

If 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