AI Advocate is Love Never Fails’ companion application for survivors, allies, and advocates who need a precise view of the policy landscape. The Expo client (web + iOS + Android) surfaces curated California legislation, rewrites dense bill text in plain language, pairs every item with outreach tooling, and now highlights real vote history for each representative the moment it becomes available.
Status: Production (v1.0). Store builds and the responsive web client share one codebase. Supabase manages authentication, data pipelines, translations, vote ingestion, and scheduling.
- Highlights
- Architecture
- Edge Functions & Automations
- Data Model Overview
- Local Development
- Testing & Quality Gates
- Operations & Runbooks
- Release Process
- Repository Layout
- Security & Privacy
- Support
| Experience | What users see | Under the hood |
|---|---|---|
| Active Bills feed | Prioritised list of California survivor-focused legislation with search, session toggle, and curated pins | Supabase bills view + Postgres websearch. Ranks by curation → score → recency. Session tags parsed from state_link and cached client-side. |
| AI-powered summaries | Simple / Medium / Complex write-ups in English & Spanish on every card and detail page | sync-updated-bills cleans LegiScan text, prompts OpenAI gpt-4o-mini once for both languages, stores summaries + text-embedding-3-small vectors. |
| Representative lookup with context | Address-based finder that lists state legislators only and shows their freshest vote on the active bill | find-your-rep edge function caches OpenStates lookups. The app cross-references legislators + v_rep_vote_history, falling back to chamber-aware “not eligible yet” messaging when an assembly vote is still pending. |
| Legislator vote history | Timeline of votes with filters on profile screens plus outreach templates | v_rep_vote_history invoker-secured view joins vote_records, vote_events, bills, and legislators. votes-backfill hydrates historic rolls; votes-daily watches for updates. |
| Bookmarks & reactions | “Saved” tab, emoji reactions, and toast feedback that stay in sync across devices | RPC helpers (toggle_bookmark_and_subscription, handle_reaction) wrap row-level security, with realtime channels broadcasting optimistic updates. |
| Love Never Fails hub | Brand-first landing tab with storytelling, give/volunteer CTAs, and external deep links | Native platforms use a WebView; the web client renders card CTAs to respect CSP rules. |
| Accessibility & localisation | Locale switcher, reader-friendly typography, and TTS prompts | i18next + React Native Paper tokens drive the theme. Translations live in bill_translations; Expo Speech powers narration on supported devices. |
Expo Router (iOS / Android / Web)
├─ React Query & TanStack • React Native Paper • i18next
└─ Custom hooks for auth, localisation, analytics
▼
Supabase (PostgREST • Edge Functions • Realtime • pgvector)
├─ REST + RPC surface secured by row-level security
├─ Edge Functions (Deno, TypeScript)
└─ PostgreSQL with pg_cron, vault, and verification scripts
- Client – Expo Router manages navigation, deep links, and tab layouts. React Query caches Supabase responses. Paper’s Material 3 theming is centralised in
mobile-app/constants/paper-theme.tsso the brand palette stays consistent. - API – Public clients read via the PostgREST API with
anonorauthenticatedroles. All writes go through RPC helpers or edge functions, keeping RLS intact. - Automation – Cron triggers in Supabase orchestrate bill ingestion, summary generation, vote syncing, and email scheduling. Verification SQL scripts (e.g.,
supabase/verification/20251021_vote_history.sql) guard against regressions before deploys.
| Function / Task | Purpose | Trigger | Notes |
|---|---|---|---|
bulk-import-dataset |
Pulls the latest CA legislative dataset, filters to survivor-centric bills, and stages rows for review. | Manual / cron | Use before sync-updated-bills when new sessions drop. |
sync-updated-bills |
Sanitises bill text, generates bilingual summaries, stores embeddings, and flags completion. | Nightly cron + manual | Idempotent; respects job_state checkpoints. |
votes-backfill |
Hydrates historical vote events and member records from OpenStates. | Manual | Accepts force=true query param to reprocess populated bills. |
votes-daily |
Incremental poller that syncs new vote events since the last run. | Scheduled via supabase functions schedule create votes-daily ... |
Updates job_state (votes-daily:last-run). |
find-your-rep |
Cached address → legislators lookup with OpenStates + LocationIQ. | Client invoked | Added provider ID matching so state reps surface even when names vary. |
translate-bill / translate-bills |
On-demand translation helpers that reuse cached content when available. | Client invoked | Works alongside the nightly summary job. |
summarize-simple / summarize-simple-backfill |
Legacy summarisation utilities retained for specialised queues. | Manual | Useful for targeted re-summarisation. |
send-push-notifications |
Service-role only function for Expo push fan-out. | Cron / manual | Requires vault-managed Expo credentials. |
verify-app-check |
App Check handshake for the client. | Client invoked | JWT disabled per mobile requirements. |
ingest-and-summarize |
Local mock harness demonstrating ingestion without external APIs. | Manual | Safe sandbox for new contributors. |
Cron topology: Supabase’s pg_cron schedules call
votes-dailyandsync-updated-bills. The production environment also runs a nightlyinvoke_full_legislative_refreshworkflow (seesupabase/scripts/) to chain dataset pull → summaries → vote sync.
Key tables & views:
bills,bill_translations,bill_embeddings– curated legislation, multilingual summaries, and semantic search vectors.legislators– unified roster keyed by OpenStates provider IDs with lookup keys for fuzzy matching.vote_events,vote_records– normalised roll call data. Policies ensure service-role write access whileanon/authenticatedstay read-only.v_rep_vote_history– invoker-secured view feeding representative profile screens and the lookup tool.job_state– generic checkpoint table used by every long-running function.location_lookup_cache– throttling layer for address → representative resolution.bookmarks,reactions,user_push_tokens– user-generated signals powering engagement features.
Recent migrations (2024-09 through 2025-10) align RLS policies with Supabase guidance, ensure v_rep_vote_history grants, and harden audit coverage. Run supabase/verification/20251021_vote_history.sql after each deploy to confirm security posture and data availability.
- Node.js 20+, Yarn 1.22+, Git, Supabase CLI ≥ 2.48 (upgrade regularly), and Watchman (macOS).
- Expo tooling (
npm install -g eas-clioptionally) and either Android Studio, Xcode, or a web browser for the Expo target. - Access to required secrets: OpenStates, LocationIQ, LegiScan, OpenAI, Supabase keys.
git clone https://github.com/JoelA510/AIAdvocate.git
cd AIAdvocate
# Mobile app dependencies
cd mobile-app
yarn installmobile-app/.envEXPO_PUBLIC_SUPABASE_URLEXPO_PUBLIC_SUPABASE_ANON_KEYEXPO_PUBLIC_OPENSTATES_API_KEYEXPO_PUBLIC_LOCATIONIQ_API_KEY- Optional extras:
EXPO_PUBLIC_LNF_URL,EXPO_PUBLIC_RECAPTCHA_SITE_KEY, Firebase config.
supabase/.envSUPABASE_URL,SUPABASE_SERVICE_ROLE_KEYLEGISCAN_API_KEY,OPENSTATES_API_KEY,OpenAI_GPT_KeyDB_URL(percent-encode the password when using--db-url)- Any downstream service secrets (Expo push, Gemini, etc.).
Never commit .env files; rely on Supabase Vault or CI secrets.
cd ../supabase
supabase start # launches local Postgres + Studio
supabase db push # applies migrations, policies, seed data
# Optional: run edge functions locally
supabase functions serve sync-updated-bills
supabase functions serve votes-backfill
supabase functions serve votes-daily
supabase functions serve find-your-repcd ../mobile-app
yarn expo startChoose web, iOS simulator, or Android emulator from the Expo dev tools. The app auto-loads translations and Supabase config on boot; run expo-doctor if dependencies drift.
# Pull & summarise new bills
supabase functions serve bulk-import-dataset
supabase functions serve sync-updated-bills
# Refresh legislators & vote records
supabase functions serve votes-backfill
supabase functions serve votes-daily| Command | Purpose |
|---|---|
yarn lint |
ESLint + custom rules for the Expo client. |
yarn test |
Jest unit tests for shared utilities/components. |
yarn tsc --noEmit |
TypeScript project-wide type check. |
npx supabase gen types typescript --local |
(Optional) Regenerate database types for client consumption. |
supabase db verify |
Run verification scripts (requires CLI ≥ 2.50). |
Add tests for new pipelines wherever possible. For data migrations, pair them with verification SQL under supabase/verification/.
supabase functions deploy votes-backfill
supabase functions deploy votes-daily
# Trigger a controlled run (service-role token required)
python scripts/backfill-votes.mjs # or call the HTTPS endpointPassing force=true in the query string reprocesses bills even if votes already exist.
- Inspect
job_state→votes-daily:last-runtimestamp. - Review logs in the Supabase dashboard for
votes-daily. - Run the verification SQL to ensure
v_rep_vote_historyreturns rows.
supabase functions invoke sync-updated-bills --no-verify-jwt --body '{"force":true}'Ensure OpenAI quotas are available before running large batches.
Clear stale entries with a simple SQL job:
DELETE FROM location_lookup_cache WHERE updated_at < now() - interval '30 days';Schedule through pg_cron if needed.
- Versioning – bump
app.json(version,buildNumber,android.versionCode) and update any in-app version banners. - Changelog – summarise user-facing changes and note database migrations applied.
- Quality gates – run the commands listed in Testing & Quality Gates.
- Supabase schema –
supabase db push --include-allto ensure the remote project matches migrations. Run verification scripts. - Edge functions –
supabase functions deploy <name>for any touched functions, then smoke test via HTTPS. - EAS builds –
eas build --platform android --profile productionandeas build --platform ios --profile production. Attach release notes. - Launch checklist – confirm
votes-dailyschedule is active, check monitoring dashboards, and communicate release highlights to Love Never Fails stakeholders.
AIAdvocate/
├── mobile-app/ # Expo project
│ ├── app/ # Router-based screens and layouts
│ ├── components/ # Shared UI primitives (Bill, VotingHistory, etc.)
│ ├── constants/ # Brand palette and Paper theme definitions
│ ├── hooks/ # Platform-aware utilities (color scheme, viewport)
│ ├── src/lib/ # Supabase client, lookup helpers, analytics
│ ├── src/providers/ # Auth, i18n, language contexts
│ ├── types/ # Type augmentation (MD3, navigation)
│ └── assets/ # Images, fonts, icons
└── supabase/
├── migrations/ # SQL schema, policy, and view migrations
├── verification/ # Post-deploy validation queries
├── functions/ # Deno edge functions (bill ingestion, votes, lookup)
├── scripts/ # Developer tooling (cron orchestration, diagnostics)
└── config.* # Supabase project configuration
- No survivor-identifying information is collected. Anonymous auth tokens map only to Supabase session IDs.
- Secrets live in environment variables or Supabase Vault; never hardcode keys in the client.
- Edge functions redact third-party API payloads before logging. Only aggregate diagnostics flow into
cron_job_errors. - Row-level security denies writes from
anon/authenticatedroles; mutations must go through RPCs/edge functions with explicit policies.
Questions, bug reports, or feature requests? Reach out to Love Never Fails or open a GitHub issue. Every improvement helps survivors stay informed, empowered, and safe.