CONTRIBUTING.md
Rama main · réplica de lectura del repositorio original.
# Contributing
Thanks for helping out. A few things keep reviews quick:
- **Sign the CLA** when the bot asks on your first pull request.
- **Format before you commit:** `yarn format` runs Prettier (the repo's
`.prettierrc`) on the files your branch changes. CI checks the same files
and fails when one is not formatted.
- **Lint and types:** `yarn lint` and `yarn typecheck:ratchet` (no new type
errors).
- **Tests:** add or update a spec under `tests/` for what you change; `yarn
test:api` runs the API specs against a local stack (`scripts/test-e2e-all.sh`).
- **API routes:** after adding or changing one, run `yarn docs:api` and commit
`docs/API-REFERENCE.md` and `docs/openapi.json`. It needs no database: the script
sets `AT_NO_DB=1`, which makes the DB layer refuse to connect (and the pool is
only created on first query anyway).
- **Strings:** add English in `client/src/locales/en` (and the CS2 module's
`locales/en.json`), then `yarn --cwd client i18n:sync` fills the other
languages.
- **Migrations:** a new module migration takes the next free number and goes
at the end of the list.
## PR size
Every pull request is read line by line, so one that can't be read in one
sitting doesn't get a real review. Keep each one small, and if an AI agent
writes it, have it use the
[`ponytail`](https://github.com/DietrichGebert/ponytail) skill: the smallest
change that fully solves the task.
- Aim for under ~10 files and a few hundred changed lines per PR.
- A bigger change goes in several PRs, all opened at once: each one complete
on its own (it builds, passes its tests, leaves nothing half done) and
reviewed and merged in order. From a fork, open each against `main` and
say in the body which PR it builds on ("depends on #N"); its diff shrinks
to its own commits once that one is merged. Branches in this repo can use
GitHub's
[stacked pull requests](https://docs.github.com/en/pull-requests/how-tos/create-pull-requests/creating-stacked-pull-requests)
instead; they do not work across forks.
- No autofix sweeps, renames or reformatting across files as a side effect of
something else. A new lint rule lands with its existing violations listed in
the lint config, and the fixes follow in small PRs of their own.
- Mechanical changes go in a PR of their own, never mixed into one that
changes behaviour.
- New files are welcome; keep each under 450 lines of code (blank lines and
comments don't count; lint checks it). A file that grows past that gets
split, in a PR that only moves code.
## Types
Lint enforces these, so `yarn lint` tells you before review does.
- Values our own code makes or stores have their real type. No `unknown`,
no `any`, no `as unknown as`.
- Data from outside (a request body, a fetch, a game server or recorder
message, a file someone uploaded) comes in as `Untrusted`
(`api/src/utils/untrusted.ts`, the same in the client; a module imports it
from `integrations/types`). Check it once where it arrives, and pass the
checked, typed value on.
- SQL parameters and rows are `SqlValue`. JSON you build is `JsonValue` /
`JsonObject`.
- No nested ternaries, no `x!`, no `{ … } as T` (type the variable instead),
`import type` for types, `===`.
- When a cast really can't be avoided (two copies of a library's types, a
library's private field), use `eslint-disable-next-line … -- reason`, with
the reason.