Status Trio
Status Trio는 macOS 메뉴바 앱으로 Wi‑Fi·배터리·볼륨을 하나의 아이콘에 통합합니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기A app that gives US citizens a simpler path to understand and connect with their government
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A non-partisan civic-engagement web and mobile platform that connects U.S. citizens with their elected representatives — and gives election candidates an equal-footing surface for running at the local, state, and federal level.
Beyond connection, the app helps citizens find and track their federal, state, and local representatives — including the bills those representatives sponsor, the votes they cast, the committees they sit on, the executive orders they sign (when applicable), and the public statements they make. Representatives and candidates in turn get verified pages where they can post, run polls, manage events, and engage their constituents through a moderated, district-scoped channel.
CivicView is filed as a Florida Benefit Corporation (initial Profit Corporation filing on the books — tracking #800474911808; Benefit Corp Amendment filed 2026-06-12). No ads, no venture capital, no partisan agenda. The revenue model is a $5/month consumer subscription for creating polls; browsing is free forever and commenting requires only identity verification.
Phase: Live in demo mode. civicview.app, the installable desktop PWA, Google Play and the Microsoft Store all serve the live site, with demo accounts. The verified-account system + paid subscription flow are scaffolded end-to-end on the backend and frontend but inert until external accounts (ID.me, Stripe live mode) come online — gated on the Indiegogo campaign launch. See the Launch sequence section below for the staged rollout plan.
CivicView is built and maintained by Jeffrey De La Nuez ([email protected]) — sole creator, founder, and owner of the business.
The project was developed with major assistance from Anthropic's Claude AI suite:
The architecture decisions, product direction, content moderation policies, business structure, and accountability live with the founder. Claude accelerated the work; it did not own it.
CivicView enforces a tiered engagement model. The same person can hold up to three independent identities concurrently (citizen + verified rep + verified candidate), each with its own permission scope.
| Action | Free / unverified citizen | ID.me verified | ID.me verified + subscribed ($5/mo) |
|---|---|---|---|
| Browse representatives, bills, votes, executive orders | ✅ | ✅ | ✅ |
| Track reps / bills / elections (alerts when officials you track post or schedule events, and election reminders; no bill alerts yet) | ✅ | ✅ | ✅ |
| Like / dislike posts and polls | — | ✅ | ✅ |
| Vote on polls | — | ✅ | ✅ |
| Comment on posts and polls | — | ✅ | ✅ |
| Create polls (on poll page or unclaimed rep / candidate pages) | — | — | ✅ |
Verification confirms the citizen is a real U.S. person at a real address (state + congressional district), which is the foundation for the "verified constituent" claim every engagement carries.
Sitting elected officials can claim their auto-generated page through a verification flow that uses ID.me identity verification cross-referenced against the official's government website. Once verified:
Declared election candidates get the same surface as reps once admin-approved (FEC ID / party nomination check today; ID.me-based self-serve in the future build). Distinct color treatment so candidate pages don't read as incumbent reps.
When a candidate wins, an admin flow promotes them to rep + archives the defeated incumbent.
A single human can sign in as citizen + rep + candidate at the same time,
in the same browser. The navbar exposes an identity dropdown ("pills")
that lets the user pick which identity engages on any action — comment,
vote, react. Author-badge UI, ✓ markers, and engagement attribution
follow the chosen identity. Three independent cookies + bearer tokens
back this: cl_session (rep), cl_citizen (citizen), cl_candidate
(candidate).
Today (demo preview): Self-serve demo citizen accounts are minted via
/api/citizen-auth/demo-signup. Demo citizens get:
verified=False (with verified_method='demo')is_subscribed=True (with subscription_status='demo', stripe_subscription_id=NULL)This lets the demo cohort exercise the full engagement experience (comment
on posts + polls, create polls) end-to-end. The NULL stripe_subscription_id
distinguishes demo grants from real paid subscribers when we audit
post-launch. Both grants are flagged for removal in the codebase
(auth_citizen.demo_signup) once real billing + verification go live.
Once ID.me + Stripe are live: Demo citizens get an opt-in migration (see the Identity verification + demo migration plan section below). Future signups go through the real verification → optional subscription flow.
/api/admin/*civicview.app (apex) + api.civicview.app (backend)CivicView uses a consistent pattern for every external dependency: an abstract base class + a production backend + a dev / fallback backend. The factory picks based on env vars — when the production credentials aren't set, the dev backend takes over gracefully so the backend boots cleanly in any environment.
| Service | Module | Production backend | Dev fallback | Required env vars |
|---|---|---|---|---|
| Image storage | services/image_storage.py | R2Storage | LocalDiskStorage | R2_ACCOUNT_ID + R2_ACCESS_KEY_ID + R2_SECRET_ACCESS_KEY + R2_BUCKET_NAME |
services/email_service.py | PostmarkEmailService | DevEmailService (sends nothing; logs the masked recipient only) | POSTMARK_API_TOKEN + POSTMARK_FROM_EMAIL | |
| Billing | services/stripe_service.py | StripeBillingService | DevBillingService (returns about:blank) | STRIPE_API_KEY + STRIPE_PRICE_ID + STRIPE_WEBHOOK_SECRET |
| Identity verification | services/idme_service.py | IdMeService | DevIdMeService (fail-closed) | IDME_CLIENT_ID + IDME_CLIENT_SECRET + IDME_REDIRECT_URI |
| Web push (browsers, 2026-10-18) | services/web_push.py | WebPushService (http-ece + py-vapid over httpx) | off: browsers are not offered push and nothing is sent | VAPID_PUBLIC_KEY + VAPID_PRIVATE_KEY + VAPID_SUBJECT (Render), NEXT_PUBLIC_VAPID_PUBLIC_KEY (Vercel) |
All four use lazy imports so missing optional dependencies don't crash boot. All four log loudly when the dev fallback is active so an operator knows the integration isn't live yet.
SESSION_SECRET. Mirror bearer tokens for cross-site-cookie-blocked
environments.SESSION_SECRET via HKDF. Recovery codes
bcrypt-hashed. Enforced at first login for rep / candidate / admin
identities when FORCE_2FA_ENABLED=1 (citizens opt-in). Admin access
always needs 2FA on the admin account, whatever that flag says;
ADMIN_2FA_REQUIRED=false turns that off for local development./api/admin/*. GitHub
Dependabot + CodeQL + Secret Scanning + Push Protection on the source repo.
See docs/SECURITY.md + docs/INCIDENT-RESPONSE.md.backend/
app/
main.py # FastAPI app + lifespan startup + router mounts
db.py # SQLAlchemy engine + session + auto-migrate
auth.py # Rep session helpers
auth_citizen.py # Citizen session helpers
auth_candidate.py # Candidate session helpers
seed.py # Demo accounts + demo backfill helpers
models/
pages.py # ALL SQLAlchemy models (~1.4K lines)
schemas/
pages.py # ALL Pydantic request/response schemas
routers/ # FastAPI route modules — one per feature area
auth.py, auth_citizen.py, auth_candidate.py
pages.py, citizen_polls.py, feed.py
billing.py # Stripe Checkout + Portal + webhook
identity_verification.py # ID.me OAuth start + callback
two_factor.py # TOTP enroll + verify + recovery codes
admin.py, appeals.py
congress.py, federal_officials.py, state_officials.py, local_officials.py
bills.py, votes.py, eos.py, elections.py, candidates.py
notifications.py, address.py, waitlist.py, ai.py, google_civic.py, events.py, states.py
services/ # External-dep abstractions + business logic
image_storage.py # R2Storage + LocalDiskStorage
email_service.py # PostmarkEmailService + DevEmailService
stripe_service.py # StripeBillingService + DevBillingService
idme_service.py # IdMeService + DevIdMeService
password_reset.py # Token mint/verify/purge
account_deletion.py # Soft + hard delete + verification archive
totp_service.py # TOTP secret encryption + verification
totp_enforcement.py # FORCE_2FA_ENABLED enforcement
ai_service.py # Claude wrapper for bill / vote / EO / comment AI
congress_service.py # Congress.gov client + photo override layer
requirements.txt
data/ # Curated seed JSON for accounts / candidates / etc.
frontend/
app/ # Next.js App Router pages
page.js # Home — interactive map + side panel
polls/page.js # Global polls feed
admin/ # Admin moderation dashboard
account/delete/ # Self-serve account deletion
methodology, editorial-standards, privacy, terms, contact # About-column footer
password-reset/ # Password reset (request + confirm modes)
components/ # ~50 components — see directory for full list
lib/ # API clients (pagesApi.js is the workhorse) + auth hooks
public/ # Static assets
docs/
SECURITY.md # Security posture, env-var setup, R2 setup
INCIDENT-RESPONSE.md # Runbook: compromised admin / DB breach / DDoS / leak
LEGAL-REVIEW-ROADMAP.md # Attorney engagement plan ($1.5K-$3K Tier-2 path)
civicview_financial_model.xlsx # 5-year P&L behind the crowdfunding goal
identity-model.pdf # Source-of-truth identity model spec
build_identity_model_pdf.py # Regenerates the identity model PDF
access-policy.md # Draft access and neutrality policy (not yet published)
*-prd.md, bills-*.md # Product specs and design handoffs
*listing*.md, *runbook*.md # Store listings and launch runbooks
# Business working files (outreach strategy, crowdfunding drafts, the
# Benefit Corp filing language) live outside the repo since 2026-09-24.
Design Exports/ # Reference design-system snapshots (not in app)
DEPLOY.md, SETUP_GUIDE.md # Production + local-dev runbooks
DESIGN_HANDOFF.md # Design system handoff doc
render.yaml # Render service definition
The canonical, item-by-item list of shipped features lives on the
Help build this page (source:
frontend/components/HelpBuildThisView.js). Maintained as the single
source of truth so the public funding ask and the internal status doc
never drift. As of May 2026 the list runs ~35 entries; high-level
categories:
FeedCard the /posts page uses
(full likes + comments + edit/delete/report inline), with an
"All posts →" link to the canonical feed. Every post-action surface
consolidates Edit / Delete / Report into a single kebab dropdown
(PostActionsMenu) for a uniform look across home, /posts,
/polls, and rep / candidate PageView feeds.services/ai_service.py.People said the official wording of some ballot measures is hard to
follow. A ballot measure's Details on the Elections tab now has
"Explain in plain English" under the official ballot summary: a short
paragraph, the specifics, the votes it needs, and what a Yes vote and
a No vote mean, labeled AI-generated, with the official summary staying
the wording that counts. It is written once per measure from the official
text in the elections data only (services/ballot_measure_service.py,
GET /api/ballot-measures/{id}/plain-english, POST .../generate in the
AI rate limit) and saved, so everyone sees the same text. It uses Claude
Sonnet, not Haiku: in testing Haiku added conditions the ballot text
doesn't have. The prompt keeps it neutral and never says how to vote; an
explanation with any figure that isn't in the official text is not
saved. If a measure's wording changes, its explanation is written again.
The three Florida 2026 amendments ship with explanations read before
release (app/data/ballot_measure_explanations.json); checked by
tests/test_ballot_measure_plain_english.py and
npm run test:summary-text.
frontend/lib/feedFilters.js (npm run test:feed-filters).POST /api/ai/filter-posts
now, and each new post gets the same tone and topic tags polls and
comments get (services/post_classifier.py; the privacy page says so).services/poll_visibility.py; checked by
tests/test_feed_results_and_filters.py).frontend/lib/summaryText.js, checked by
npm run test:summary-text).GET /api/activity/dashboard.GET /api/events/upcoming answer 500.The same pushes as the Android app now reach desktop browsers, browsers
on Android phones, CivicView on an iPhone or iPad Home Screen (iOS 16.4
or later) and the Windows app, through each browser's own push service,
encrypted for that browser. The settings list, Mute, quiet hours,
summaries, grouping and the private-by-default text apply exactly as on
the phone, and Notification details is one choice for all of a person's
devices. The settings list shows "This browser" with Turn on; the
browser asks for permission only from that tap or the opt-in card's
Enable, and the card comes only after a track (and once when the Home
Screen app opens). In Safari on an iPhone, Turn on shows a short Add to
Home Screen guide. Installed apps show the unread count on their icon.
The Android app keeps FCM, unchanged. Web push stays off until the VAPID
keys are set: run scripts/generate_vapid_keys.py on your own computer
and follow the runbook's Web push section
(docs/push_notifications_runbook.md).
Android notification channels (Officials you track, Events, Replies, Bill
updates, Poll results, Election reminders, Changes to pages you track,
Summaries), private by default (each notification shows only a neutral
line, not who posted, unless the person chooses Show details), a
monochrome status bar icon and grouped notifications.
New alerts: replies to your comments (on posts and citizen polls), a
daily pass over the federal bills you track on Congress.gov, and "Results
are in" when a poll you voted in closes. One short settings list,
enforced on the server and the same on every device, replaces the 32
per-item switches, with Mute per official in My Tracked. The app checks
Android's notification setting at every launch and resume and offers
Open settings when it is off. App 1.2.0 (versionCode 5) builds the
notifications itself; 1.1.0 keeps working. Details in Push alerts and
summaries below and docs/push_notifications_runbook.md.
Pushes were silently dropped by default (a hidden 'daily' cadence let
one through per 20 hours), signing out never unhooked the phone, bell
clicks didn't open the post, and many settings did nothing. Now pushes
go as they happen by default, daily and weekly summaries and quiet hours
hold updates instead of dropping them, the phone follows the signed-in
account, every bell, archive and push link opens the post, only
switches that work are shown, emails have one-click unsubscribe, and
times are in the recipient's clock. Details in Push alerts and
summaries below and docs/push_notifications_runbook.md.
GovTrack started refusing our requests (fetch failures from October 3,
403s from October 15), which emptied every profile's Votes tab,
party-line figure and Compare's shared votes. Votes now come from stored
House Clerk and Senate roll calls, synced in the background with ten
years of history; the party-line figure now counts the current
Congress's party-split votes, the same way for every member. Details in
Roll-call votes below and docs/bills-data-spike.md. Screenshots in
docs/screenshots/member-votes-official/.
Thirteen commits on Updates_and_fixes, all pushed. Tip 718f5ab.
Demo sunset (PRD docs/demo-sunset-and-migration-prd.md)
ad7ae91 — increment 2: dismissible dashboard card asking existing
demo accounts for a contact email, PUT /me/contact-email (422s on a bad
address, unlike signup which drops silently) + POST /me/contact-email/dismiss.
Dismissal is a server column so it follows the account across devices.3a10040 — increment 3: the dormant switch. services/entitlements.py
(IDME_ENABLED, DEMO_SUNSET_AT, require_verified, require_subscribed)
wired onto 8 endpoints, no-op until the flag flips. 19 tests in both states.5623cd3 — increment 4: authored_verified snapshot columns on SEVEN
tables (the PRD listed five; the two comment-reaction tables were an
oversight). Verification is never retroactive — a demo account that later
verifies must not relabel everything it wrote. Boot-time convergent backfill.Florida election data — accuracy
1a6e892 — statewide fundraising re-sourced from the FL Division of
Elections with per-account provenance. Donalds' "$67.0M" was a hand-typed
literal with no committee ID; the real structure is a campaign account
plus a separate PAC, and Florida publishes no candidate↔committee link,
so any combined total is an editorial join and is labeled as ours. Also:
Jerry Demings was still rendering as an active candidate (suspended
2026-06-05), and Burn Rate printed a fabricated "0%" from null / total.ce7a581 — Wikimedia portrait seeding with article validation (surname
must appear in the title, extract must mention the state or office). 7 of 20
resolved, zero false positives — a stranger's face is worse than initials.e19cddd — CFO + Agriculture races added (they were absent, not empty).
Fixed ContactTab rendering "WASHINGTON, D.C." over a Tallahassee official,
and falling back to member.office, which on state records holds the branch
classification "Executive". Reproduces on every state's Lt. Governor.8fbc2bf — every FL legislator now has a portrait (40/40 Senate,
119/119 House); 6 statewide executives got sourced bios, experience,
focus areas and real Capitol contact details.9566463 — Aug 18 primary results + the un-narrowed ballot guard.
24 of 35 races were showing a fictional November ballot (FL-1 listed nine
Republicans against each other). A general ballot holds at most one nominee
per party, so 2+ of a major party now flags general_roster_unresolved and
the UI refuses to present the list as a ballot. withdrawn filtering moved
into _resolve_ids so no surface can forget it.Trust + UX rules (all from Jeffrey's review)
2b8303d + 1659007 — links never silently download. A .txt citation
led to finding 173 Mississippi legislators with a raw .xml and 120 Vermont
legislators with a district-map .pdf in contact.official_website. All 293
relocated. frontend/lib/externalLink.js labels file links;
components/ui/FileLink.js asks before any download starts — a label is
not consent. Ordinary pages never prompt, or the prompt becomes noise.718f5ab — dropdown memory. There was none: navState.js covers only
page-level "where am I", and ElectionCard was passed defaultOpen={idx===0},
force-opening the primary on every mount. New frontend/lib/disclosureState.js
stores choices, not states (snapshotting every value would freeze today's
defaults into every browser forever) and keys describe the thing, never its
position. Primary results now collapse, with the winners still on the header.7b0396d): SMS row removed permanently, badges match reality.
Parts 2–4 (01eb4f4 + ruff fix 2e85866): account-synced channel
prefs (CitizenAccount.notification_prefs_json + GET/PUT
/me/notification-prefs; server wins on sign-in, 700ms debounced sync,
payload carries tz_offset_minutes); anonymous tracked pushes
(DeviceToken.tracked_json/prefs_json/last_push_at; /api/push/register
accepts tracked+prefs; fan-out reaches citizen-less devices);
quiet-hours + cadence enforcement in the send path (suppress, don't
queue — Jeffrey's call; nights 22:00–08:00 local via tz offset,
daily ≥20h / weekly ≥6d min-gap; no prefs blob = no suppression).
Corrected 2026-10-16: every registration and sign-in sends a prefs
blob, so the defaults applied to everyone and the daily min-gap dropped
pushes silently. The min-gap is gone: pushes go as they happen by
default, and daily, weekly and quiet-hour pushes go out as one summary.4620059). civicview.app is installable on
Windows/macOS/Linux/ChromeOS: the manifest's SVG-only icons (which fail
Chromium's install check) got real 192/512 PNG + maskable icons from the
brand masters; minimal service worker (no fetch handler — future Web
Push landing pad); registration skips the native shell; apple-touch
icon fixed (iOS ignores SVG)..msixbundle v1.0.1.0 uploaded (runFullTrust justified as standard
PWABuilder template); US-only market, Free, ESRB Teen, gen-AI declared
YES, block-users NO. Product-identity values + full runbook:
docs/microsoft_store_listing.md (0debcbc) + Pinecone. 10 store
screenshots + poster/box art in App store screenshots\_Upscaled\Desk top\store-ready\. Auto-publishes on pass; post-cert checklist in the
runbook §6.2cb77a9). (1) Map zoom slider is desktop-web
only — compact viewports + native app get a percent-only chip; (2) bill
NAMES on /bills (Clerk <vote-desc> captured; all recent House rows
enriched concurrently; dropdown/card/search show titles); (3) Text/✨AI
vote search on /bills for both chambers (corpus = 50 recent votes via
/api/ai/filter-items); (4) force-update gate: (env / ,
both unset = inert) + — hard full-screen block below
min, dismissible nudge below latest, fail-open everywhere; (5) push
opt-in card now also triggers on first track (any of the three stores,
works signed-out).components/TrackedManager.js (used by BOTH the My Tracked nav window and
the Manage Tracked tab — capped/scrolling sections, search, chips, per-item
alerts, a star "feature" toggle, clickable official cards). Overview is
now four "Followed X" spotlights (rep / candidate / election / bill),
each showing the one pinned item + its latest updates. Featured picks are
account-synced via a new featured_tracked table (one row per category,
UNIQUE constraint) + GET/PUT /api/tracked/featured + lib/featuredTracked.js.
My Tracked window gained an "Open in dashboard →" button; subtitle corrected
to "synced to your account".bio +
current-office-led experience to: the executive branch (President, VP, 15
Cabinet secretaries), all 9 SCOTUS justices, 13 congressional-leadership
entries (federal_officials.json), and the full U.S. delegations of FL
(30), TX (40), CA (54), NY (28), PA (19) — 171 member bios in
congress_profiles.json. Current role/year is roster-derived with
chamber-aware, gap-aware logic (Schiff "senator since 2024", Sessions
"since 2021"); prior roles curated + cited to bioguide.congress.gov.
(The generated candidates.json bios were boilerplate — not reused.)capacitor-assets generate was never run) — fixed by adding real
brand masters to frontend/assets/ (from Logos & emblems/civicview_logo_solo.png)
and regenerating icons; bumped versionCode 2→3, v1.0.1→1.0.2. (1b) same date:
description lacked sources + a government disclaimer — added a "⚠️ NOT A
GOVERNMENT APP" disclaimer. (2) 2026-07-05: "Insufficient Sources" — the
"primary sources include…/where available…" hedging implied a partial list;
rewrote "WHERE OUR INFORMATION COMES FROM" as an exhaustive list
(congress.gov, GovTrack, @unitedstates project, House Clerk, Senate,
federalregister.gov, CourtListener, fec.gov, openstates.org, Google Civic,
Census geocoder, OpenStreetMap) + a Wikimedia photo credit, ending "we use no
other sources." Store-listing source of truth: docs/playstore_listing.md.
App verified (green ✓), managed publishing OFF, US-only, category Social, org
account (exempt from the 12-tester gate), Android developer verification
COMPLETE. Appeal note held in reserve at .test_official_votes.py had stale list-parser expectations from the
intentional 19e77ca Senate-bill fix; test_tracked_cross_account.py
predated the CSRF middleware and now derives + sends X-CSRF-Token
exactly like the frontend. All 9 test files green. Lint 73 → 2 findings
(both intentional E402s). Removed 2 unused imports.CitizenAccount.start_page (nullable, auto-migrates) + allowlisted
PUT /api/citizen-auth/me/start-page + StartPageSection in dashboard
settings + once-per-session redirect in app/page.js (explicit URL
params always win; sessionStorage guard).GET /api/stats/detail
(60s TTL cache, separate from /summary so home stays fast) — identity /
engagement / content-library counts, 8-week signup + poll-vote trends,
citizens-by-state. Frontend page rebuilt with loading + error + retry
states, CSS-only charts, all numbers live or structural (none fabricated)./bills navbar "Tracked items" + "Dashboard"
now deep-link via /?open=tracked|dashboard|settings instead of dead-
ending on home; /polls infinite scroll failure now shows a tap-to-retry
button instead of silently ending the feed.services/rate_limit.py) + EngagementRateLimitMiddleware
(30 writes/min on comments/reactions/votes/reports, 10/10min on
poll/post creation, keyed by session token); demo-signup limiter
migrated onto the same mechanism (5/24h per IP unchanged).DIGEST_ENABLED env flag is OFF until Jeffrey
flips it in Render; demo-domain addresses never sent to; preview
endpoint + Settings card with live preview.PollDemographicQuestion / PollVoteDemographic /
CitizenDemographicProfile + Poll.min_cell_override;
services/demographics_catalog.py + services/poll_demographics.py +
routers/poll_demographics.py; frontend PollResultsModal /
PollDemographicsPicker / PollDemographicsForm /
DemographicProfileSection wired into every poll surface. Tests green.
PRD: docs/polls-demographic-forms-prd.md.candidates.json + elections.json generated via
backend/scripts/build_state_federal_candidates.py (config-driven: state
key dates, closed-primary flags NY/PA, NY ballot measures, Senate-cycle
gating; federal incumbents enriched from sourced profiles, challengers
FEC-facts-only). Re-run playbook persisted in Pinecone.docs/indiegogo_draft.md
(moved out of the repo on 2026-09-24, with the rest of the business
working files) is FINAL (Flexible funding, $25K, 35 days, 4 perks w/ Dec-2026 delivery,
FAQ, launch checklist, share copy, video script). Financial model gained an
isolated one-time crowdfunding block (Assumptions rows 68–75: ~8% fees →
~$23K net; NOT wired into recurring P&L). Campaign art to Indiegogo spec in
../Indiegogo images/ (21:6 text-free cover, square logo thumbnail from the
official glyph, 4 escalating reward-tier images).app/page.js); desktop-layout first-paint flash
killed via pre-paint viewport measurement (lib/useViewport.js); map camera
re-asserts its intended target after container resizes settle
(intendedCameraRef + debounced ResizeObserver, components/MapView.js).api.kie.ai/claude/v1/messages is an
Anthropic-native drop-in; measured Haiku 4.5 ≈ $0.27/$1.45 per M tokens
(~72% off) but 2.9–5.6s latency. Decision: build the provider flag, keep it
OFF; user-comment classification stays on official Anthropic regardless.Federal + state DATA layers were committed/pushed earlier this session; the
election-candidate work at the end may still be local — verify git status
(uncommitted: backend/app/services/fec_service.py, backend/app/data/fl/candidates.json,
backend/app/data/fl/elections.json).
top_issues in
congress_profiles.json; executive branch, SCOTUS (as neutral "Areas of
Focus"), and congressional leadership in federal_officials.json too.
Committee auto-fill + sponsored-bill issue-area derivation + fix to the live
bio/experience derivation (Congress.gov startYear/stateCode term fields).
Fabricated/stale seed entries purged; SAMPLE_DATA Rick Scott bioguide fixed
(S001227 → S001217).data/<state>/state_officials.json for every state from Open States bulk
data (~7,355 legislators) + statewide executives; all 50 governors curated +
sourced. Live state-legislator Bills/Votes wired (fixed the OpenStates
/bills sponsor filter needing jurisdiction; added openstates_id
fast-path) + a live AI-derived issue-areas endpoint (Haiku over bill titles).docket__court filter; roster already curated)./lookup.fec_service.py (OpenFEC): 156 FL
federal candidates added with live fundraising; US Senate + all 28 US House
district races added to fl/elections.json. All FL state candidates given
overviews; the 6 Governor contenders + AG race fully curated from official
campaign sites (issues/endorsements/experience/fundraising); withdrawn Josh
Weil removed.API_Research_Report.html: added CourtListener +
IN-USE/PLANNED tags. /help-build + financial model: Vote Smart $4,850/yr
optional add-on + Google Workspace Business Starter $8.40/mo. Gmail draft
reply to Vote Smart declining the license for now.Local/uncommitted unless pushed — Jeffrey decides the commits.
/bills page: govtrack-style
selectable seat hemicycle (Senate/House toggle, seat → mini-card → rep
profile link), recent-vote selector, slate tally bar, inline AI vote
explainer, deep-linkable vote URLs, and a "View the bill on Congress.gov"
source link. Reachable via the navbar hamburger as "Floor Bills".
backend/app/services/official_votes_service.py
(House Clerk EVS XML + Senate LIS XML parsers, lis→bioguide crosswalk,
Congress.gov beta House Roll Call API integration, GovTrack fallback,
caching) + GET /api/votes/recent and GET /api/votes/{vote_id}/members
(backend/app/routers/votes.py). 51 offline parser assertions in
backend/tests/test_official_votes.py.frontend/app/bills/page.js, frontend/components/bills/*,
frontend/app/bills/bills.css, plus the home section in
NationalOfficialsPanel.js and the navbar entry.docs/bills-feature-prd.md, docs/bills-feature-design-handoff.md,
docs/bills-data-spike.md (GO verdict, Congress.gov LIVE-validated).frontend/app/polls/page.js (see Pending table for the deliberate non-changes).archived read-only prop to the shared
components/polls/CommentsThread.js (prep for consolidating the 3-way comment
fork; increments 2 + 3 are scoped in the Pending table).Local/uncommitted unless pushed — Jeffrey decides the commits.
SavedItem model, /api/saved, viewer.is_saved, Save kebab on cards).docs/threat-detection-prd.md). Acting on verdicts is
Task #49.FeedCard parity; /polls ?kind=rep
candidate-leak fix; comments Edit/Delete/Report consolidated into the kebab
on all three comment surfaces./polls,
and /posts.POST /api/ai/filter-items, scoped to the loaded set).backend/scripts/reset_summary_cache.py).(Mirrored on /help-build under the "In progress" section.)
civicview.app)docs/microsoft_store_listing.md §6).Canonical open-tasks list. On session start, recreate every row below (plus the launch-sequence items) as a task in the Cowork Progress widget so the backlog carries across sessions. (CLAUDE.md instructs this read on every session start; Pinecone holds the per-session narrative.)
| # | Task | Status | Notes |
|---|---|---|---|
| 71 | Build /stats expanded analytics page | done (2026-06-10) | Shipped: GET /api/stats/detail (60s in-process TTL cache, separate endpoint so the home hero stays sub-100ms) + rebuilt frontend/app/stats/page.js — government-structure constants, identity/engagement/content-library live counts, 8-week signup + poll-vote trend charts (CSS bars, zero-filled buckets), citizens-by-state top-15, explicit loading/error/retry states (no fabricated fallback). Future depth (growth curves beyond 8 weeks, verified-citizen coverage map) can extend the same endpoint. |
| 84 | Wrap web app into iOS + Android native via Capacitor | done (2026-06-12) | Capacitor remote-URL shell scaffold + app-store runbook committed (9fbbac0). Android organization account verified on Google Play Console (CivicView org, ID 5150866026642573505); next step is creating the app listing + first build upload. Apple Dev $99/yr + Google Play $25 paid. 2026-06-16: app SUBMITTED to Google Play Production review (status: In review); Child Safety Standards page (/child-safety) published to clear the CSAE declaration; Android developer verification confirmed complete. |
| 90 | File Articles of Amendment for Benefit Corp status | done (2026-06-12) | Filed with Sunbiz. Initial Profit Corp filing (#800474911808) processed; CIVICVIEW, INC. is ACTIVE; cr2e011 Articles of Amendment (Exhibit A statute cites §§607.601-607.613) filed. Language in the Benefit Corp filing language (kept outside the repo since 2026-09-24). |
| 91 | Evaluate Vercel AI Gateway for backend AI calls | pending | Post-launch / deferred. Not urgent: current AI (comment classification + post summaries in backend/app/services/ai_service.py) is single-provider Anthropic and already degrades gracefully. The Gateway's OpenAI-compatible endpoint makes adoption a base_url + key swap in ai_service.py (no SDK rewrite). Adopt when AI spend warrants cost/usage dashboards, when provider failover matters, or to mix models (cheap classification + stronger summaries). Pricing: $5/mo free credit, then provider list prices at zero markup (BYOK also no markup). Caveat: routing the summarize flow through the OpenAI-compatible shim instead of the native Anthropic SDK can change system-prompt / citations / prompt-caching behavior — re-test that one flow before switching. |
| 92 | Trim /polls + /posts filter cruft (audit follow-up) | done (2026-05-31) | Removed the inert + branch chips from + in (backend only emits for Congress reps, so those chips always counted ~0). Deliberately LEFT the // pipeline and the client-side state re-filter (~lines 313-319) intact — the original audit overreached: the re-filter guards a real refetch race, not dead work. Original audit 2026-05-28. |
| 38 | Notifications v2 (honest panel · synced prefs · anonymous pushes · quiet hours) | done (2026-07-24/25) | All four parts shipped + pushed (7b0396d, 01eb4f4, 2e85866); see the 2026-07-24/25 shipped block. Enforcement is opt-in-by-sync: accounts/devices that never synced prefs keep pre-v2 behavior. Corrected 2026-10-16: in practice every app registration and every signed-in account syncs prefs, so the old defaults (quiet hours nights, cadence daily) applied to everyone and dropped pushes; see "Push alerts and summaries" below. |
| 108 | Microsoft Store listing (Windows desktop) | LIVE since mid-September 2026 (submitted 2026-07-25) | PWABuilder MSIX shell of the live PWA; web deploys need NO store resubmission. Post-cert checklist in runbook §6 (clean-machine install test, Store badge next to the Play badge, README/HelpBuild "Already built" row). Identity values in Pinecone (2026-07-25-microsoft-store-identity). |
| 109 | Force-update gate: arm when needed | shipped OFF (2026-07-25) | 2cb77a9. Gate is inert until APP_MIN_VERSION_CODE (hard block) / APP_LATEST_VERSION_CODE (nudge) are set in Render env + restart. Set LATEST when a new AAB ships (5 once 1.2.0 is at 100 percent); set MIN only when an old shell is genuinely broken (1.1.0 is not: the server still sends it the standard message). |
| 110 | Rebuild FL U.S. House rosters against the 2026 map | done (2026-09-24) | All 28 FL U.S. House races rebuilt on the 2026 map from the Division of Elections qualified-candidate list, with roster_status: verified_nominees, write-ins listed separately and incumbents only where they sit in the same district. Commit "fix(fl-data): rebuild Florida 2026 ballot from the official candidate list and certified results" in the 2026-09-24 audit series. |
| 111 | Certify FL primary results + resolve FL-11 recount | done (2026-09-24) | Certified primary results recorded with a source link and shown as "Official results"; FL-11 recount outcome recorded (Joe Strada advances). Same commit as #110. |
| 112 | Demo-sunset increments 5–7 | pending (5 blocked) | 5 — in-place ID.me upgrade path (blocked on the RP contract); 6 — fallback credential-transfer UI with audit log; 7 — sunset machinery (DEMO_SUNSET_AT countdown banner, T0/T-14/T-7/T-1 email cadence, soft-delete job, data export). Increments 1–4 are done and inert until IDME_ENABLED flips. |
| 113 | Re-justify the paid tier (poll creation only) | pending (business decision) | Commenting moved from the subscriber tier to the verified tier (64ff2ab), so the $5/mo subscription now unlocks poll creation only. HelpBuildThisView.js:217 revenue projection (3% conversion, $1.8K Y1) and the Indiegogo draft (kept outside the repo since 2026-09-24):240-241 still lean on the old engagement bundle. Flagged, deliberately not silently edited. |
| 114 | Privacy policy + terms: contact_email & deletion schedule | done without counsel; attorney review when affordable | The 2026-09-24 audit rewrote the privacy policy and terms to match the code (contact email, poll answers, recipients, deletion and retention, Florida law and Orange County venue, arbitration removed pending counsel). 2026-10-15: the privacy policy was rewritten again without an attorney (none affordable yet), checked line by line against the code and against California's CalOPPA contents (data and recipient categories, review and correction, change notices, effective date, Do Not Track, cross-site tracking) and Florida's 30-day breach notice; it offers access, correction, deletion, portability and appeal to every U.S. user, and the Terms now say 18 and older. Have an attorney review both when that becomes affordable. |
| 115 | Automated FL Division of Elections finance ingest | pending | Today's state fundraising is curated with provenance, not automated. Path is confirmed: TreSel.exe?account=N returns per-period totals for candidates and committees, and the qualified-candidate list downloads as TSV keyed by a stable AcctNum. Caveat that shapes the design: the candidate↔committee mapping cannot be automated (the state's Affiliates field is empty for candidate-aligned PCs), so that join stays a human-signed-off table, not a scraper. |
| 116 | Publish Android 1.2.0 (phone notifications) | pending (Jeffrey) | Build the signed AAB (versionCode 5), Internal testing on Jeffrey's phone with the device checklist, then a staged Production rollout; keep the 1.1.0 AAB and a tag to rebuild a 1.1.1 if a rollback is needed. Data safety changes listed in . |
| 117 | State bill tracking + Open States bill updates | deferred (after November 3, Jeffrey) | State bills can't be tracked today (billKey is Congress only). Needs a new tracked type with its own key (Open States id), storage, My Tracked rows and bell links, then an Open States pass beside the Congress.gov one in . Until then no copy promises state bill alerts. |
| 118 | Turn on web push (VAPID keys) and run the browser checklists | pending (Jeffrey) | Shipped OFF (2026-10-18, ). Run on your own computer; set , , on Render () and (the same public key) on Vercel Production, redeploy both, then run the iPhone (Home Screen app) and Windows app checklists in . Keep the pair in ; a new pair later ends every browser's subscription. |
| 119 | One coded notification id in Android pushes | deferred (after November 3, Jeffrey) | Each FCM push now carries the kind of update and the ids that open it (an official's page id, a post, poll or event id, a federal bill's number, a state code, the bell row id), never names or text unless Show details is on; the privacy policy says exactly that (2026-10-18). A later Android build replaces those ids with one coded notification id the app looks up after the tap; then reword the privacy sentence. Web push is unaffected (encrypted payload). |
Closed: Task #58 (Add financial-model link to /help-build) — won't ship as a public link. The docs/civicview_financial_model.xlsx is already in the public GitHub repo for anyone who wants to audit the math; shared on request rather than surfaced as a download on the campaign or app surfaces.
See docs/gofundme_draft.md for the full launch playbook with copy-paste
share text + GoFundMe story + perk tiers + FAQ. High-level:
Phase 1 — free / cheap pre-work (do now):
4242 4242 4242 4242)Phase 2 — corporate + banking (1-2 weeks):
Phase 3 — launch the GoFundMe ($25K goal):
Phase 4 — deploy GoFundMe proceeds in priority order:
docs/LEGAL-REVIEW-ROADMAP.md)Phase 5 — transition demo cohort to verified accounts:
The current build runs entirely on self-serve demo citizen accounts. Real verified citizen accounts ship once the ID.me Relying Party contract is funded.
When ID.me ships, demo users get an opt-in migration:
CitizenAccount row is updated in
place (verified=True, display name + city + district reconciled
against ID.me), and engagement surfaces stop labeling the user
"Unverified."CitizenAccount is created. Old polls / comments keep their
original demo authorship attribution (no identity laundering);
votes are wiped to avoid skewing historical poll results.Why opt-in, not forced: Full migration is bad because demo accounts are self-serve and not identity-verified — forcing migration would attach real verified identities to whatever the demo account did. Full reset is bad because legitimate early users (your TikTok-live early adopters, friends who tested) lose their engagement history just for upgrading. Opt-in with a sensible default threads the needle.
Implementation note for the cutover:
POST /api/citizen-auth/verify-and-merge accepts the
ID.me identity claim + a keep_demo_data: bool flag.verified=True on the existing row and reconciles
display/geo fields, or archives the old row and creates a fresh one.Captured for a future release — not in scope for current launch.
Tracked here so it stays visible across sessions:
docs/LEGAL-REVIEW-ROADMAP.md. Required before holding real
subscription funds.SETUP_GUIDE.md — run frontend +
backend locally, test on a real phone via Wi-Fi or Tailscale.DEPLOY.md — Vercel + Render +
Cloudflare DNS end-to-end walkthrough.docs/SECURITY.md — env-var
inventory, R2 / Postmark / Stripe / ID.me configuration.docs/EMAIL-DELIVERABILITY.md
— SPF / DKIM / DMARC setup for Postmark + Resend on Cloudflare DNS.docs/INCIDENT-RESPONSE.md
— runbook for compromised admin, DB breach, DDoS, credential leak.docs/gofundme_draft.md
— full GoFundMe campaign package (story, FAQ, tiers, share copy,
launch checklist).CLAUDE.md
for the two decoys that make this look already-fixed when it isn't.Each odd-numbered January a new Congress is sworn in (the 120th at noon
Eastern on January 3, 2027) and most legislatures seat new members.
docs/january-turnover.md is the
checklist: what switches by itself, then the state refresh below, the
federal roster refresh (backend/scripts/refresh_congress_roster.py),
and the checks.
One settings list (the bell's Settings, or Dashboard, Account &
settings, Notifications) decides, per kind of alert, whether it reaches
the bell, the phone or email: Officials you track, Events, Replies to
your comments, Bill updates (federal bills, from Congress.gov), Poll
results, Election reminders, Changes to pages you track, and the Weekly
email digest. The server enforces it on every send path. Mute in My
Tracked silences one official. Push alerts: As they happen (the
default), Daily summary (6 p.m. local) or Weekly summary (Saturday 10
a.m. local) for officials, events, bills and poll results; nothing is
dropped, and quiet hours hold pushes for one summary when they end.
Election reminders always come on their own. On Android each kind has
its own notification channel, and a notification, locked phone or not,
shows only a neutral line unless the person chooses Show details. Since
2026-10-18 browsers, the iPhone Home Screen app and the Windows app get
the same pushes by web push (services/web_push.py, public/sw.js,
lib/webPush.js), under the same rules. A phone or browser is bound to whoever
is signed in on it and unbound on sign-out. Emails a person can turn off
carry a one-click unsubscribe (RFC 8058) that turns off only that email
type. How it works, the release steps, Data safety and the device
checklist:
docs/push_notifications_runbook.md;
the email side: docs/EMAIL-DELIVERABILITY.md 2.6.
Reminders before voting deadlines and Election Day are built from each
state's backend/app/data/<state>/elections.json alone: a state is
covered once its reminder dates have official sources. The format, the
source and retrieved-date rules and an example are in
docs/election-reminders.md. Sending is
off until ELECTION_REMINDERS_ENABLED=true is set on Render; check
Admin, Election reminders first.
The privacy policy and the Terms promise a notice on CivicView at
least 30 days before a material change. Set SITE_NOTICE_TEXT on
Render (and optionally SITE_NOTICE_URL, a "Read more" link such as
/privacy or a full https:// address) and a small banner appears at
the top of every page on the site and in the apps; unset the text to
take it down. Each browser can dismiss it once per notice: a new text
or link shows again. Served by GET /api/app/notice
(backend/app/routers/app_meta.py), shown by
frontend/components/SiteNoticeBanner.js.
Every member's Votes tab, the party-line figure, Compare's shared votes
and the /bills seat chart read stored roll calls from the official
records: the Clerk of the House (clerk.house.gov/evs/{year}/roll{NNN}.xml,
counted through Congress.gov's /house-vote API) and the U.S. Senate
(senate.gov vote menus and per-vote XML). GovTrack is not used anywhere
(it began refusing our requests in October 2026).
roll_calls, roll_call_positions (one row per member
per vote, with the member's party on that vote), roll_call_choices
(who each member voted for in an election of the Speaker, shown as
"Voted for {name}" for every name alike), roll_call_members,
roll_call_sync_state. Created by the boot auto-migrate.backend/app/services/roll_call_sync.py) runs in the API
process, never on the boot path: first pass two minutes after boot,
then hourly from 9 a.m. to midnight Eastern on weekdays and every 6
hours otherwise. It fetches only roll calls that are not stored yet,
newest first, and holds a Postgres advisory lock so two instances
during a deploy cannot both run it. Each pass writes one log line
starting Roll-call sync:. A site that fails five times in a row is
left alone for the rest of the pass, and after three passes with no
progress on history the sync goes back to the normal schedule; both
are logged. On by default in production only (off in tests and local
runs).ROLL_CALL_BACKFILL_CONGRESSES changes the number of Congresses
(default 5). Nothing stored is ever deleted.GET /api/admin/roll-calls (admin sign-in) lists each
session's official and stored counts, what was added in the last 24
hours and the last pass. ROLL_CALL_SYNC_ENABLED=false turns the
sync off in production; =true runs it anywhere else./api/votes/recent answers only for sessions in the
stored window, and reads a past session from storage; an unstored
vote is fetched on request only inside the window, for a session not
yet complete, and not far beyond the highest stored number.roll_call_store.party_line).
A member who changed party, in either direction, is measured only on
the votes cast in their current party (the party on their most recent
vote); if that is independent, no figure (Jeffrey, 2026-10-16).h-{congress}-{session}-{roll} / ,
the same on /bills and on profiles, so the vote explainer caches one
explanation per roll call and builds it from the stored record.The 50 backend/app/data/<state>/state_officials.json files (state
legislators, governors and statewide executives) are refreshed from
Open States by one script. Run it whenever legislatures change, and
always in January, after the new legislatures are sworn in.
cd backend
python scripts/refresh_state_officials.py --all # dry run: prints the report, writes nothing
python scripts/refresh_state_officials.py --state TX --state NE # some states only
python scripts/refresh_state_officials.py --all --write # write the files
python scripts/build_photo_credits.py --write # only if a Wikimedia photo changed
python tests/test_data_lint.py
It needs git and network access. The source is the openstates/people repository (public domain, CC0 1.0), cloned shallow into a temporary folder; seats per district come from openstates-core. The Open States API is used only when a person is missing from the repository.
How it treats the data:
openstates_id. The same person in the
same seat keeps their id and every hand-curated field (photos and
photo credits, bios, capitol offices, sources).tx-sen-4-ligon. An id is never handed to a different person,
because posts, claimed pages, citizen polls and follows are keyed on
it. Ids that leave the roster are kept in each file's _retired_ids
so they are never reused.vacancies list on each chamber. The app
does not show them yet.The report lists, per state: added, removed, changed (name, party, district), vacancies, anything unverified, and fields left empty.
January checklist:
--all and read the whole report.candidates.json and
elections.json still point at sitting people. Do not regenerate
those files with build_state_federal_candidates.py.tests/test_data_lint.py and python -m pytest.CivicView is proprietary software. All source code, designs, content, and assets are © 2026 Jeffrey De La Nuez. All rights reserved.
No part of this repository may be copied, modified, distributed, or
otherwise used without the express written permission of the copyright
holder. See LICENSE for full terms.
For licensing inquiries: [email protected]
/api/stats/summary endpoint
powering the home page hero tiles (Senators / Representatives /
SCOTUS structural counts + live Reps joined / Verified citizens /
Demo accounts created), plus a placeholder /stats page that an
expanded analytics surface (engagement curves, growth by state)
will absorb post-launch.GET /api/app/versionAPP_MIN_VERSION_CODEAPP_LATEST_VERSION_CODEAppUpdateGate413c1e2). Tapping a tracked-post push opens the
exact post: root-mounted tap listener → /?page=<id>#post-<post_id>;
PageView's #post- hash now feeds the highlight/scroll machinery
(bounded retries) — which also completed the bell-notification deep
link (#106e).863db06). Every
page is followable from its own hero (page-only accounts like the
internal test rep/candidate were unfollowable anywhere — they have no
registry profile, by design); notifications_inapp tracker match is
now case-insensitive (mixed-case page ids fanned out to nobody).Business info/Play_appeal_reviewer_note.mdfrontend/app/privacy/page.js
had claimed Anthropic receives "never your comments," but comment_classifier
/ poll_classifier / moderation_service do send user polls/comments to
Claude Haiku for tagging + moderation. Rewrote the disclosure to state UGC is
sent for classification/moderation (PII/engagement history are not; no
training). Web change — deploys instantly, no store re-review.civicview.app authenticated (SPF/DKIM/DMARC — also satisfied #26).
Backend mirrors every /api/waitlist signup into Brevo list #3 "Launch
Waitlist" via app/services/brevo_service.py (BackgroundTask; env
BREVO_API_KEY + BREVO_WAITLIST_LIST_ID=3 on Render) + a one-time
scripts/backfill_waitlist_to_brevo.py. Template ID 1 "CivicView — Launch
Waitlist Welcome" (from Claude Design; email HTML + optimized images at
frontend/public/email/), and a Brevo automation (list-add → template)
auto-welcomes each new signup. Verified end-to-end in prod.main.py:
response middleware adds Cache-Control: public, max-age=60, s-maxage=600, swr=86400 to public read-only endpoints (auth/personalized excluded) +
startup background task warms the member directory + committees. Infra: API
now serves from the Cloudflare-proxied api.civicview.app with a Cache
Rule; NEXT_PUBLIC_API_URL points there. Verified cf-cache-status: HIT
(Miami edge); /api/auth/me correctly DYNAMIC. Frontend was already lazy.
Heavier per-member disk precompute deferred (not needed now).kind='tracked_post'
in-app notifications fan out to every tracking citizen when an official
posts (BackgroundTask, own session); bell renders kind-aware copy.executivejudicialBRANCH_FILTERSbranchCountsfrontend/app/polls/page.jsbranchpollBranchbranchCountsbranchFiltered| 93 | Unify forked comment rendering onto CommentsThread | done (2026-06-04) | Three independent comment implementations: shared components/polls/CommentsThread.js (feed thread — /polls, /posts, home), components/PostCard.js (own renderCommentRow ~1406-1822, rep/candidate page posts, HAS an edit path), and components/CitizenPollsSection.js (own local CommentsThread function ~903-1637 mounted ~824, citizen polls, NO edit path, uses archived to lock closed polls). Goal: collapse onto the shared component (the only one that's production-proven for both post and poll modes). Increment 1 — DONE 2026-05-31: added an archived (read-only) prop to the shared CommentsThread (closed poll → composer replaced with a "closed to new comments" note + Reply control hidden; existing edit/delete/report untouched). esbuild-verified. The missing capability that blocked consolidation. Increment 2 — TODO (CitizenPollsSection, do first, smaller): import CommentsThread from './polls/CommentsThread'; delete the local CommentsThread function (~903-1637) + its renderCommentRow + comment state/handlers; mount with mode="poll" pollId signedIn={!!citizen||isOwner} onLoginRequired={onCitizenLoginRequired} ownerOfficialId ownerKind archived={archived}. Drop the old pollAuthorId/citizen props — the shared component infers the viewer internally and gates replies via comment authorship + ownerOfficialId. Increment 3 — TODO (PostCard, bigger): replace the inline renderCommentRow (~1406-1822) + render block + comment handlers (loadComments/edit/delete/report/react) with <CommentsThread mode="post" postId signedIn onLoginRequired onMutated={onCommentCountChanged} ownerOfficialId={post.official_id} ownerKind />; the shared component already covers edit + AI filter + feed-count sync on /posts. Do each increment as its OWN commit and runtime-test on the dev server (reply two-party gate, archived lock, feed-count pill) — these paths are not statically verifiable. Mount caveat: after a host-side edit, esbuild lags ~seconds-to-minutes (virtiofs cache, anthropics/claude-code#50873) — wait a beat before verifying, or restart the session for a fresh mount. |
| 94 | Threat-detection v2 enhancements (post-v1) | pending | Deferred follow-ups from the threat-detection PRD (docs/threat-detection-prd.md §14): image/media moderation; multi-language support; repeat-offender escalation to auto-suspend; embeddings-based near-duplicate threat detection; user-facing "why was this flagged" explanations; and an optional synchronous block-on-submit path for the worst, highest-confidence cases. Revisit after the v1 flag→review pipeline (Task #41) is live and tuned. |
| 25 | Fill out remaining states' content | in progress | DONE: federal issue-data for all 535 members of Congress + exec + SCOTUS + leadership; state legislators + governors + statewide execs for all 50 states (Open States); FL state judiciary (CourtListener opinions) + FL federal/state election candidates. REMAINING: official photos for state legislators; state judiciary rosters for the other 49 states (CourtListener, 5 req/min free-tier limit — see #97); LOCAL officials (sheriffs/judges/DAs/school boards — paid, see #99). 2026-06-16: added About bio + current-office experience for the executive branch, SCOTUS, congressional leadership, and the full FL/TX/CA/NY/PA U.S. delegations (171 bios in congress_profiles.json). |
| 26 | Start DMARC monitoring (SPF merge done) | in progress | Email deliverability on civicview.app. SPF merge DONE (2026-06-04): the duplicate SPF TXT records were merged into a single v=spf1 … -all (RFC 7208 requires exactly one; multiple caused a PermError). REMAINING: publish a DMARC record in monitoring mode — a _dmarc.civicview.app TXT record with p=none + rua=mailto: aggregate-report mailbox — to watch SPF/DKIM alignment before tightening to quarantine/reject. DKIM already in place. |
| 95 | Vote Smart API — stated issue positions | blocked (budget) | Quoted 2026-06 at $4,850/yr for the Public-Facing Platform License (dev tiers $350/mo, $1k/3mo, $1.9k/6mo, non-public only). Draft reply sent declining for now. When funded: env-gated votesmart_service.py (abstract+prod+dev like idme/stripe), bioguide→candidateId crosswalk, map NPAT positions to a sourced stated-positions field. ToS HARD RULE: data may NOT be used in campaign activity → only on official/rep profiles, never candidate campaign surfaces. Coverage partial. |
| 96 | OpenFEC federal candidates for other big states | in progress | TX, CA, NY, PA DONE (2026-06-05) via backend/scripts/build_state_federal_candidates.py (config-driven generator; re-run playbook in Pinecone — search "state federal candidates playbook"). REMAINING: the other 45 states. Caveat: OpenFEC reflects active FEC committees, not ballot qualification → includes some withdrawn/non-qualifying filers (verify vs state). Fundraising is a static snapshot (re-run to refresh) — optional: build a live refresh endpoint. |
| 97 | State judiciary (CourtListener) for other states | pending | FL done (supreme-court opinions via state_live.py, docket__court filter). Populate judiciary.supreme_court rosters per state via the People API. Caveat: CourtListener free tier = 5 requests/MINUTE → bulk needs a Free Law Project membership. Trial/county judges + DAs remain a paid (Ballotpedia) gap. |
| 98 | Full candidate depth (state/local + minor filers) | pending (paid) | Campaign-site curation done for FL Governor + AG CONTENDERS only. The long tail of minor filers, all state-leg/local candidate slates, and endorsements have no free source → Ballotpedia/BallotReady subscription, or per-race manual curation on request. State candidates also have no fundraising (FEC is federal-only; FL campaign finance is at the FL Division of Elections, not integrated). |
| 99 | Local officials (sheriffs/judges/DAs/school boards) | pending (paid) | No comprehensive free source. Cicero or Ballotpedia (paid). The free Google Divisions OCD-ID bridge is already wired into /api/address/lookup (ocdDivisions) as the join key for whatever local source is chosen. |
| 100 | AI provider base-URL abstraction (KIE flag — OFF) | pending — flag stays OFF until Jeffrey says | Env-gate the Anthropic base URL in backend/app/services/ai_service.py: AI_PROVIDER=official|kie (+ optional ANTHROPIC_BASE_URL override), default official. KIE verified 2026-06-05: https://api.kie.ai/claude/v1/messages is an Anthropic-native drop-in (same Messages shape incl. cache fields, resolves claude-haiku-4-5); measured Haiku ≈ $0.27/M in, $1.45/M out (~72% off official $1/$5; credits round to 2dp so ±10%); latency 2.9–5.6s on small calls → suitable for the cached precompute pipeline only, never interactive. HARD RULE: user-comment classification stays on official Anthropic regardless (privacy commitments). Also evaluate the official Batch API (50% off, zero middleman) as the default cost lever for precompute. Key: "KIE API Key" at the bottom of the Keys file. Review KIE's data-retention/DPA terms before any production flip. |
| 49 | Threat detection Phase 1+ — act on verdicts | pending | Phase 0 (shadow mode) is live (verdicts logged, nothing hidden). Phase 1+: build a labeled eval set; implement moderation_service._apply_decision (set hide_reason='threat_hidden' on auto_hide) + surface flag/auto_hide verdicts in the admin queue; wire the self-harm resources flow; then flip moderation_policy.SHADOW_MODE off for Phase 2 (doxxing + credible_threat only) AFTER attorney review of the policy (docs/LEGAL-REVIEW-ROADMAP.md). See docs/threat-detection-prd.md §11. |
| 101 | Rate-limit engagement writes (comments / reactions / votes) | done (2026-06-10) | Shipped: services/rate_limit.py (shared sliding-window, exact trailing window, Retry-After) + middleware/rate_limit.py (ENGAGE 30/60s on comments/reactions/votes/reports, CREATE 10/10min on poll+post creation, keyed by session token, IP fallback; 429 code='rate_limited'). Demo-signup limiter migrated to the same service (5/24h per IP unchanged). In-process — exact on the single Render worker; revisit Redis-backed buckets only if we scale workers. |
| 102 | Citizen dashboard Overview/Settings split + start-page preference | done (2026-06-10) | Dashboard split into Overview (civic content) + Account & settings views; new CitizenAccount.start_page + PUT /api/citizen-auth/me/start-page (allowlist: home/polls/posts/bills/dashboard/stats) + StartPageSection + once-per-session redirect in app/page.js. Deep links: /?open=tracked|dashboard|settings. Reps/candidates can get the same preference later if it earns its keep. |
| 103 | Audit follow-ups 2026-06-10 (verify + fix batch) | done (2026-06-12) | Completed 2026-06-12 (verify-and-fix pass over all four items). Original scope from the backend audit agent: (a) poll auto-supersede race under concurrent creates can exceed the 20-poll cap; (b) comment most-liked/disliked sort runs in Python — push into SQL; (c) confirm whether the posts feed omits candidate-authored posts (flagged, unverified); (d) browseReps in the dashboard only closes the overlay — consider scrolling to the officials panel. |
| 104 | Weekly civic digest email | done (2026-06-10) — sending gated on DIGEST_ENABLED | Opt-in (CitizenAccount.digest_opt_in, default OFF) Saturday-9am-ET email: tracked officials' posts/polls this week, polls closing ≤7d, district events ≤14d. Empty digest = no send; demo-domain emails never sent; per-citizen digest_last_sent_at idempotency. In-process scheduler in main.py lifespan — set DIGEST_ENABLED=true in Render env to turn on. Preview: GET /api/citizen-auth/me/digest/preview + Settings card. |
| 105 | Tracked-official in-app alerts | done (2026-06-10) | emit_tracked_content_notifications fans kind='tracked_post' rows to all tracking citizens when an official posts (BackgroundTask + own session, zero poster latency). Bell renders kind-aware copy. Email stays consolidated in the digest (#104). |
| 106 | Notifications/digest follow-ups (deferred) | pending (a, b, e done; c mostly done) | (a) poll-close alerts, done 2026-10-17 ("Results are in" to the citizens who voted, services/poll_results.py); (b) per-item deep links in digest emails (post/poll URLs, not just the app link), done 2026-10-16 (every digest item names the official and links to it); (c) instant-email alert opt-in per alert type + unsubscribe handling, the unsubscribe half done 2026-10-16 (one-click RFC 8058 links on every email a person can turn off), and per-type email choices done 2026-10-17 where an email exists (bill updates, election reminders, page changes, the digest); (d) compare share URLs (/compare?a=…&b=…); (e) 413c1e2: #post- hash now drives PageView scroll+pulse for bell clicks AND push taps). |
| 107 | CI hardening — CodeQL advanced + Bandit/ruff gate | done (2026-06-10) | CodeQL advanced setup + Bandit/ruff security gate added to CI; fixed the upload-permission failure (9534fe8). |
| 29 | Congress data load-time / caching pass | done caching (2026-06-16) | Edge caching live + VERIFIED (cf-cache-status: HIT). backend/app/main.py Cache-Control middleware on public read-only endpoints + startup cache warmup; API served from Cloudflare-proxied api.civicview.app + a Cache Rule; NEXT_PUBLIC_API_URL switched. Deferred (optional, not currently needed): full per-member disk precompute of detail/bills/votes; frontend skeletons/prefetch. |
v1.1.0docs/push_notifications_runbook.mdocd-bill/...services/bill_updates.pyfeat/web-pushscripts/generate_vapid_keys.pyVAPID_PUBLIC_KEYVAPID_PRIVATE_KEYVAPID_SUBJECT=mailto:[email protected]civicview-apiNEXT_PUBLIC_VAPID_PUBLIC_KEYdocs/push_notifications_runbook.mdC:\dev\US apps\Keys\s-{congress}-{session}-{vote}