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:
114
ARCHITECTURE.md
114
ARCHITECTURE.md
@@ -312,7 +312,7 @@ News articles correlated to a specific member of Congress.
|
||||
| email | varchar (unique) | |
|
||||
| hashed_password | varchar | bcrypt |
|
||||
| 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 |
|
||||
| created_at | timestamptz | |
|
||||
|
||||
@@ -418,11 +418,39 @@ Stores notification events for dispatching to user channels (ntfy, RSS).
|
||||
| user_id | int (FK → users, CASCADE) | |
|
||||
| bill_id | varchar (FK → bills, SET NULL) | nullable |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| `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)` |
|
||||
| `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`.
|
||||
|
||||
@@ -533,6 +563,26 @@ Auth header: `Authorization: Bearer <jwt>`
|
||||
| POST | `/test/follow-mode` | Required | Simulate a follow-mode notification to preview delivery behavior. |
|
||||
| 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`
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
@@ -610,12 +660,15 @@ Auth header: `Authorization: Bearer <jwt>`
|
||||
has no sponsor data), upserts Member, sets bill.sponsor_id
|
||||
↳ New bills → fetch_bill_documents.delay(bill_id)
|
||||
↳ Updated bills → fetch_bill_documents.delay(bill_id) if changed
|
||||
↳ Updated bills → emit bill_updated notification if action is a milestone:
|
||||
- "progress" tier: passed/failed, signed/vetoed, enacted, markup, conference,
|
||||
reported from committee, placed on calendar, cloture, roll call
|
||||
→ all follow types (bill, sponsor, topic) receive notification
|
||||
- "referral" tier: referred to committee
|
||||
→ pocket_veto and pocket_boost only; neutral follows silently skip
|
||||
↳ Updated bills → emit bill_updated notification if categorize_action() returns a category:
|
||||
- vote: passed, failed, agreed to, roll call
|
||||
- presidential: signed, vetoed, enacted, presented to the president
|
||||
- committee_report: markup, ordered to be reported, ordered reported, reported by, discharged
|
||||
- calendar: placed on
|
||||
- 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)
|
||||
↳ Gets text versions from Congress.gov (XML preferred, falls back to HTML/PDF)
|
||||
@@ -755,14 +808,20 @@ class ReverseBrief:
|
||||
|---|---|
|
||||
| `/` | Dashboard — personalized feed + trending bills |
|
||||
| `/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/[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 |
|
||||
| `/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) |
|
||||
| `/login` | Email + password sign-in |
|
||||
| `/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
|
||||
|
||||
@@ -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
|
||||
- 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
|
||||
|
||||
Reference in New Issue
Block a user