docs: overhaul documentation + add deployment guides

- ARCHITECTURE.md: update for v0.9.0/v0.9.3 — collections schema,
  collection_bills schema, alert_filters in notification_prefs,
  action_category in notification payload, migrations 0015/0016,
  /api/collections + /api/share endpoints, updated pages table,
  pipeline flow reflects categorize_action(), v0.9.0 and v0.9.3
  feature history entries
- ROADMAP.md: new file merging "MVP threshold" and "Feature Roadmap"
  docs into one clean shipped/upcoming/backlog structure with v1.0
  definition; removes stale design notes and duplicate entries
- DEPLOYING.md: new — prerequisites, .env setup, first run, admin
  account, domain/SSL with Caddy, useful commands
- UPDATING.md: new — SSH setup, manual deploy, deploy script, Gitea
  webhook + webhook listener, rollback procedure, env-only updates
- Delete: "MVP threshold this make v1 complete.md" and
  "PocketVeto — Feature Roadmap.md" (superseded by ROADMAP.md)
- how-it-works/page.tsx: accurate per-mode default alert sets,
  Alert Filters callout linking to Notifications settings
- notifications/page.tsx: Follow mode default includes amendment filed;
  Pocket Veto default excludes calendar placement

Authored-By: Jack Levy
This commit is contained in:
Jack Levy
2026-03-02 19:22:02 -05:00
parent a39ae4ccba
commit 676bf1b78d
8 changed files with 728 additions and 334 deletions

View File

