1. App-server boundary
The app never calls public APIs directly. All data goes through the bff Edge Function, and the app’s fixture JSON is the BFF response contract. The server’s contract.ts mirrors the Dart parsers so the two sides cannot drift.
2. Data ingestion
ingest-assembly and ingest-nec collect four National Assembly services and six NEC operations through nine pg_cron jobs. Plenary attendance has no open API, so per-session xlsx files are downloaded, checked against their total columns and loaded.
3. Address → district mapping
The district table in law is written against administrative dongs as of 2024-04-10. build_district_areas.ts matches today’s dong codes by code, then dong name within the same district, then legal-dong overlap, placing all 3,628 and stopping instead of guessing on ambiguity.
4. Publication limits as types
Restricted<T> is a sealed type without a value getter, so code that skips the withheld branch does not compile. Time is handled only as offset-aware KstInstant, and poll closing time comes from the server.
5. App structure
Each feature has data, domain, application and presentation layers composed with Riverpod 3 and a go_router shell. Retries apply only to dropped connections so 404 screens do not hang in loading.
6. Trust boundary and security
The server resolves districts itself through juso and V-World and ignores client-sent values. Only SHA-256 hashes of tokens are stored, never addresses or coordinates. Every table has RLS enabled with no policies, so only the service role can read it.
7. Caching and offline
The BFF caches per route: profiles 300 s, community 15 s, account no-store. When offline, the app shows the last response.
8. Open work
Live count streaming, server-side publication blocking and candidate analysis are not implemented. Pledge data has no API and is entered manually for a few districts.