CivicLens ingests structured payloads from third-party content feeds, maps them to geographic entities, and guides users through six escalating engagement levels.
Schema-versioned payload
Each record carries entity_id, jurisdiction, category, severity, and supporting refs.
State + ZIP → entities
State is the primary filter. ZIP narrows. ZIP alone is never sufficient.
Match user interests
Records filtered by interests + jurisdiction, ordered by relevance and severity.
function classify(payload) {
// 1. Jurisdiction lookup (state required)
const entity = oversight_refs.find({ state: payload.state, zip: payload.zip });
if (!entity) return route("unmapped_queue");
// 2. Severity by keyword + impact metrics
const severity = scoreSeverity(payload.body, payload.metrics);
// 3. Category routing
const category = matchCategory(payload.tags, taxonomy);
// 4. Engagement template binding
const templates = action_templates.where({ active: true, level: { lte: 6 } });
return { entity, severity, category, templates, status: "open" };
}View record summary
Amplify socially
Public comment
File records request
Upload documents
Show up in person
Six main tables, UUID primary keys, row-level security on all PII.
entitiesTracked public bodies
findingsCivic records & content
action_logUser engagement events
user_profilesParticipant profiles
oversight_refsJurisdiction mapping
org_accountsOrganization tenants
Users who take a logged action (any level 1–6). Counted in findings.participants.
Users who view but don't act. Counted in findings.observers. Never merged with participants.
Every user row in user_profiles and action_log is scoped to its owner. Admins query through service-role policies; participants only see their own activity.