Skip to content

Contributing

FloorVote is open source, and contributions are welcome.

Local development

Prerequisites: Node.js 22+, npm.

bash
# Install dependencies (root manages api/ and web/ workspaces; central/ is standalone)
npm install
cd central && npm install

# Local secrets — copy the annotated example (every key is optional for demo dev)
cp api/.dev.vars.example api/.dev.vars
cp central/.dev.vars.example central/.dev.vars   # only if you work on central

# One-command seeded local dev — fresh D1, auto-login, api(8787) + web(5173)
npm run dev:local

# Or run api + web together without seeding
npm run dev

Open http://localhost:5173. With dev:local, login is automatic (demo mode).

Running tests

bash
cd api && npm test       # tenant worker tests (vitest + @cloudflare/vitest-pool-workers)
cd central && npm test   # central worker tests
cd web && npm test       # frontend tests (vitest + jsdom)

API and central tests use real D1 bindings — no mocked databases.

Coding conventions

The full rules live in the Developing section of AGENTS.md in the repository, which is also what coding agents read. Key points:

  • Inline styles with design tokens from web/src/styles/tokens.ts — no raw colors or font sizes
  • Register new Material Symbols icons in web/index.html before using them
  • New migration files only — never edit existing ones
  • Timestamps in SQLite space-format UTC, never ISO

Pull requests

  • Keep changes focused — one feature or fix per PR.
  • Run npm test in api/, central/, and web/ before submitting.
  • Run npm run build from web/ to catch type errors (vitest does not run tsc).
  • Describe what changed and why in the PR body.

Licensing and sign-off

FloorVote is licensed under AGPL-3.0. By submitting a pull request, you agree to license your contribution under the same license, and you retain copyright in your own work.

Every commit must carry a Signed-off-by line certifying the Developer Certificate of Origin — a short statement that you wrote the code, or otherwise have the right to submit it. The full text is in DCO at the repository root.

Git adds the line for you with -s:

bash
git commit -s -m "Fix vote tally rounding"

The name and email must match your commit author details. A bot checks this on every pull request and fails if any commit is missing a sign-off. To fix a branch after the fact:

bash
git rebase --signoff main    # sign off every commit on the branch
git push --force-with-lease

There is no CLA to sign and no form to fill out — the one line in the commit is the whole process.

Forking and operator overlays

FloorVote is deployed per-team, and each instance is tied to a specific Cloudflare account, tenant domains, and secrets. Because of that, a fork that runs its own instance will tend to accumulate an operator overlay: files like wrangler.toml, deploy scripts, and an internal ops runbook that are meaningful only to that operator's deployment and should never appear in the public repository. If you fork to run your own instance, expect to build up the same kind of overlay. The guidance below is for sending a change from a fork like that back upstream.

Decide the base before writing any code, not after. If a change is general product code, it belongs upstream — branch from upstream/main for it, not your fork's main, from the start. Deciding this up front means the eventual PR is just a push and gh pr create; deciding only after building the feature on your fork's main means untangling the operator overlay from the change afterward.

bash
git remote add upstream https://github.com/floorvote/floorvote.git   # once, if not already set up
git fetch upstream
git checkout -b <branch> upstream/main    # branch from UPSTREAM, not your fork's main
# implement the change, or `git cherry-pick <sha>` if it already exists as a commit on your fork's main
git push origin <branch>
gh pr create --repo floorvote/floorvote --head <your-org>:<branch> --base main

Assert your overlay in CI, not in a written list. An overlay file that a merge silently deletes fails quietly — the branding disappears, or a deploy script stops matching your resource names, and nothing errors. Prose inventories of "which files are ours" go stale as the overlay moves. The durable version is a committed list of the paths you expect to differ, checked in CI against git diff --name-only upstream/main..main, which fails both when an overlay file goes missing and when a new divergence appears that should have gone upstream instead.

Note this only applies if your fork tracks its operator files. api/wrangler.toml and central/wrangler.toml are gitignored here, so an operator who clones rather than forks keeps them untracked, never merges into a divergent tree, and has no overlay to protect.

Do this in a separate worktree, not by switching branches in place, if your fork carries overlay files. Checking out an upstream-based branch in your primary checkout deletes every tracked file absent from that branch (for example, your wrangler.toml). Nothing is lost, since it's all still committed, but it breaks local dev and deploys until you switch back.

bash
git worktree add ../floorvote-upstream upstream/main
cd ../floorvote-upstream
git checkout -b <branch>
# ... push, open the PR ...
cd -
git worktree remove ../floorvote-upstream

Auditing what's genuinely fork-only: use git diff --stat upstream/main..main, not git log upstream/main..main. The log over-reports, because a fork commit stays listed even after its change reaches upstream via cherry-pick under a different SHA, while the diff shows the true net delta. Anything in that diff that's general product code, rather than operator config, is a candidate for a PR back upstream.

Going deeper

Maintainer-grade design documentation — the sync pipeline, the LegiScan API reference, email and calendar internals, and style and date conventions — lives in docs/internal/ in the repository, and is not published to this site.

Two interactive diagrams are the exception, since GitHub renders .html as source rather than as a page: the architecture dossier and the sync flow are served here as standalone pages.

Development supported by the Bipartisan Policy Center.Architecture and security have been reviewed and strengthened through a volunteer engagement with U.S. Digital Response.Bill data comes from LegiScan, licensed CC BY 4.0; summaries and relevance scores are generated by FloorVote. FloorVote reads legislative data through a provider interface, and LegiScan is the maintained implementation.FloorVote is open source under AGPL-3.0.