Saj Chandoo

Portfolio: Organizer turned data builder

Everything I build starts with the person, not the category.

Your voice, not a score. Your words, not “people like you.” Your identity, not a list.

Organizer turned data builder. By day I run the direct response data program at the Human Rights Campaign, across tens of millions of records. By night, two live apps and a third in the wings.

The philosophy

I started by knocking on doors, then training canvass teams and running field operations across multiple states through an election cycle. Most data people do not learn this from a textbook, but doors teach it fast: data only matters if it reaches the person who needs it, in a form they can actually use.

Every pull I run, every suppression list I build, every audience I cut comes back to that (the lesson does not change, only the scale does). The people closest to the problem should be the ones shaping the tools. That is the line from organizing to data to building: figure out what people actually need, then make the thing.

The work

Three apps, one habit: each one refuses its category's default. CivicRadar refuses to store who you are. Euphora refuses to score what can't honestly be measured. The third, a still-unnamed taste portrait in development, refuses to average you into people like you. Every case study ends with that choice and what it cost.

CivicRadar

Live

CivicRadar takes every identity and issue you actually hold, queer, immigrant, on Medicaid, whatever the combination, and gives you one feed of the bills that touch any of them, each with the relevant advocacy orgs' positions attached. No following a separate org for every part of your life.

27
identity tags
162
org files
46
states covered
17
jurisdictions live
  • Matching is a deterministic keyword scorer, not a model call, weighting 27 identity tags above 26 issue tags.
  • AI enters at one point only: drafting the email and call script, on Claude Haiku. It never decides the match.
  • 162 reviewed org files across five advocacy networks, covering 46 states, joined so every org's stance on a bill shows on one card, not just one group's opinion.
Nothing a user tells the app is stored server-side. For a queer immigrant on Medicaid, the risk is not that the tool fails to help, it is that it becomes a list somewhere with your name next to "trans" or "undocumented." There is no server-side table to subpoena because it does not exist.

Next.js 16, React 19, TypeScript. Tailwind v4. Upstash Redis, Cloudflare R2, Upstash Vector. Claude Haiku (drafting), Gemini Flash (summaries), Voyage embeddings. OpenStates, LegiScan, Congress.gov, Legistar. Vercel. AGPL v3 once the repo goes public (the org-positions corpus stays out of it; the data is the actual moat, not the code).

Euphora

Live

Euphora is a speech and voice practice app built around one refusal: it will not score your fluency with AI.

5
lanes
6
courses
42
lessons
0
fluency scores
  • Feedback is tiered by whether the measurement is actually valid, not by what demos well. Pitch is measurable, so on-device tracking covers pitch and resonance for the voice masculinization and feminization lanes.
  • Rate metrics never touch transcription, and they are switched off entirely for the stuttering lane, because rate pressure worsens blocks.
  • Every technique carries an evidence tier, A through D. No coached technique is rated A.
The refusal is enforced in the codebase, not a policy page. A unit test fails if the self-rating rubric ever mentions stuttering, blocks, or disfluency.

Why I built it

I stutter, and canvassing taught me to keep talking through the fear. Structure did the rest, so I built the structure into an app. I still stutter. That is the point.

TypeScript, React, Vite SPA, installable PWA. Capacitor for iOS and Android. On-device DSP (pitchy, custom FFT/LPC). Dexie/IndexedDB and Capacitor Filesystem. Cloudflare Workers. No backend, no accounts, no analytics.

Live at euphora.app. Native builds packaged, not yet store-submitted.

Untitled taste portrait

In development and unnamed

A taste portrait built from your own ratings and your own words, not a watch log and not a collaborative-filtering black box. Movies, TV, games, and books in one place.

587+
tests
4
media types
5
taste axes
280
char verdict cap
  • Your free-text flavor tags get weighted by frequency and rating, then rendered as a hand-built word cloud. No chart library involved.
  • Recommendations run two-stage: a plain scorer builds the candidate pool from TMDB, IGDB, and Open Library, then Gemini curates from inside that pool only, grounded in your own verdicts.
  • Claude gets exactly one job: the extended-thinking deep portrait.
Your own words over other people's behavior. A smaller signal than a million watch histories, but yours, and it stays legible instead of getting averaged away. Deliberately the opposite of CivicRadar on privacy: server-side with accounts, because taste is meant to be shared, where CivicRadar keeps user data localStorage-only because its users' risk is ending up on a list.

Next.js 16, React 19, TypeScript. Tailwind, Fraunces display type. PostgreSQL (Neon, Drizzle). TanStack Query. Auth.js. Gemini Flash (curation), Claude Sonnet (deep portrait). TMDB, IGDB, Open Library. Vercel.

