| 1 | # Docket - Architecture |
| 2 | |
| 3 | ## Shape |
| 4 | ``` |
| 5 | POLLERS (Python) API + WEB (Next.js) |
| 6 | Congress.gov ─┐ ┌─ Next.js app (PWA, dark-first) |
| 7 | GovInfo ──────┤ │ /register (feed) /bill/[id] |
| 8 | USAspending ──┤→ normalize → Postgres ──→│ /spending /states |
| 9 | Treasury ─────┤ (actions, bills, │ /how-we-pick /who-pays |
| 10 | House/Senate ─┤ awards, members, │ |
| 11 | OpenStates ───┘ votes, raw_events) └─ /api (read) → events feed |
| 12 | │ │ |
| 13 | └── change-detection ──→ events ──→ alerts (ntfy + web-push) |
| 14 | ``` |
| 15 | |
| 16 | ## Services |
| 17 | 1. **ingest** (Python) - one poller module per source (`pollers/congress.py`, `usaspending.py`, `treasury.py`, `govinfo.py`, `votes_house.py`, `votes_senate.py`, `openstates.py`), each idempotent, rate-budget aware, with Congress.gov scraper fallback. A scheduler (cron/APScheduler) runs them at the cadences in `DATA_SOURCES.md`. |
| 18 | 2. **change-detection** - diffs each poll against stored `updateDate`/hash, writes normalized `actions` + emits `events`. |
| 19 | 3. **summaries** (Phase 2) - extraction-template pipeline; banned-words + Polarization-Index gate before publish. MVP ships structured fields only. |
| 20 | 4. **web/api** (Next.js) - read API over SQLite/Postgres for the feed + entity pages; the brand-pack UI. |
| 21 | 5. **alerts** - topic/agency/threshold subscriptions → ntfy + web-push. No party-based subscriptions. |
| 22 | |
| 23 | ## Data model (Postgres, draft) |
| 24 | - `bills` (id, source, externalId, congress/session, number, title, sponsor_id, status, policy_area, updated_at, source_url) |
| 25 | - `actions` (id, bill_id?, source, action_type, action_date, chamber, text, dollar_amount?, affected_estimate?, source_url) - the spine of the feed |
| 26 | - `votes` (id, bill_id, chamber, date, yea, nay, present, result, source_url) + `vote_positions` (vote_id, member_id, position) |
| 27 | - `members` (id, name, party, state, chamber, source_url) |
| 28 | - `awards` (id, source, externalId, agency, recipient, amount, action_date, program, source_url) |
| 29 | - `fiscal` (metric, date, value, source_url) - debt/deficit/outlays |
| 30 | - `states` (code, name, openstates_coverage_level, last_seen) - powers the coverage map |
| 31 | - `events` (id, ref_type, ref_id, action_date, dollar_amount, affected_estimate, action_type) - the ranked feed source (rule #3: ONLY these fields feed ranking) |
| 32 | - `raw_events` (source, externalId, payload jsonb, fetched_at) - provenance |
| 33 | - `subscriptions` (user_id, kind[topic|agency|threshold|bill|state|member], value) |
| 34 | |
| 35 | ### Full-spectrum additions (2026-06-26) |
| 36 | - `amendments` (id, congress, type[HAMDT|SAMDT], number, bill_id, sponsor_id, status, agreed_method[recorded|voice|uc|null], text?, source_url) |
| 37 | - `bill_versions` (id, bill_id, version_code[ih|rh|eh|eas|enr|…], govinfo_package_id, fetched_at, text_ref) - the diff inputs |
| 38 | - `bill_diffs` (id, bill_id, from_version, to_version, section_id, change[added|removed|amended], snippet, rider_flag bool, source_url) - powers "what got slipped in" |
| 39 | - `procedural_events` (id, bill_id?, kind[voice_vote|unanimous_consent|cloture|suspension|mtr|discharge], date, chamber, detail, members_jsonb?, source_url) - parsed from CREC; `members` null when no individual record exists |
| 40 | - `lobbying` (id, registrant, client, covered_bill_id?, amount_range, period, source_url) - LDA.gov |
| 41 | - `executive_actions` (id, type[eo|nprm|final_rule|notice], title, agency, fr_doc_number, date, source_url) - Federal Register |
| 42 | - `apportionments` (id, agency, account, appropriated, apportioned, status[released|delayed|withheld], date, source_url→.max.gov) - the impound tracker |
| 43 | - `state_coverage` (state, source[openstates|legiscan], data_type, last_scrape, confidence[ok|stale|missing]) - the honesty layer |
| 44 | |
| 45 | **Hard constraint:** every user-facing row exposes a non-null `source_url` whose host is allowlisted (neutrality rule #1, enforced by test). For aggregator-sourced rows (OpenStates/LegiScan/OpenOMB/Lambda), `source_url` points to the **underlying official record**, not the aggregator (DATA_SOURCES.md third-party rule). |
| 46 | |
| 47 | ### What-changed pipeline (the signature feature) |
| 48 | Per bill: `/related/BILLS-{id}` → fetch each version XML → section-parse + align (id + fuzzy heading) → diff → write `bill_diffs` (added/removed/amended), run rider keyword regex on appropriations-bill added sections → `rider_flag`. Surfaced as a "What changed / what was added" view + a per-section diff. Conference reports diffed against last engrossed version. Voice-vote/UC additions labeled "no individual record." |
| 49 | |
| 50 | ## Ranking (neutrality rule #3) |
| 51 | `score(event) = f(actionDate, dollarAmount, affectedEstimate, actionType)` - no engagement/news/social inputs. Pure function, unit-tested to reference only those four fields. |
| 52 | |
| 53 | ## Build/run hosts |
| 54 | - **Frontend:** Next.js dev server; `npm run dev` from the repo root. |
| 55 | - **Backend:** Python ingest; `python -m ingest.run` from `services/ingest/`. |
| 56 | - **Database:** SQLite for dev/staging; Postgres for production (Phase 1+). |
| 57 | - **Deploy target:** Cloudflare front + self-hosted API backend. |
| 58 | |
| 59 | ## Phases (see PLAN.md §8 for detail) |
| 60 | 0. ✅ Brand + clickable shell (fake data) - DONE |
| 61 | 1. Federal ingest spine → real feed (read-only) - DONE backend (USAspending/votes); **NEXT: wire read-API so frontend shows real data** |
| 62 | 2. **Full bills/laws**: Congress.gov bills + GovInfo full text → bill pages + lookup/search (federal) |
| 63 | 3. **"What got slipped in"**: bill_versions + diff pipeline + rider flags - the signature feature |
| 64 | 4. **Closed-door layer**: CREC parsing (voice votes/UC/procedural) + Federal Register + lobbying + apportionment impound tracker |
| 65 | 5. **State granular**: OpenStates + LegiScan + per-state confidence scores |
| 66 | 6. **Accessibility modes**: Readable/Light + Plain Mode + plain-language summaries (docs/ACCESSIBILITY.md) |
| 67 | 7. Alerts + accounts (topic/state/member/bill follows) |
| 68 | 8. Trust pages + launch |