# Contributing

Thanks for your interest in `@ironarachne/rng`. This document covers how to get
set up, how changes get merged, and how releases are cut.

## Getting set up

```bash
git clone https://github.com/ironarachne/rng.git
cd rng
npm install
```

Node 20 or newer is required. CI tests against Node 20, 22, and 24.

## Everyday commands

| Command            | What it does                                     |
| ------------------ | ------------------------------------------------ |
| `npm run check`    | Lint, build, and test — run this before pushing.  |
| `npm test`         | Run the Vitest suite.                             |
| `npm run lint`     | Check formatting and lint rules with Biome.       |
| `npm run lint:fix` | Apply formatting and safe lint fixes.             |
| `npm run build`    | Compile TypeScript to `dist/`.                    |
| `npm run docs`     | Generate the TypeDoc site into `docs/`.           |

`dist/` and `docs/` are build output. They are gitignored and generated by CI —
never commit them.

## How changes get merged

`main` is protected. Nothing is pushed to it directly; every change lands
through a pull request.

1. **Open an issue first** for anything beyond a small fix, so the approach can
   be agreed before you spend time on it.
2. **Branch from `main`.** Name it for the work: `fix/weighted-zero-total`,
   `feat/gaussian-float`, `chore/bump-biome`.
3. **Write the change and its tests.** Follow [CODE_STYLE.md](CODE_STYLE.md) —
   in particular the rules on TSDoc, error messages, and determinism.
4. **Run `npm run check`.** CI runs the same thing; catching it locally is
   faster.
5. **Open a pull request** against `main` and fill in the template. Keep pull
   requests focused — one concern each. Unrelated cleanups belong in their own
   pull request.
6. **CI must pass.** The `Lint` and `Test` jobs are required checks; the merge
   button stays disabled until they are green.
7. **Get a review.** Address feedback with new commits rather than
   force-pushing, so reviewers can see what changed.
8. **Squash and merge.** The pull request title becomes the commit subject on
   `main`, so write it as one. Your branch is deleted automatically.

## Commit messages

Use [Conventional Commits](https://www.conventionalcommits.org/) for pull
request titles and commits. This keeps generated release notes readable.

```
feat: add gaussian float distribution
fix: reject infinite commonality in weighted()
docs: document the throws condition on randomSet
chore: bump biome to 2.4
```

Mark a breaking change with a `!` — `feat!: reseed on every next() call` — and
explain the impact in the pull request body.

## What counts as breaking

This library's contract is that a given seed always produces the same sequence.
Any change that alters that sequence is breaking, even if no signature changes:

- Changing the algorithm in `RNG.next()`.
- Changing how many times an existing method calls `next()`.
- Changing the order in which values are consumed.

Such a change needs a major version bump and a clear note in the pull request.

## Reporting security issues

This library is not cryptographically secure and is not intended for use where
randomness is a security boundary — that is documented, not a vulnerability.
For anything else that looks security-relevant, email ben@overmyer.net rather
than opening a public issue.

## Releasing

Releases are automated. Maintainers cut one by tagging `main`.

If you use Claude Code, `/release <patch|minor|major>` drives the whole
sequence below and verifies the result; `.claude/skills/release/SKILL.md` is
the source of truth for it. The manual steps are:

1. Land everything intended for the release on `main`.
2. Bump the version on a branch. `--no-git-tag-version` matters: `main` is
   protected, so the bump has to merge before the tag can point at it.

   ```bash
   git checkout -b release/3.1.0
   npm version 3.1.0 --no-git-tag-version
   git commit -aq -m "chore: release 3.1.0"
   git push -u origin release/3.1.0
   ```

3. Open a pull request for it and merge once CI is green.
4. Tag the merged commit on `main` and push the tag:

   ```bash
   git checkout main && git pull
   git tag -a v3.1.0 -m "Release 3.1.0"
   git push origin v3.1.0
   ```

Pushing a `v*` tag triggers `.github/workflows/release.yml`, which:

1. Verifies the tag matches `package.json`'s version, then lints, builds, and
   tests. A mismatch fails the release before anything is published.
2. Publishes to npm with [provenance](https://docs.npmjs.com/generating-provenance-statements)
   attached.
3. Creates a GitHub Release with auto-generated notes from the merged pull
   requests.

Separately, every push to `main` rebuilds the TypeDoc site and deploys it to
[ironarachne.github.io/rng](https://ironarachne.github.io/rng).

### One-time maintainer setup

The release workflow needs these configured before the first tagged release:

- **npm trusted publishing** — on npmjs.com, under the `@ironarachne/rng`
  package settings, add a trusted publisher with:

  | Field            | Value         |
  | ---------------- | ------------- |
  | Organization     | `ironarachne` |
  | Repository       | `rng`         |
  | Workflow         | `release.yml` |
  | Environment      | `release`     |

  The workflow filename is the basename only — not a path. The environment
  must read `release` to match the `publish` job, or be left blank.

  This replaces a long-lived `NPM_TOKEN`: the workflow authenticates over
  OIDC, so there is no publish secret stored in the repository at all.

- **Pages** — Settings → Pages → Source set to **GitHub Actions**.
- The `release` environment, which you can use to require manual approval
  before a publish goes out.