Fully built and tested, not yet deployed.

Also building

A row under the three apps for real builds that are not full webapps.

Genealogy analyzerPersonal tool, on GitHubA Python engine that reconciles DNA match data from four incompatible clustering tools into one paternal-only report.

A five-method convergence score separates a real surname lead from a coincidence. It also does narrative work no other project can: it is the "start with the person, not the category" thesis applied to reconstructing my own family, and it predates every shipped app, so it reads as the honest origin of the whole approach.

GitHub →
Master ScribePrivate build, shown not linkedA pipeline that turns voice and meeting transcripts into structured output across four destinations.

Built with the rigor of a system handling someone else's data. It carries the site's second-strongest governance story: a quarantine gate that refuses the most sensitive content on every unattended path (human-present processing only), and a deterministic clinical-safety check the model is never asked to perform, now tested in two independent implementations. The repo is permanently private, since the prompt engineering is inseparable from real names; it shows here the way the life dashboard does: architecture and pattern, with no link.

The day job

Data Manager, Human Rights Campaign

Every direct response pull at the largest LGBTQ+ advocacy organization in the country runs through me: direct mail, digital and SMS, telemarketing, and events. That means audience selection, suppression logic, and quality control across tens of millions of records spanning a CRM, a data warehouse, and the voter file. The stakes are real: a bad pull can torch tens of thousands of dollars, or put the wrong message in front of the wrong person.

The role keeps widening

  • A 208,000-row, multi-year unified analysis of eight years of event attendees that changed how invite lists get built
  • The team's GitHub governance and project intake
  • A Copilot adoption guide for the team, with separate reading paths for technical and non-technical staff
  • The semantic layer for the AI analytics platform

I got here by an unusual path: organizing, then donor databases, then the full operational picture. When I took this role I did not know SQL, which the job needed for about eighty percent of the work; I taught myself, took over the full program within six months, and have not stopped building since.

Stack

Core, daily, at scale

  • SQL at scale (Redshift, Oracle, Azure)
  • enterprise CRM
  • cloud data warehouse
  • voter file
  • audience and suppression logic
  • BI and semantic-layer tooling
  • Git and GitHub

Build with, shipped with

  • TypeScript
  • React
  • Next.js
  • Vite
  • Tailwind
  • Capacitor (iOS/Android)
  • PostgreSQL (Neon, Drizzle)
  • on-device audio DSP (Web Audio, custom FFT/LPC)
  • Vercel
  • Cloudflare Workers
  • REST and third-party APIs (OpenStates, LegiScan, Congress.gov, TMDB, IGDB, Open Library)
  • LLM integration (Claude, Gemini)

Working knowledge, growing

  • Python (helper scripts and query automation at work, Pandas in the genealogy analyzer)

Systems

Boring reliability: systems that hold at any energy level, not just peak performance. Proof: a private ops dashboard I live in, sixteen services pulled into one static page, rebuilt six times a day, with eighteen scheduled workflows and thirteen Claude Code routines running under it. Of the repo's last seventy commits, eight were typed by a human.

Private build

A private ops dashboard that pulls sixteen services into one static page, calendar, email, health, money, tasks, reading, contacts, commute, weather, and my own knowledge base among them, rebuilt six times a day behind real auth.

The interesting parts are derived, not displayed:

  • A recovery score from sleep and heart-rate-variability deltas against a rolling baseline
  • Step counts deduped across devices
  • Cross-calendar conflict detection
  • A priority cascade for how each evening gets used
  • A money burn-rate projection
  • A staleness backstop that recomputes freshness at render, instead of trusting its own last-good status

The automation layer

A Cloudflare Worker cron exists because GitHub's native scheduler is unreliable. The Claude Code routines fire on their own schedules and commit their own data: a morning kickstart that drafts the day's top three, an evening brief, weekend planning, meal planning among them. They run under a written house convention: idempotent, fail loud rather than serve stale, least privilege, plus a safety-net workflow whose only job is merging the automation's own branches. Running unattended AI agents against your own life data forces you to write the rules you would want an org to write. I did.

Honest state

Not everything works. The budget integration sat broken for seven weeks on an expired token. A phone-notification bridge is wired end to end and has never received real data. The repo's own review docs say so, and ten dead components have already been cut rather than left to rot.

Astro (static, near-zero JS) with React islands, TypeScript, flat JSON in a private repo as the entire data layer, Cloudflare Pages and Workers, Cloudflare Access OTP enforced at three layers, GitHub Actions for scheduling.

8 of the last 70 commits, typed by a human.