- Add bill_notes table schema (migration 0014)
- Add missing migrations 0012 and 0013 to the migrations table
- Add /api/notes endpoints section
- Add ntfy test, RSS test, follow-mode test, and history endpoints to /api/notifications
- Add POST /trigger-weekly-digest to admin API table
- Add weekly digest Monday beat schedule entry
- Update users.notification_prefs to document timezone field
- Update notifications.event_type to include weekly_digest
- Add NotesPanel.tsx to Frontend Key Components
- Add v0.7.0 (weekly digest + local-time quiet hours) to Feature History
- Add v0.8.0 (personal notes) to Feature History
Authored-By: Jack Levy
- bill_notes table (migration 0014): user_id, bill_id, content, pinned,
created_at, updated_at; unique constraint (user_id, bill_id)
- BillNote SQLAlchemy model with back-refs on User and Bill
- GET/PUT/DELETE /api/notes/{bill_id} — auth-required, one note per (user, bill)
- NotesPanel component: collapsible, auto-resize textarea, pin toggle,
save + delete; shows last-saved date and pin indicator in collapsed header
- Pinned notes render above BriefPanel; unpinned render below DraftLetterPanel
- Guests see nothing (token guard in component + query disabled)
Co-Authored-By: Jack Levy
Weekly Digest (send_weekly_digest Celery task):
- Runs every Monday 8:30 AM UTC via beat schedule
- Queries all followed bills updated in the past 7 days per user
- Sends low-priority ntfy push (Priority: low, Tags: newspaper,calendar)
- Creates a NotificationEvent (weekly_digest type) for RSS feed visibility
- Admin can trigger immediately via POST /api/admin/trigger-weekly-digest
- Manual Controls panel now includes "Send Weekly Digest" button
Local-time quiet hours:
- Browser auto-detects IANA timezone via Intl.DateTimeFormat().resolvedOptions().timeZone
- Timezone saved to notification_prefs alongside quiet_hours_start/end on Save
- Dispatcher converts UTC → user's local time (zoneinfo stdlib) before hour comparison
- Falls back to UTC if timezone absent or unrecognised
- Quiet hours UI: 12-hour AM/PM selectors, shows detected timezone as hint
- Clearing quiet hours also clears stored timezone
Co-Authored-By: Jack Levy
- Add DraftLetterPanel: collapsible UI below BriefPanel for bills with a
brief; lets users select up to 3 cited points, pick stance/tone, and
generate a plain-text letter via the configured LLM provider
- Stance pre-fills from follow mode (pocket_boost → YES, pocket_veto → NO)
and clears when the user unfollows; recipient derived from bill chamber
- Add POST /api/bills/{bill_id}/draft-letter endpoint with proper LLM
provider/model resolution from AppSetting (respects Settings page choice)
- Add generate_text() to LLMProvider ABC and all four providers
- Expose has_document on BillSchema (list endpoint) via a single batch
query; BillCard shows Brief / Pending / No text indicator per bill
Authored-By: Jack Levy
- Add get_optional_user dependency; dashboard returns guest-safe payload
- AuthGuard only redirects /following and /notifications for guests
- Sidebar hides auth-required nav items and shows Sign In/Register for guests
- Dashboard shows trending bills as "Most Popular" for unauthenticated visitors
- FollowButton opens AuthModal instead of acting when not signed in
- Members page pins followed members at the top for quick unfollowing
- useFollows skips API call and invalidates dashboard on follow/unfollow
Authored-By: Jack Levy
Test notification:
- Click header -> {PUBLIC_URL}/notifications so tapping the test opens the app
Real bill alerts (dispatcher):
- Title reformatted: "New Bill Text: HR 1234" (event type + bill identifier)
- Body: bill full name on first line, AI summary below (300 chars)
- Tags updated per event type (page, memo, siren) instead of generic scroll
- Click header was already set from bill_url; no change needed there
Authored-By: Jack Levy
HTTP headers are ASCII-only; the em dash in "PocketVeto — Test Notification"
caused a UnicodeEncodeError on every test attempt. Replaced with colon.
Frontend catch blocks now extract the real server error detail from the
axios response body instead of showing a generic fallback message.
Authored-By: Jack Levy
- POST /api/notifications/test/ntfy — sends a real push using current form
values (not saved settings) so auth can be verified before saving; returns
status + HTTP detail on success or error message on failure
- POST /api/notifications/test/rss — confirms the feed token exists and
returns event count; no bill FK required
- NtfyTestRequest + NotificationTestResult schemas added
- Frontend: Test button next to Save on both ntfy and RSS sections; result
shown inline as a green/red pill; uses current form state for ntfy so
the user can test before committing
All future notification types should follow the same test-before-save pattern.
Authored-By: Jack Levy
Notifications:
- New /notifications page accessible to all users (ntfy + RSS config)
- ntfy now supports no-auth, Bearer token, and HTTP Basic auth (for ACL-protected self-hosted servers)
- RSS enabled/disabled independently of ntfy; token auto-generated on first GET
- Notification settings removed from admin-only Settings page; replaced with link card
- Sidebar adds Notifications nav link for all users
- notification_dispatcher.py: fan-out now marks RSS events dispatched independently
Action history:
- Migration 0012: deduplicates existing bill_actions rows and adds UNIQUE(bill_id, action_date, action_text)
- congress_poller.py: replaces existence-check inserts with ON CONFLICT DO NOTHING (race-condition safe)
- Added backfill_all_bill_actions task (no date filter) + admin endpoint POST /backfill-all-actions
Authored-By: Jack Levy
- Add chamberBadgeColor util: amber/gold for Senate, slate/silver for House
- Apply chamber badge to BillCard and bill detail header
- ActionTimeline: show latest_action_date/text as fallback when full history
not yet fetched, with note that full history loads in background
- Manual Controls: poll task status every 5s after triggering, show spinning
indicator while running, task ID prefix, and completion/failure state
Authored-By: Jack Levy
- Fetch bill actions from Congress.gov and populate the action timeline
- Add nightly batch task and beat schedule for active bill actions
- Add admin reprocess endpoint for per-bill debugging
- Add BriefPanel with "What Changed" view and version history
- Add External API Health section with per-source latency testing
- Redesign Manual Controls as health panel with status dots and descriptions
- Add Resume Analysis task for stalled LLM jobs
- Add Backfill Dates & Links task for bills with null metadata
- Fix LLM provider/model DB overrides being ignored (env vars used instead)
- Fix Gemini 404: gemini-1.5-pro deprecated → gemini-2.0-flash
- Fix Anthropic models list: use REST API directly (SDK too old for .models)
- Replace test-LLM full analysis with lightweight ping (max_tokens=20)
- Add has_document field to BillDetail; show "No bill text published" state
- Fix "Introduced: —" showing for bills with null introduced_date
- Add bills_missing_sponsor and bills_missing_metadata to admin stats
- Add GovInfo health check using /collections endpoint (fixes 500 from /packages)
Authored-By: Jack Levy
- get_llm_provider() now accepts provider + model args so DB overrides
propagate through to all provider constructors (was always reading
env vars, ignoring the admin UI selection)
- /test-llm replaced with lightweight ping (max_tokens=20) instead of
running a full bill analysis; shows model name + reply, no truncation
- /api/settings/llm-models endpoint fetches available models live from
each provider's API (OpenAI, Anthropic REST, Gemini, Ollama)
- Admin UI model picker dynamically populated from provider API;
falls back to manual text input on error; Custom model name option kept
- Default Gemini model updated: gemini-1.5-pro → gemini-2.0-flash
Co-Authored-By: Jack Levy
When a bill page opens with no stored articles, the backend queues a
fetch_news_for_bill Celery task and returns immediately. Added a retry
loop (up to 3x, 6 s apart) driven off newsArticles state so articles
populate without a manual refresh. Fixed broken useEffect dependency
([billId] → [newsArticles]) that caused the timer to never fire.
News is now fetched via a separate useBillNews query (staleTime: 0)
independent of the cached bill detail response.
Co-Authored-By: Jack Levy
Backend:
- Add fetch_bill_actions task with pagination and idempotent upsert
- Add fetch_actions_for_active_bills nightly batch (4 AM UTC beat schedule)
- Wire fetch_bill_actions into new-bill creation and _update_bill_if_changed
- Add backfill_brief_citations task: detects pre-citation briefs by JSONB
type check, deletes them, re-queues LLM processing against stored text
(LLM calls only — zero Congress.gov or GovInfo calls)
- Add admin endpoints: POST /bills/{id}/reprocess, /backfill-citations,
/trigger-fetch-actions; add uncited_briefs count to /stats
Frontend:
- New BriefPanel component: wraps AIBriefCard, adds amber "What Changed"
badge for amendment briefs and collapsible version history with
inline brief expansion
- Swap AIBriefCard for BriefPanel on bill detail page
- Admin panel: Backfill Citations + Fetch Bill Actions buttons; amber
warning in stats when uncited briefs remain
- Add feature roadmap document with phased plan through Phase 5
Co-Authored-By: Jack Levy
- Replace fragile entry.get("link") with _gnews_entry_url() helper that
checks entry.link attribute then falls back to entry.links[].href,
fixing cases where feedparser puts the URL in a non-standard location
- Lazy news re-fetch on bill detail now only triggers when the stored
trend score confirms gnews_count > 0, preventing endless re-queuing
for bills with genuinely no news coverage
Co-Authored-By: Jack Levy
- Drop global unique constraint on news_articles.url; replace with
(bill_id, url) so the same article can appear for multiple bills
- news_fetcher dedup now scoped to bill_id instead of global URL
- Bill detail endpoint triggers a background news fetch when no
articles are stored, so gnews articles surface on next load
Migration 0009.
Co-Authored-By: Jack Levy
Adds Google Trends, NewsAPI, and Google News RSS scoring for members,
mirroring the existing bill interest pipeline. Member profiles now show
a Public Interest chart (with signal breakdown) and a Related News panel.
Key changes:
- New member_trend_scores + member_news_articles tables (migration 0008)
- fetch_gnews_articles() added to news_service for unlimited RSS article storage
- Bill news fetcher now combines NewsAPI + Google News RSS (more coverage)
- New member_interest Celery worker with scheduled news + trend tasks
- GET /members/{id}/trend and /news API endpoints
- TrendChart redesigned with signal breakdown badges and bar+line combo chart
- NewsPanel accepts generic article shape (bills and members)
Co-Authored-By: Jack Levy
Lazy-enriches member profiles on first view via Congress.gov detail API.
Adds office address, phone, official website, congress.gov link, birth
year, terms history, leadership roles, and sponsored/cosponsored counts.
Includes DB migration 0007 for new member columns.
Co-Authored-By: Jack Levy
Adds MobileHeader with hamburger button (left-aligned) that opens a
slide-in sidebar drawer on mobile. Desktop layout is unchanged. All
hardcoded multi-column grids updated with responsive Tailwind breakpoints.
Co-Authored-By: Jack Levy
- Poller now fetches bill detail on insert to get sponsor (list endpoint
has no sponsor data)
- Add backfill_sponsor_ids task + admin endpoint + UI button to fix the
1,616 existing bills with NULL sponsor_id
- Member name search now matches both "Last, First" and "First Last"
using split_part() on the stored name column; same fix applied to
global search
- Load Bill.sponsor relationship eagerly in get_member_bills to prevent
MissingGreenlet error during Pydantic serialization
- Remove .trim() on search onChange so spaces can be typed
Authored-By: Jack Levy
Covers full stack, database schema, API endpoints, Celery pipeline,
LLM service design, frontend structure, auth, deployment, and feature
history through v0.2.0.
Authored-By: Jack Levy
Party badge colors defined in lib/utils.ts were being stripped from the
production CSS bundle because the lib/ directory was missing from the
Tailwind content glob.
Authored-By: Jack Levy
Replaces light/dark variant classes (which produced unreadable pale pink
in dark mode) with solid saturated colors: red-600 for Republicans,
blue-600 for Democrats, slate-500 for Independents — all with white text.
Authored-By: Jack Levy
Without resolver 127.0.0.11, nginx caches upstream IPs at startup and
returns 502 after container restarts until manually reloaded.
Authored-By: Jack Levy
LLM prompts updated to output {text, citation, quote} objects for every
key_point and risk. govinfo_url stored on BillBrief (migration 0006) so
the frontend can link directly to the source document without an extra
query. AIBriefCard renders § citation chips that expand inline to show
the verbatim quote and a View source → GovInfo link. Old plain-string
briefs continue to render unchanged.
Authored-By: Jack Levy
- GET /api/admin/stats returns total bills, docs fetched, briefs generated
(full + amendment), and remaining count
- Admin page shows stat cards + progress bar, auto-refreshes every 30s
Authored-By: Jack Levy
- Drop hres/sres/hconres/sconres (simple/concurrent resolutions) from poller;
only track hr, s, hjres, sjres (legislation that can become law)
- On first run, seed from 60 days back instead of full congress history,
keeping the bill count to ~1,600 instead of 13,000+
Authored-By: Jack Levy
- User model with email/hashed_password/is_admin/notification_prefs
- JWT auth: POST /api/auth/register, /login, /me
- First registered user auto-promoted to admin
- Migration 0005: users table + user_id FK on follows (clears global follows)
- Follows, dashboard, settings, admin endpoints all require authentication
- Admin endpoints (settings writes, celery triggers) require is_admin
- Frontend: login/register pages, Zustand auth store (localStorage persist)
- AuthGuard component gates all app routes, shows app shell only when authed
- Sidebar shows user email + logout; Admin nav link visible to admins only
- Admin panel (/settings): user list with delete + promote/demote, LLM config,
data source settings, and manual celery controls
Authored-By: Jack Levy