Contributing#
Contributions are welcome: bug fixes, tournament-workflow improvements, accessibility, translations, documentation, tests and ideas. This page summarises how work flows into CrabbyTab. The source documents are:
- CONTRIBUTING.md: setting up, making a change, opening a pull request.
- AGENTS.md: the engineering rules, for people and AI coding agents alike.
- CODE_OF_CONDUCT.md.
- SECURITY.md: report vulnerabilities privately with GitHub's Report a vulnerability, never in a public issue.
Contributions are distributed under AGPL-3.0-or-later.
Branches and pull requests#
| Branch | Role |
|---|---|
beta |
Where development is integrated. Feature branches start here and pull requests target it. |
main |
The default branch on GitHub. It receives beta when a release is cut; it is currently behind beta. |
| feature branches | One per change, named after it (for example claude/p2-results-pages). Merged into beta by pull request. |
The workflow:
- For a substantial change, open an issue first so the scope can be agreed.
- Branch from
beta. Keep the branch to one fix or feature. Ifbetamoves on, merge it into your branch. - Make the change, with tests and documentation (below).
- Run the checks locally (below).
- Open a pull request into
betausing the template: summary, related issue, changes, verification, screenshots for UI changes, and any configuration, data or migration implications.
CI runs lint, the type check, the tests and the build on every push and pull request; all must pass.
Local setup#
Node.js 22 (package.json sets engines.node to 22.x) and npm. See
Installation and local development.
npm ci # install exactly what package-lock.json records
npm run dev # http://localhost:3000
Copy .env.example to .env.local only if your change needs Firebase or email. Ordinary tests need no
credentials.
Engineering rules in short#
From AGENTS.md:
- Read first. Inspect the existing code before changing it; the code is the source of truth. Reuse existing helpers rather than duplicating them. Keep changes focused; do not redesign unrelated pages.
- Local-first. Draws, allocation, standings and validation run in the browser as deterministic functions, separate from React components. Do not add paid services or remote computation.
- Tournament integrity. Never silently relax a hard draw constraint, discard a team, or treat a missing result as zero. Report constraints that cannot be met. Randomness takes an explicit seed, so the same inputs and seed give the same draw. Do not claim a draw is optimal unless the algorithm proves it.
- Document trade-offs. Heuristics, tie-breaking rules and known differences from Tabbycat are written down in
the code and in
docs/(seetabbycat-parity-plan.md, "Still different from Tabbycat"). - Security. Enforce access in
firestore.rulesor server routes, never only in the UI. Keep secrets out of the client bundle. Do not weaken the rules to work around a bug. Never log tokens or participant data. - Data. Validate before saving; use transactions or batches for multi-step writes; never silently overwrite newer changes; keep stored data backward compatible.
- UI. Accessible labels, keyboard use, visible focus, status not by colour alone, loading/empty/error states, and protection against double submission. New user-facing text must be translatable (below).
- Dependencies. Use the existing stack (Next.js 15, React 19, TypeScript, Tailwind 3, Firebase, lucide-react, PapaParse, Vitest). Do not add a dependency for something small, and never upgrade a major version as part of an unrelated change.
- Git. Do not rewrite history on shared branches or commit secrets,
.env.local, build output ornode_modules.
Code style#
- TypeScript throughout. Avoid
any; prefer explicit types orunknownwith narrowing. Do not loosentsconfig.jsonor disable lint rules to make a change pass. - Small functions with clear names. Comments explain why (a domain rule, a Tabbycat reference, a trade-off), not what the code does.
- Business logic lives in
src/lib/**andsrc/features/tournament/application/; Firestore access insrc/features/tournament/infrastructure/and server routes undersrc/app/api/. See Architecture. - Components are server components unless they need interactivity or browser APIs (
"use client"). - Styling with Tailwind and the shared components in
src/components/ui/; see design-system.md. For right-to-left languages use logical utilities (ms-*,pe-*,text-start). npm run lintmust be clean.
Tests#
Every change to business logic needs a test: draws and constraints, byes and side balance, standings and tie-breaks, ballots and scoring, breaks, CSV import, permissions and error cases. Include the infeasible case, not just the happy path, and a fixed-seed reproducibility check for anything random.
Before opening a pull request, run what CI runs:
npm run lint
npx tsc --noEmit
npm run test
npm run build
If your change alters draws, standings, breaks or allocation, the Tabbycat differential test may report a changed
discrepancy. Review it, record a verdict in tools/tabbycat-diff/known-differences.json, and regenerate the
report. Details, and how to run single files: Testing.
If you cannot run a check, say so in the pull request and list what you did run.
Documentation to update#
Update docs in the same pull request when a change affects setup, environment variables, commands, tournament rules or draw behaviour, data formats, security or user-facing workflows.
Developer docs in
docs/*.md(the detailed, per-area reference). The main ones:File Area draw-generation.md,adjudicator-allocation.md,tournament-rules-enforcement.mddraws, allocation, rule checks results-and-ballots.md,ballot-intake.md,standings-and-breaks.mdresults and the break participants.md,registration.md,data-import-export.mdparticipants, registration, CSV, DebateXML public-pages-and-notifications.md,email-notifications.md,rounds-schedule-and-motions.mdpublic site, email, schedule, motions staff-access.md,staff-invitations.md,api.mdroles, invitations, the REST API storage-and-scale.md,live-collaboration.mddevice storage, upload, live sync, Firestore cost internationalization.md,design-system.mdlanguages, UI components tabbycat-parity-plan.mdthe roadmap and "Still different from Tabbycat" This wiki, which lives in the repository under
docs/wiki/. Edit the pages there in your pull request, not in the GitHub wiki editor. Links between pages use the page name without.md([Testing](Testing)); links todocs/*.mduse full GitHub URLs on thebetabranch. Describe only implemented behaviour and mark limitations plainly.When
docs/wiki/**changes onmainorbeta, the Pages workflow (.github/workflows/pages.yml) renders the pages into the guide on the project website and, if theWIKI_TOKENsecret is set, copies them to the GitHub wiki (pages are added or updated, never deleted). To preview the guide locally, see site/README.md..env.examplewhen you add a configuration variable (placeholders only).
Translations#
All user-facing text goes through the i18n catalogues, and every English key must exist in every other catalogue,
or the tests fail. When you add a string, add the key to every locale (copy the English text if you cannot
translate it, and say so in the pull request). Every non-English catalogue is machine-drafted, so corrections
from fluent speakers are especially welcome. How to add a string, add a language and run npm run i18n:check:
Languages and translations.
Lockfiles#
package-lock.jsonis the authoritative lockfile; CI installs withnpm ci. If you change dependencies, updatepackage.jsonandpackage-lock.jsontogether with npm.- The repository also contains a
bun.lock. Do not delete or regenerate it casually, and do not switch the project to another package manager. - Build-time tools for the website (
marked) are installed into a temporary folder and are deliberately not inpackage.json.