@@ -312,7 +312,7 @@ News articles correlated to a specific member of Congress.
| email | varchar (unique) | | | email | varchar (unique) | |
| hashed_password | varchar | bcrypt | | hashed_password | varchar | bcrypt |
| is_admin | bool | First registered user = true | | is_admin | bool | First registered user = true |
| notification_prefs | jsonb | ntfy topic URL, ntfy auth method/token/credentials, ntfy enabled, RSS token, quiet_hours_start/end (0–23 local), timezone (IANA name, e.g. `America/New_York`) | | notification_prefs | jsonb | ntfy topic URL, ntfy auth method/token/credentials, ntfy enabled, RSS token, quiet_hours_start/end (0–23 local), timezone (IANA name, e.g. `America/New_York`), alert_filters (nested dict: `{neutral: {...}, pocket_veto: {...}, pocket_boost: {...}}` — 8 boolean keys per mode) |
| rss_token | varchar (nullable) | Unique token for personal RSS feed URL | | rss_token | varchar (nullable) | Unique token for personal RSS feed URL |
| created_at | timestamptz | | | created_at | timestamptz | |
@@ -418,11 +418,39 @@ Stores notification events for dispatching to user channels (ntfy, RSS).
| user_id | int (FK → users, CASCADE) | | | user_id | int (FK → users, CASCADE) | |
| bill_id | varchar (FK → bills, SET NULL) | nullable | | bill_id | varchar (FK → bills, SET NULL) | nullable |
| event_type | varchar | `new_document`, `new_amendment`, `bill_updated`, `weekly_digest` | | event_type | varchar | `new_document`, `new_amendment`, `bill_updated`, `weekly_digest` |
| payload | jsonb | `{bill_title, bill_label, brief_summary, bill_url, milestone_tier}` | | payload | jsonb | `{bill_title, bill_label, brief_summary, bill_url, action_category, milestone_tier}` |
| dispatched_at | timestamptz (nullable) | NULL = pending dispatch | | dispatched_at | timestamptz (nullable) | NULL = pending dispatch |
| created_at | timestamptz | | | created_at | timestamptz | |
`milestone_tier` in payload: `"progress"` (passed, signed, markup, conference, etc.) or `"referral"` (committee referral). Neutral follows silently skip referral-tier events; pocket_veto and pocket_boost receive them as early warnings. `action_category` in payload (new events): one of `vote`, `presidential`, `committee_report`, `calendar`, `procedural`, `referral`. `milestone_tier` is retained for backwards compatibility (`"referral"` or `"progress"`). The dispatcher checks `notification_prefs.alert_filters[follow_mode][action_category]` to decide whether to send. `new_document` and `new_amendment` events are filtered by event type directly (not action_category).
---
### `collections`
Named, curated groups of bills. Shareable via UUID token.
| Column | Type | Notes |
|---|---|---|
| id | int (PK) | |
| user_id | int (FK → users, CASCADE) | |
| name | varchar | 1–100 characters |
| slug | varchar | URL-safe version of name |
| is_public | bool | Signals inclusion in future public directory |
| share_token | uuid | Unique share URL token — read-only for non-owners |
| created_at | timestamptz | |
---
### `collection_bills`
Join table linking bills to collections.
| Column | Type | Notes |
|---|---|---|
| collection_id | int (FK → collections, CASCADE) | |
| bill_id | varchar (FK → bills, CASCADE) | |
| added_at | timestamptz | |
Unique constraint: `(collection_id, bill_id)`.
--- ---
@@ -444,6 +472,8 @@ Stores notification events for dispatching to user channels (ntfy, RSS).
| `0012_dedupe_bill_actions_unique.py` | Unique constraint on `(bill_id, action_date, action_text)` for idempotent action ingestion | | `0012_dedupe_bill_actions_unique.py` | Unique constraint on `(bill_id, action_date, action_text)` for idempotent action ingestion |
| `0013_add_follow_mode.py` | `follow_mode` column on `follows` (`neutral` / `pocket_veto` / `pocket_boost`) | | `0013_add_follow_mode.py` | `follow_mode` column on `follows` (`neutral` / `pocket_veto` / `pocket_boost`) |
| `0014_add_bill_notes.py` | `bill_notes` table with unique constraint `(user_id, bill_id)` | | `0014_add_bill_notes.py` | `bill_notes` table with unique constraint `(user_id, bill_id)` |
| `0015_add_collections.py` | `collections` table (`id`, `user_id`, `name`, `slug`, `is_public`, `share_token` UUID, `created_at`) |
| `0016_add_collection_bills.py` | `collection_bills` join table (`collection_id` FK, `bill_id` FK, `added_at`); `share_token` UUID column on `bill_briefs` |
Migrations run automatically on API startup: `alembic upgrade head`. Migrations run automatically on API startup: `alembic upgrade head`.
@@ -533,6 +563,26 @@ Auth header: `Authorization: Bearer <jwt>`
| POST | `/test/follow-mode` | Required | Simulate a follow-mode notification to preview delivery behavior. | | POST | `/test/follow-mode` | Required | Simulate a follow-mode notification to preview delivery behavior. |
| GET | `/history` | Required | Recent notification events (dispatched + pending). | | GET | `/history` | Required | Recent notification events (dispatched + pending). |
### `/api/collections`
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | `/` | Required | Current user's collections with bill count. |
| POST | `/` | Required | Create collection `{name, is_public}`. Generates slug + share_token. |
| GET | `/{id}` | Required | Collection detail with bills. |
| PUT | `/{id}` | Required | Update `{name, is_public}`. |
| DELETE | `/{id}` | Required | Delete collection (owner only). |
| POST | `/{id}/bills/{bill_id}` | Required | Add bill to collection. |
| DELETE | `/{id}/bills/{bill_id}` | Required | Remove bill from collection. |
| GET | `/share/{token}` | — | Public read-only view of a collection by share token. |
### `/api/share`
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | `/brief/{token}` | — | Public brief + bill data by share token (from `bill_briefs.share_token`). |
| GET | `/collection/{token}` | — | Public collection + bills by share token. |
### `/api/admin` ### `/api/admin`
| Method | Path | Auth | Description | | Method | Path | Auth | Description |
@@ -610,12 +660,15 @@ Auth header: `Authorization: Bearer <jwt>`
has no sponsor data), upserts Member, sets bill.sponsor_id has no sponsor data), upserts Member, sets bill.sponsor_id
↳ New bills → fetch_bill_documents.delay(bill_id) ↳ New bills → fetch_bill_documents.delay(bill_id)
↳ Updated bills → fetch_bill_documents.delay(bill_id) if changed ↳ Updated bills → fetch_bill_documents.delay(bill_id) if changed
↳ Updated bills → emit bill_updated notification if action is a milestone: ↳ Updated bills → emit bill_updated notification if categorize_action() returns a category:
- "progress" tier: passed/failed, signed/vetoed, enacted, markup, conference, - vote: passed, failed, agreed to, roll call
reported from committee, placed on calendar, cloture, roll call - presidential: signed, vetoed, enacted, presented to the president
→ all follow types (bill, sponsor, topic) receive notification - committee_report: markup, ordered to be reported, ordered reported, reported by, discharged
- "referral" tier: referred to committee - calendar: placed on
→ pocket_veto and pocket_boost only; neutral follows silently skip - procedural: cloture, conference
- referral: referred to
→ All three follow types (bill, sponsor, topic) receive notification.
Whether it is dispatched depends on the user's per-mode alert_filters in notification_prefs.
2. document_fetcher.fetch_bill_documents(bill_id) 2. document_fetcher.fetch_bill_documents(bill_id)
↳ Gets text versions from Congress.gov (XML preferred, falls back to HTML/PDF) ↳ Gets text versions from Congress.gov (XML preferred, falls back to HTML/PDF)
@@ -755,14 +808,20 @@ class ReverseBrief:
|---|---| |---|---|
| `/` | Dashboard — personalized feed + trending bills | | `/` | Dashboard — personalized feed + trending bills |
| `/bills` | Browse all bills with search, chamber/topic filters, pagination | | `/bills` | Browse all bills with search, chamber/topic filters, pagination |
| `/bills/[id]` | Bill detail — brief with § citations, action timeline, news, trend chart | | `/bills/[id]` | Bill detail — brief with § citations, action timeline, news, trend chart, draft letter, notes |
| `/members` | Browse members of Congress, filter by chamber/party/state | | `/members` | Browse members of Congress, filter by chamber/party/state |
| `/members/[id]` | Member profile — bio, contact info, leadership roles, service history, sponsored bills, public interest trend chart, recent news | | `/members/[id]` | Member profile — bio, contact info, leadership roles, service history, sponsored bills, public interest trend chart, recent news |
| `/following` | User's followed bills, members, and topics | | `/following` | User's followed bills, members, and topics with accordion sections and topic filters |
| `/topics` | Browse and follow policy topics | | `/topics` | Browse and follow policy topics |
| `/collections` | User's collections (watchlists) — create, manage, share |
| `/collections/[id]` | Collection detail — bills in the collection, share link |
| `/notifications` | Notification settings — ntfy config, alert filters (per follow mode), quiet hours, digest mode, RSS |
| `/how-it-works` | Feature guide covering follow modes, collections, notifications, AI briefs, bill browsing |
| `/settings` | Admin panel (admin only) | | `/settings` | Admin panel (admin only) |
| `/login` | Email + password sign-in | | `/login` | Email + password sign-in |
| `/register` | Account creation | | `/register` | Account creation |
| `/share/brief/[token]` | Public shareable brief view — no sidebar, no auth required |
| `/share/collection/[token]` | Public shareable collection view — no sidebar, no auth required |
### Key Components ### Key Components
@@ -1100,6 +1159,39 @@ Nginx uses `resolver 127.0.0.11 valid=10s` (Docker's internal DNS) so upstream c
- Tailwind content scan extended to include `lib/` directory - Tailwind content scan extended to include `lib/` directory
- Nginx DNS resolver fix: prevents stale-IP 502s after container restarts - Nginx DNS resolver fix: prevents stale-IP 502s after container restarts
### v0.9.0 — Collections, Shareable Links & Notification Improvements
**Collections / Watchlists:**
- `collections` and `collection_bills` tables (migrations 0015/0016)
- Named, curated bill groups with public/private flag and UUID share token
- `CollectionPicker` popover on bill detail page — create or add to existing collections from the bookmark icon
- `/collections` page — list, create, manage collections
- `/collections/[id]` — collection detail with share link
- `GET /api/collections/share/{token}` — public read-only collection view
**Shareable Brief Links:**
- `share_token` UUID column on `bill_briefs` (migration 0016)
- Share button (Share2 icon) in `BriefPanel` copies a public link
- `/api/share/brief/{token}` and `/api/share/collection/{token}` — public router, no auth
- `/share/brief/[token]` and `/share/collection/[token]` — public frontend pages, no sidebar
- `AuthGuard` updated: `/collections` → AUTH_REQUIRED; `/share/` prefix → NO_SHELL (uses `.startsWith()`)
**Notification Improvements:**
- Quiet hours dispatch fix: events held during quiet window fire correctly on next run
- Digest mode: bundled ntfy summary on daily or weekly schedule instead of per-event pushes
- `send_notification_digest` Celery task with `digest_frequency: "daily" | "weekly"` setting
- Notification history panel on settings page split into direct follows vs topic follows
### v0.9.3 — Granular Per-Mode Alert Filters
- `categorize_action()` replaces `is_milestone_action()` / `is_referral_action()` — maps action text to one of 6 named categories: `vote`, `presidential`, `committee_report`, `calendar`, `procedural`, `referral`
- `action_category` stored in every `NotificationEvent.payload` going forward; `milestone_tier` retained for RSS/history backward compatibility
- `alert_filters` added to `notification_prefs` JSONB — nested dict: `{neutral: {...}, pocket_veto: {...}, pocket_boost: {...}}`, each with 8 boolean keys covering all event types (`new_document`, `new_amendment`, and the 6 action categories)
- Dispatcher `_should_dispatch()` checks `prefs["alert_filters"][follow_mode][key]` — replaces two hardcoded per-mode suppression blocks
- Notifications settings page: tabbed **Alert Filters** section (Follow / Pocket Veto / Pocket Boost tabs), each with 8 independent toggles, Milestones parent checkbox (indeterminate-aware), **Load defaults** revert button, and per-tab **Save** button
- How It Works page updated with accurate per-mode default alert sets and filter customization guidance
- No DB migration required — `alert_filters` stored in existing JSONB column
--- ---
## Deployment ## Deployment

227
DEPLOYING.md Normal file
View File

@@ -0,0 +1,227 @@
# Deploying PocketVeto
Step-by-step guide for standing up the full stack on a fresh server.
---
## Prerequisites
**Server:**
- Linux (Ubuntu 22.04+ or Debian 12 recommended)
- Docker Engine 24+ and Docker Compose v2 (`docker compose` — note: no hyphen)
- At least 2 GB RAM (4 GB recommended if running an Ollama LLM locally)
- Port 80 open to the internet (and 443 if you add SSL)
**API keys you will need:**
| Key | Where to get it | Required? |
|---|---|---|
| `DATA_GOV_API_KEY` | [api.data.gov/signup](https://api.data.gov/signup/) — free, instant | **Yes** |
| One LLM key (OpenAI / Anthropic / Gemini) | Provider dashboard | **Yes** (or use Ollama) |
| `NEWSAPI_KEY` | [newsapi.org](https://newsapi.org) — free tier (100 req/day) | Optional |
Google Trends (`pytrends`) needs no key.
---
## 1. Get the code
```bash
git clone https://git.jackhlevy.com/jack/civicstack.git
cd civicstack
```
---
## 2. Configure environment
```bash
cp .env.example .env
nano .env # or your preferred editor
```
**Minimum required values:**
```env
# Network
LOCAL_URL=http://YOUR_SERVER_IP # or https://yourdomain.com if behind SSL
PUBLIC_URL= # leave blank unless you have a public domain
# Auth — generate with: python -c "import secrets; print(secrets.token_hex(32))"
JWT_SECRET_KEY=your-generated-secret
# PostgreSQL
POSTGRES_USER=congress
POSTGRES_PASSWORD=your-strong-password
POSTGRES_DB=pocketveto
# Redis
REDIS_URL=redis://redis:6379/0
# Congress.gov + GovInfo (shared api.data.gov key)
DATA_GOV_API_KEY=your-api-key
# LLM — pick one
LLM_PROVIDER=openai
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o
```
Other providers (swap in place of the OpenAI block):
```env
# Anthropic
LLM_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
ANTHROPIC_MODEL=claude-sonnet-4-6
# Gemini
LLM_PROVIDER=gemini
GEMINI_API_KEY=AIza...
GEMINI_MODEL=gemini-2.0-flash
# Ollama (local model — server must be running on the host)
LLM_PROVIDER=ollama
OLLAMA_BASE_URL=http://host.docker.internal:11434
OLLAMA_MODEL=llama3.1
```
Optional extras:
```env
NEWSAPI_KEY=your-newsapi-key # enables richer news correlation
PYTRENDS_ENABLED=true # Google Trends; disable if hitting rate limits
CONGRESS_POLL_INTERVAL_MINUTES=30 # how often to check Congress.gov
```
---
## 3. Build and start
```bash
docker compose up --build -d
```
This will:
1. Pull base images (postgres, redis, nginx, node)
2. Build the API, worker, beat, and frontend images
3. Start all 7 containers
4. Run `alembic upgrade head` automatically inside the API container on startup
5. Seed the Celery Beat schedule in Redis
**First build takes 3–8 minutes** depending on your server. Subsequent builds are faster (Docker layer cache).
---
## 4. Verify it's running
```bash
docker compose ps
```
All services should show `Up`:
```
civicstack-api-1 Up
civicstack-beat-1 Up
civicstack-frontend-1 Up
civicstack-nginx-1 Up 0.0.0.0:80->80/tcp
civicstack-postgres-1 Up (healthy)
civicstack-redis-1 Up (healthy)
civicstack-worker-1 Up
```
Check the API health endpoint:
```bash
curl http://localhost/api/health
# → {"status":"ok","timestamp":"..."}
```
Open `http://YOUR_SERVER_IP` in a browser.
---
## 5. Create the admin account
Navigate to `http://YOUR_SERVER_IP/register` and create the first account.
**The first registered account automatically becomes admin.** All subsequent accounts are regular users. The admin account gets access to the Settings page with the pipeline controls, LLM switching, and user management.
---
## 6. Trigger initial data load
Log in as admin and go to **Settings**:
1. **Trigger Poll** — fetches bills updated in the last 60 days from Congress.gov (~5–10 minutes to complete)
2. **Sync Members** — syncs current Congress members (~2 minutes)
The Celery workers then automatically:
- Fetch bill text from GovInfo
- Generate AI briefs (rate-limited at 10/minute)
- Fetch news articles and calculate trend scores
You can watch progress in **Settings → Pipeline Status**.
---
## 7. Optional: Domain + SSL
If you have a domain name pointing to the server, add an SSL terminator in front of nginx. The simplest approach is Caddy as a reverse proxy:
```bash
# Install Caddy
apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install caddy
```
`/etc/caddy/Caddyfile`:
```
yourdomain.com {
reverse_proxy localhost:80
}
```
```bash
systemctl reload caddy
```
Caddy handles HTTPS certificates automatically via Let's Encrypt.
After adding SSL, update `.env`:
```env
PUBLIC_URL=https://yourdomain.com
```
Then rebuild the API so the new URL is used in notification payloads:
```bash
docker compose up --build -d api
```
---
## Useful commands
```bash
# View logs for a service
docker compose logs --tail=50 api
docker compose logs --tail=50 worker
docker compose logs -f worker # follow in real time
# Restart a service
docker compose restart worker
# Run a database query
docker compose exec postgres psql -U congress pocketveto
# Apply any pending migrations manually
docker compose exec api alembic upgrade head
# Open a Python shell inside the API container
docker compose exec api python
```
---
## Troubleshooting
See `TROUBLESHOOTING.md` for common issues (502 errors after rebuild, wrong postgres user, frontend changes not showing, etc.).

View File

@@ -1,93 +0,0 @@
### Phase 3 — Personal Workflow & Context
_Focus: Allowing users to organize data and adding the "Cross-Bill" intelligence layer._
- **Collections / Watchlists:** `collections` table for public/private bill groupings shareable via UUID tokens.
- **Personal Notes:** Private `bill_notes` table to store user stances, tags, and pinned insights on bill detail pages.
- **Cross-Bill Referencing (New):**
- **Extraction:** Update LLM prompts to identify and extract related bill identifiers (e.g., "H.R. 1234") from text.
- **Referential Table:** Implement `bill_references` to map source-to-target bill relationships with context quotes.
- **Ghost Hydration:** Create the `/api/admin/bills/hydrate` endpoint to fetch and analyze referenced bills not currently in the DB.
- **UI Hover Previews:** Use `Radix UI` popovers in the `BriefPanel` to show snapshots of referenced legislation without leaving the page.
- **Weekly Digest:** Celery beat task to dispatch 7-day summaries of followed bills via ntfy and RSS.
---
### Phase 4 — Accountability & Member Scoring
_Focus: Deep-diving into legislative performance and voting records._
- **Votes & Committees:** Fetch roll-call votes and committee actions from Congress.gov into a new `bill_votes` table.
- **Member Effectiveness Score:** Nightly Celery task calculating scores based on sponsored bills, advancement milestones, and "bills enacted" metrics.
- **Bipartisan Progress Metric (Proposed):** Enhance effectiveness scores by weighting co-sponsorship across party lines and committee movement in opposing chambers.
- **Representation Alignment:** Neutral view showing how member votes align with a user’s followed topics.
---
### Phase 5 — Systemic Intelligence
_Focus: Enhancing search and providing broader legislative context._
- **Search Improvements:** Add filters for bill type, status, chamber, and date; implement "Search within member bills".
- **Topic-Scoped Search:** Allow users to search legislation exclusively within followed topics like "Healthcare" or "Energy".
- **Member Effectiveness Transparency:** Display the "Member Effectiveness" formula on profile pages to maintain non-partisan trust.
---
### Phase 6 — Desktop & Polish
_Focus: Optimization for professional-grade power users._
- **Desktop View:** Multi-column layout with sticky sidebars and expanded grids optimized for large screens.
- **Notification Channels v2:** Expand beyond ntfy/RSS to include Discord webhooks, Telegram, and SMTP email.
- **Backfill Tasks:** Automate `first_name` and `last_name` population for all member records.
---
## 🛠️ Updated Technical Implementation Details
### Database Schema Updates
|**Table**|**New Columns / Purpose**|
|---|---|
|**`bill_references`**|`source_id`, `target_id`, `context_quote` to link related bills.|
|**`bill_notes`**|`user_id`, `bill_id`, `content`, `stance`, `tags`, `pinned`.|
|**`collections`**|`user_id`, `name`, `slug`, `is_public` for shared watchlists.|
|**`bill_votes`**|Stores roll-call data and results for accountability tracking.|
### Enhanced LLM Pipeline
The `llm_processor.py` task will now perform a dual-pass:
1. **Analysis:** Generate the standard brief with citations and fact/inference labels.
2. **Mapping:** Extract bill IDs found in the text and insert them into the `bill_references` table. If a target ID is missing from the `bills` table, it triggers a low-priority `hydrate_bill` task.
### Performance Guardrails
- **Selective Hydration:** The poller will only "Ghost Hydrate" referenced bills if they are from the current or previous Congress to prevent API exhaustion.
- **Cached Previews:** Referenced bill snapshots for hover tooltips will be cached in Redis to minimize database hits during dashboard scrolling.

View File

@@ -1,215 +0,0 @@
## Roadmap
- [x] Docker Stack — PostgreSQL, Redis, FastAPI, Celery, Next.js, Nginx fully containerized
- [x] Bill Polling — Congress.gov incremental sync every 30 min, filtered to legislation that can become law
- [x] Document Fetching — GovInfo bill text retrieval with smart truncation for token budgets
- [x] LLM Analysis — Multi-provider AI briefs (OpenAI, Anthropic, Gemini, Ollama) with amendment diffing
- [x] News Correlation — NewsAPI + Google News RSS articles linked to bills via topic tags
- [x] Trend Scoring — Composite zeitgeist score (0–100) from NewsAPI + Google News + Google Trends, nightly
- [x] Full-text Search — PostgreSQL tsvector search across bills and members
- [x] Follows — Per-user follows for bills, members, and topics
- [x] Dashboard — Personalized feed + trending bills
- [x] Multi-user Auth — JWT email/password auth, admin role, user management panel
- [x] Admin Panel — LLM provider switching, pipeline stats, manual task triggers
- [x] Citations — Every AI brief key point and risk cites the section + verbatim quote from bill text
- [x] Citation UI — § chips expand inline to show quote + GovInfo source link
- [x] Party Badges — Solid red/blue/slate badges readable in light and dark mode
- [x] Nginx DNS Fix — Resolver directive prevents stale-IP 502s after container restarts
- [x] Sponsor Linking — Poller fetches bill detail for sponsor; backfill task fixes existing bills
- [x] Member Search — "First Last" and "Last, First" both match via PostgreSQL split_part()
- [x] Search Spaces — Removed .trim() on keystroke that ate spaces in search inputs
- [x] Mobile UI — Responsive layout: sidebar collapses, cards stack, touch-friendly controls
- [x] Member BIO & Photo — Display member headshots (photo_url already stored, not yet shown in UI)
- [x] Bill Action Fetching — BillAction table populated via Congress.gov actions endpoint; nightly batch + event-driven on bill change
- [x] What Changed (Amendment Briefs) — BriefPanel surfaces amendment briefs with "What Changed" badge and collapsible version history
- [x] Source Viewer — "View source" link in § citation popover opens GovInfo document in new tab (Option A; Option B = in-app highlighted viewer deferred pending UX review)
- [x] Admin Reprocess — POST /api/admin/bills/{bill_id}/reprocess queues document + action fetches for a specific bill
- [x] LLM Model Picker — live model list fetched from each provider's API; custom model name fallback
- [x] Admin Health Panel — bill pipeline breakdown table, external API health tests with latency, manual controls with status dots + task polling
- [x] Chamber Badges — amber/gold for Senate, slate/silver for House; applied on bill cards and detail pages
- [x] Action History Fallback — shows latest_action_date/text while full history loads; full timeline once fetched
- [x] Backfill All Actions — admin task to fetch action history for all pre-existing bills
- [x] Notifications (Phase 1) — ntfy dispatch, RSS feed, per-user settings UI, 5-min dispatcher beat task
- [x] Brief Regeneration UI — admin button to delete existing briefs for a bill and re-queue LLM processing. Useful for improving citation/diff logic without a full re-poll. (Backend reprocess endpoint already exists.)
- [x] Follow Modes — `neutral | pocket_veto | pocket_boost` on the `follows` table; FollowButton mode selector with descriptions and tooltips
- [x] Public Browsing — unauthenticated guests browse bills, members, topics, and trending dashboard; AuthModal gates follow/interactive actions; sidebar and nav adapt to guest state
- [x] Draft Letter Generator — collapsible panel on bill detail pages; select up to 3 cited brief points, stance auto-fills from follow mode, recipient derived from chamber, ZIP optional and never stored; calls configured LLM provider
- [x] Bill Text Status Indicators — BillCard shows Brief / Pending / No text badge; backed by a single batch query on the list endpoint
---
## To Do
---
### Phase 2 — High Impact *(can run in parallel after Phase 1)*
- [x] **Change-driven Alerts** — milestone keywords expanded (markup, conference, referral tier); topic followers now receive bill_updated milestone events; committee referral events delivered to pocket_veto/boost but suppressed for neutral; all three follow types covered for both tiers.
- [x] **Fact vs Inference Labeling** — `label: "cited_fact" | "inference"` on every cited key_point and risk; prompt engineering updated for all providers; "Inferred" badge in citation UI; backfill task for existing briefs.
---
### Phase 3 — Personal Workflow
- [ ] **Collections / Watchlists** — `collections` (id, user_id, name, slug, is_public) + `collection_bills` join table. UI to create/manage collections and filter dashboard by collection. Shareable via public slug URL (read-only for non-owners).
- [x] **Personal Notes** — `bill_notes` table (user_id, bill_id, content, pinned). Collapsible panel on bill detail page. Pinned notes float above the brief. Private — auth-gated, never shown to guests.
- [ ] **Shareable Links** — UUID token on briefs and collections → public read-only view, no login required. Same token system for both. No expiry by default. UUID (not sequential) to prevent enumeration.
- [x] **Weekly Digest** — Celery beat task (Monday 8:30 AM UTC), queries followed bills for changes in the past 7 days, formats a low-noise summary, dispatches via ntfy + RSS. Admin can trigger immediately from Manual Controls.
---
### Phase 4 — Accountability
- [ ] **Votes & Committees** — fetch roll-call votes and committee referrals/actions from Congress.gov. New `bill_votes` table. UI: timeline entries for committee actions (already partially populated from bill actions) + vote results filterable by followed members and topics.
- [ ] **Member Effectiveness Score** — nightly Celery task; transparent formula: sponsored bills count, bills advanced through stages, co-sponsored, committee participation, "bills enacted" metric. Stored in `member_scores` table. Displayed on member profile with formula explanation.
- [ ] **Representation Alignment View** — for each followed member, show how their votes and actions align with the user's followed topics. Based purely on followed members (no ZIP/district storage). Neutral presentation — no scorecard dunking.
---
### Phase 5 — Polish *(slot in anytime, independent)*
- [ ] **Search Improvements** — filters on global search (bill type, status, chamber, date range); search within a member's sponsored bills; topic-scoped search.
- [ ] **Desktop View** — wider multi-column layout optimized for large screens (sticky sidebar, expanded grid, richer bill detail layout).
- [ ] **first_name / last_name Backfill** — Celery task to populate empty first/last from stored "Last, First" `name` field via split.
---
### Later / Backlog
- [ ] **Notification Channels v2** — email (SMTP), Discord webhook, Telegram bot (after ntfy + RSS v1 ships)
- [ ] **Source Viewer Option B** — in-app bill text viewer with cited passage highlighted and scroll-to-anchor. Deferred pending UX review of Option A (GovInfo link).
- [ ] **Raw Diff Panel** — Python `difflib` diff between stored document versions, shown as collapsible "Raw Changes" below amendment brief. Zero API calls. Deferred — AI amendment brief is the primary "what changed" story.
- [ ] **Shareable Collection Subscriptions** — "Follow this collection" mechanic so other users can subscribe to a public collection and get its bills added to their feed.
- [x] Pocket Veto mode (follow stance) — toggle on a bill to treat it as “I don’t want this to pass”; adds to watchlist and triggers milestone alerts
- [x] Follow modes — Neutral + Pocket Veto + Pocket Boost; FollowButton is a mode selector with explanation tooltips
- [ ] Pocket Veto notification rules — alert only on advancement milestones + failure outcomes (failed committee / failed floor / stalled) — notification dispatcher needs to filter by follow_mode
### PocketVeto function
#### How it should work (so it’s useful and not cringey)
Instead of “follow/unfollow,” each bill gets a **Follow Mode**:
- **Follow** (neutral): “Keep me posted on meaningful changes.”
- **Pocket Veto** (oppose): “Alert me if this bill is advancing toward passage.”
- (Optional later) **Pocket Boost** (support): “Alert me when action is needed / when it’s in trouble.” also suggest an action the user can take to let their representatives know that you support this bill.
For Pocket Veto specifically, the key is **threshold alerts**, not spam:
- **Committee referral**
- **Committee hearing scheduled**
- **Markup scheduled**
- **Reported out of committee**
- **Placed on calendar**
- **Floor vote scheduled**
- **Passed chamber**
- **Conference / reconciliation activity**
- **Sent to President**
- **Signed / Vetoed**
And the “failed” side:
- **Failed in committee**
- **Failed floor vote**
- **Stalled** (no action for X days while similar bills move)
#### Why it’s valuable for “normal people”
Most people don’t want to follow politics continuously. They want:
- “Tell me if the bad thing is about to happen.”
That’s exactly what Pocket Veto mode does.
#### Guardrail to keep it non-partisan / non-toxic
Make it explicit in UI copy:
- It’s a **personal alert preference**, not a moral label.
- It doesn’t publish your stance unless you share it.
#### Data model addition (simple)
Add fields to `follows` (or a new table):
- `follow_mode`: `neutral | pocket_veto | pocket_boost`
- `alert_sensitivity`: `low | medium | high` (optional)
Then alert rules can be:
- neutral: material changes
- pocket_veto: only “advancing toward passage” milestones
- pocket_boost: “action points” + milestones
Yes — that’s a solid idea **if it’s done as a “welcome + how it works” nudge**, not an annoying pop-up that blocks the UI.
A toast can work, but for a first-time user you’ll usually get better results with a **dismissible banner** or a **one-time “welcome” card** on the dashboard, because:
* toasts disappear (people miss them)
* first-run onboarding usually needs at least one click (“Got it” / “Start here”)
### Best pattern (low effort, high impact)
**First visit → show a dismissible Welcome card/banner** (top of dashboard) with:
* 1 sentence purpose
* 3 bullets of key features
* 2 buttons: **“Add my first follow”** and **“See a demo bill”**
* “Don’t show again” checkbox (or implicit on dismiss)
You can still use a toast, but make it:
* sticky until dismissed
* or paired with a banner/card
### What it should say (copy you can paste)
**Title:** Welcome to PocketVeto
**Body (tight):**
* Follow bills, members, or topics (low-noise)
* See *what changed* in plain English
* Verify every claim with **Back to Source** citations
**Buttons:** “Add a follow” | “Load demo”
### Implementation detail (no creepy tracking)
Store a simple flag:
* `localStorage.setItem("pv_seen_welcome", "1")`
Don’t store it server-side unless you already have user accounts and it’s part of preferences.
### Backlog item (checkboxes)
* [x] First-visit welcome UI (banner/card — guests only, shown on dashboard)
* [x] Dismiss + “don’t show again” (localStorage `pv_seen_welcome`)
* [x] CTA: Browse Bills
* [ ] CTA: Load demo data (optional)
* [ ] Link: “How it works” page/modal (optional)
If you tell me your UI stack (sounds like Next.js + shadcn/ui), I can give you a drop-in component for a clean welcome card + toast behavior.

112
ROADMAP.md Normal file
View File

@@ -0,0 +1,112 @@
# PocketVeto — Roadmap
> Consolidated feature roadmap and release history. Shipped items reflect what is live as of v0.9.3.
---
## Shipped
### Foundation & Core Pipeline
- [x] Docker Compose stack — PostgreSQL, Redis, FastAPI, Celery, Next.js, Nginx fully containerized
- [x] Bill polling — Congress.gov incremental sync, filtered to legislation that can become law (`hr`, `s`, `hjres`, `sjres`)
- [x] Document fetching — GovInfo bill text retrieval with smart truncation for LLM token budgets
- [x] LLM analysis — multi-provider AI briefs (OpenAI, Anthropic, Gemini, Ollama) with amendment diffing
- [x] Citations — every key point and risk cites the bill section + verbatim quote; `cited_fact` / `inference` label
- [x] Citation UI — § chips expand inline with quote + GovInfo source link; "Inferred" badge on analytical items
- [x] News correlation — NewsAPI + Google News RSS articles linked to bills via topic tags
- [x] Trend scoring — composite zeitgeist score (0–100) from NewsAPI + Google News + Google Trends, nightly
- [x] Full-text search — PostgreSQL tsvector across bills and members
- [x] Multi-user auth — JWT email/password, admin role, user management panel
- [x] Admin panel — LLM provider/model switching, pipeline stats, external API health, manual task triggers
- [x] Bill type filtering — only legislation that can become law; 60-day seed window on fresh install
### Member Profiles & Mobile
- [x] Member profiles — bio, contact info, leadership roles, service history, sponsored/cosponsored counts
- [x] Member interest scoring — composite trend score (NewsAPI + GNews + pytrends), nightly; lazy-loaded on first profile view
- [x] Member news — articles correlated to members via name search
- [x] Mobile UI — responsive layout, slide-in drawer, hamburger header, touch-friendly controls
### Follows & Personalization
- [x] Follows — per-user bill, member, and topic follows
- [x] Follow modes — `neutral | pocket_veto | pocket_boost` with mode-selector UI and descriptions
- [x] Dashboard — personalized feed from followed bills/members/topics + trending
- [x] Public browsing — unauthenticated guests browse bills, members, topics, and trending; `AuthModal` gates interactive actions
- [x] Welcome banner — dismissible onboarding card for guests; `localStorage` dismiss state
### Bill Detail Features
- [x] Bill action pipeline — full legislative action history from Congress.gov; nightly batch + event-driven on change
- [x] Action timeline — full history with latest-action fallback while history loads
- [x] Amendment briefs — "What Changed" badge; collapsible version history panel
- [x] Bill text status indicators — Brief / Pending / No text badge on BillCard (single batch query)
- [x] Draft letter generator — select up to 3 cited brief points, generate constituent letter via configured LLM; ZIP not stored
- [x] Personal notes — private per-bill note with pin, auto-resize textarea; one note per user per bill
- [x] Sponsor linking — poller fetches bill detail for sponsor; backfill task for existing bills
- [x] "No bill text" state — shown on detail page when GovInfo has nothing published
### Collections & Sharing
- [x] Collections / Watchlists — named groups of bills, public/private, UUID share token; `CollectionPicker` popover on bill detail
- [x] Shareable brief links — `share_token` on `bill_briefs`; public `/share/brief/[token]` page, no login required
- [x] Shareable collection links — public `/share/collection/[token]` page, no login required
### Notifications
- [x] ntfy push — Celery dispatcher POSTs to user's ntfy topic; optional token/basic auth
- [x] RSS feed — private tokenized XML feed per user; always real-time
- [x] Quiet hours — local-time window (IANA timezone auto-detected by browser); events queued and sent as batch after window
- [x] Digest mode — bundled ntfy summary on daily or weekly schedule instead of per-event pushes
- [x] Weekly digest — Monday 8:30 AM UTC; 7-day summary of followed bill activity via ntfy + RSS
- [x] Change-driven alerts — `categorize_action()` maps action text to 6 named categories; all three follow types (bill, sponsor, topic) covered
- [x] Granular per-mode alert filters — 8 independently toggleable alert types per follow mode (Follow / Pocket Veto / Pocket Boost); preset defaults; tabbed UI with per-tab save
### UX & Polish
- [x] Party badges — solid red/blue/slate, readable in light and dark mode
- [x] Chamber badges — amber/gold for Senate, slate/silver for House
- [x] Fact vs inference labeling — `cited_fact` / `inference` on every cited point; backfill task for existing briefs
- [x] Following page — accordion sections, search, topic filters
- [x] Member search — "First Last" and "Last, First" both match via PostgreSQL `split_part()`
- [x] How It Works page — feature guide covering follow modes, alert filter customization, collections, notifications, AI briefs
---
## Up Next
### Phase 4 — Accountability
- [ ] **Votes & Committees** — fetch roll-call votes from Congress.gov into a `bill_votes` table. UI: vote results filterable by followed members and topics; timeline entries for committee actions.
- [ ] **Member Effectiveness Score** — nightly Celery task; transparent formula: sponsored bills, bills advanced through stages, co-sponsorships, committee participation, bills enacted. Stored in `member_scores`. Displayed on member profile with formula explanation.
- [ ] **Representation Alignment View** — for each followed member, show how their votes and actions align with the user's followed topics. Neutral presentation — no scorecard framing.
### Phase 5 — Search & Polish
- [ ] **Search Improvements** — filters on global search (bill type, status, chamber, date range); search within a member's sponsored bills; topic-scoped search.
- [ ] **Desktop View** — wider multi-column layout for large screens: sticky sidebar, expanded grid, richer bill detail.
- [ ] **first_name / last_name Backfill** — Celery task to populate empty first/last from stored "Last, First" `name` field via split (affects ~10% of member records).
### Backlog
- [ ] **Notification Channels v2** — email (SMTP), Discord webhook, Telegram bot.
- [ ] **Cross-Bill Referencing** — extract referenced bill IDs from LLM briefs; `bill_references` table; hover preview popover in BriefPanel for referenced legislation.
- [ ] **Source Viewer Option B** — in-app bill text viewer with cited passage highlighted and scroll-to-anchor. Deferred pending UX review of the current GovInfo-link approach.
- [ ] **Raw Diff Panel** — `difflib` diff between stored document versions, shown as collapsible "Raw Changes" below the amendment brief. Zero API calls.
- [ ] **Shareable Collection Subscriptions** — "Follow this collection" so other users can subscribe to a public collection and get its bills added to their feed.
---
## v1.0 Definition
v1.0 ships when the following are all live:
- [x] Full bill pipeline (poll → fetch text → LLM brief → news → trend score)
- [x] Cited AI briefs with fact/inference labels
- [x] Follow modes (neutral / pocket_veto / pocket_boost)
- [x] Granular per-mode notification filters
- [x] Push (ntfy) + RSS + digest + quiet hours
- [x] Collections with share links
- [x] Shareable brief links
- [x] Personal notes
- [x] Draft letter generator
- [x] Public browsing (no account required to read)
- [x] Multi-user auth with admin panel
- [ ] Member effectiveness score
- [ ] Roll-call vote data
All other items above are post-v1.0.

253
UPDATING.md Normal file
View File

@@ -0,0 +1,253 @@
# Updating PocketVeto — Remote Server Setup & Deployment Workflow
How to push new code from your development machine and pull it on the production server.
---
## Overview
The workflow is:
```
Local machine → git push → git.jackhlevy.com → (pull on server) → docker compose up --build -d
```
You develop locally, push to the Gitea remote, then update the production server — either manually over SSH or via an automated webhook.
---
## 1. SSH access to the production server
Make sure you can SSH into the server without a password:
```bash
# On your local machine — generate a key if you don't have one
ssh-keygen -t ed25519 -C "pocketveto-deploy"
# Copy the public key to the server
ssh-copy-id user@YOUR_SERVER_IP
```
Test:
```bash
ssh user@YOUR_SERVER_IP "echo ok"
```
---
## 2. Server: clone the repo and authenticate
On the server, clone from your Gitea instance:
```bash
ssh user@YOUR_SERVER_IP
cd /opt # or wherever you want to host it
git clone https://git.jackhlevy.com/jack/civicstack.git
cd civicstack
```
If your Gitea repo is private, create a **deploy token** in Gitea:
- Gitea → Repository → Settings → Deploy Keys → Add Deploy Key (read-only is fine)
- Or: Gitea → User Settings → Applications → Generate Token
Store credentials so `git pull` doesn't prompt:
```bash
# Using a personal access token stored in the URL
git remote set-url origin https://YOUR_TOKEN@git.jackhlevy.com/jack/civicstack.git
```
Verify:
```bash
git pull # should succeed with no password prompt
```
---
## 3. Option A — Manual update (simplest)
SSH in and run:
```bash
ssh user@YOUR_SERVER_IP
cd /opt/civicstack
git pull origin main
docker compose up --build -d
```
That's it. Docker rebuilds only the images that changed (layer cache means unchanged services rebuild in seconds). Migrations run automatically when the API container restarts.
**One-liner from your local machine:**
```bash
ssh user@YOUR_SERVER_IP "cd /opt/civicstack && git pull origin main && docker compose up --build -d"
```
---
## 4. Option B — Deploy script
Create `/opt/civicstack/deploy.sh` on the server:
```bash
#!/bin/bash
set -e
cd /opt/civicstack
echo "==> Pulling latest code"
git pull origin main
echo "==> Building and restarting containers"
docker compose up --build -d
echo "==> Done. Current status:"
docker compose ps
```
```bash
chmod +x /opt/civicstack/deploy.sh
```
Now from your local machine:
```bash
ssh user@YOUR_SERVER_IP /opt/civicstack/deploy.sh
```
---
## 5. Option C — Automated webhook (Gitea → server)
This triggers a deploy automatically every time you push to `main`.
### 5a. Create a webhook listener on the server
Install a simple webhook runner. The easiest is [`webhook`](https://github.com/adnanh/webhook):
```bash
apt install webhook
```
Create `/etc/webhook/hooks.json`:
```json
[
{
"id": "civicstack-deploy",
"execute-command": "/opt/civicstack/deploy.sh",
"command-working-directory": "/opt/civicstack",
"response-message": "Deploying...",
"trigger-rule": {
"match": {
"type": "payload-hmac-sha256",
"secret": "your-webhook-secret",
"parameter": { "source": "header", "name": "X-Gitea-Signature-256" }
}
}
}
]
```
Start the webhook service:
```bash
# Test it first
webhook -hooks /etc/webhook/hooks.json -port 9000 -verbose
# Or create a systemd service (recommended)
```
`/etc/systemd/system/webhook.service`:
```ini
[Unit]
Description=Webhook listener for civicstack deploys
After=network.target
[Service]
ExecStart=/usr/bin/webhook -hooks /etc/webhook/hooks.json -port 9000
Restart=on-failure
User=root
[Install]
WantedBy=multi-user.target
```
```bash
systemctl enable --now webhook
```
Expose port 9000 (or proxy it through nginx/Caddy at a path like `/hooks/`).
### 5b. Add the webhook in Gitea
- Gitea → Repository → Settings → Webhooks → Add Webhook → Gitea
- **Target URL:** `http://YOUR_SERVER_IP:9000/hooks/civicstack-deploy`
- **Secret:** same value as `your-webhook-secret` above
- **Trigger:** Push events → branch `main`
Now every `git push origin main` automatically triggers a deploy.
---
## 6. Checking the deployed version
After any update you can confirm what's running:
```bash
# Check the git commit on the server
ssh user@YOUR_SERVER_IP "cd /opt/civicstack && git log --oneline -3"
# Check container status
ssh user@YOUR_SERVER_IP "cd /opt/civicstack && docker compose ps"
# Hit the health endpoint
curl http://YOUR_SERVER_IP/api/health
```
---
## 7. Rolling back
If a bad deploy goes out:
```bash
ssh user@YOUR_SERVER_IP
cd /opt/civicstack
# Roll back to the previous commit
git revert HEAD --no-edit # preferred — creates a revert commit, keeps history clean
# Or hard reset if you're sure (discards the bad commit locally — use with care)
# git reset --hard HEAD~1
git push origin main # if using Option C, this triggers a new deploy automatically
docker compose up --build -d # if manual
```
---
## 8. Environment and secrets
`.env` is **not** tracked in git. If you need to update a secret or add a new API key on the server:
```bash
ssh user@YOUR_SERVER_IP
nano /opt/civicstack/.env
# Then restart only the affected services (usually api + worker)
cd /opt/civicstack
docker compose up -d --no-build api worker beat
```
`--no-build` skips the rebuild step — only a config reload is needed for env var changes.
---
## Summary
| Goal | Command |
|---|---|
| Manual deploy | `ssh server "cd /opt/civicstack && git pull && docker compose up --build -d"` |
| One-step deploy script | `ssh server /opt/civicstack/deploy.sh` |
| Automated on push | Gitea webhook → webhook listener → `deploy.sh` |
| Rollback | `git revert HEAD` + redeploy |
| Update env only | Edit `.env` on server + `docker compose up -d --no-build api worker beat` |
| Check what's running | `ssh server "cd /opt/civicstack && git log --oneline -1 && docker compose ps"` |

View File

@@ -5,6 +5,7 @@ import {
Calendar, Calendar,
Clock, Clock,
FileText, FileText,
Filter,
Heart, Heart,
HelpCircle, HelpCircle,
Rss, Rss,
@@ -82,29 +83,46 @@ export default function HowItWorksPage() {
{/* Following */} {/* Following */}
<Section id="follow" title="Following bills" icon={Heart}> <Section id="follow" title="Following bills" icon={Heart}>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Follow any bill to track it. PocketVeto checks for changes — new text, amendments, status Follow any bill to track it. PocketVeto checks for changes and notifies you through your
updates — and notifies you through your configured channels. Three modes let you tune the configured channels. Three modes let you tune the signal to your interest level — each
signal to your interest level. with its own independent set of alert filters.
</p> </p>
<div className="space-y-3"> <div className="space-y-3">
<Item icon={Heart} color="bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400" title="Following (neutral)"> <Item icon={Heart} color="bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400" title="Follow">
Alerts on all material changes: new text published, amendments filed, and status updates The standard mode. Default alerts: new bill text, amendments filed, chamber votes,
like floor votes or committee referrals. presidential action, and committee reports.
</Item> </Item>
<Item icon={Shield} color="bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400" title="Pocket Veto"> <Item icon={Shield} color="bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400" title="Pocket Veto">
Alerts only when the bill advances toward becoming law — new official text, floor For bills you oppose and only want to hear about if they gain real traction. Default
scheduling, or committee passage. Useful when you oppose a bill and only need to act alerts: chamber votes and presidential action only — no noise from early committee or
if it gains traction. document activity.
</Item> </Item>
<Item icon={Zap} color="bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" title="Pocket Boost"> <Item icon={Zap} color="bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" title="Pocket Boost">
All the alerts of neutral mode, plus periodic reminders to contact your representative. For bills you actively support. Default alerts: everything — new text, amendments,
Use this when you actively support a bill and want to stay engaged. votes, presidential action, committee reports, calendar placement, procedural moves,
and committee referrals. Also adds &ldquo;Find Your Rep&rdquo; action buttons to push
notifications.
</Item> </Item>
</div> </div>
<div className="rounded-md bg-muted/60 px-4 py-3 space-y-1.5">
<p className="text-xs font-medium flex items-center gap-1.5">
<Filter className="w-3.5 h-3.5" /> Adjusting alert filters
</p>
<p className="text-xs text-muted-foreground leading-relaxed">
The defaults above are starting points. In{" "}
<Link href="/notifications" className="text-primary hover:underline">Notifications → Alert Filters</Link>,
each mode has its own tab with eight independently toggleable alert types. For example,
a Follow bill where you don&apos;t care about committee reports — uncheck it and only
that mode is affected. Hit <strong>Load defaults</strong> on any tab to revert to the
preset above.
</p>
</div>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
You can also follow <strong>members</strong> (get alerts when they sponsor new bills) and{" "} You can also follow <strong>members</strong> (alerts when they sponsor new bills) and{" "}
<strong>topics</strong> (get alerts when new bills matching that topic are briefed). <strong>topics</strong> (alerts when new bills matching that topic are briefed).
Member and topic follows use the neutral mode only. Member and topic follows use the Follow mode filters.
</p> </p>
</Section> </Section>

View File

@@ -49,7 +49,7 @@ const MODES = [
key: "neutral", key: "neutral",
label: "Follow", label: "Follow",
description: "Bills you follow in standard mode", description: "Bills you follow in standard mode",
preset: { new_document: true, new_amendment: false, vote: true, presidential: true, committee_report: true, calendar: false, procedural: false, referral: false }, preset: { new_document: true, new_amendment: true, vote: true, presidential: true, committee_report: true, calendar: false, procedural: false, referral: false },
}, },
{ {
key: "pocket_veto", key: "pocket_veto",