> The long-form documentation lives in docs/ and is published at <https://orochibraru.com/opencount>. This file is the short version: what you need to get a change landed.
> Source: https://orochibraru.com/opencount/docs/contributing · Site index: https://orochibraru.com/llms.txt

# Contributing

The long-form documentation lives in [`docs/`](https://github.com/orochibraru/opencount/blob/main/docs/) and is published at
<https://orochibraru.com/opencount>. This file is the short version: what you
need to get a change landed.

- [docs/development.md](https://orochibraru.com/opencount/docs/development): setup, scripts, hooks,
  conventions, releases
- [docs/architecture.md](https://orochibraru.com/opencount/docs/architecture): how the app is put together
- [docs/testing.md](https://orochibraru.com/opencount/docs/testing): the unit suite and the rules its runner
  enforces
- `CLAUDE.md`: every convention, each one a hard rule, for people and AI agents
  alike
- `TODO.md`: the backlog

## Requirements

- [mise](https://mise.jdx.dev), which pins Bun, Go, Tesseract and Poppler. Or
  [Bun](https://bun.sh) alone, with Tesseract and Poppler installed yourself if
  you touch receipt reading.
- Docker, to build the image.

## Setup

```bash
mise run bootstrap   # bun install, then .env with a fresh AUTH_SECRET
bun run dev          # dev server on :5173, worker included
```

Without mise: `bun install`, copy `.env.example` to `.env` and set `AUTH_SECRET`
(`openssl rand -base64 32`).

The dev server migrates `./data/opencount.sqlite` and starts the worker under
watch. Delete the file to start over from a fresh instance; the setup code is in
the dev server's output.

Installing dependencies wires the git hooks through prek: `pre-commit` runs the
fixers and checks, `commit-msg` enforces Conventional Commits, `pre-push` runs
the type check, the import-cycle check and the unit tests.

## The commands

```bash
bun run dev          # Vite dev server on :5173, with the worker
bun run build        # build/server and build/worker
bun run start        # run the compiled server, which spawns the worker
bun run check        # svelte-check (fails on warnings) and tsc
bun run lint         # every prek hook over every file
bun run format       # bun fmt, Markdown included
bun run circular     # fails on an import cycle
bun run test         # unit tests
bun run test:integration  # HTTP flows against the built server
bun run test:e2e     # Playwright against the built server
bun run db:generate  # a migration from the schema
mise run verify      # every hook at the commit and push stages, as CI runs them
```

The package manager is **bun**. Never npx or npm.

## The container

`compose.yaml` builds the image from your checkout and runs it on port 3000,
with `AUTH_SECRET` and `ORIGIN` from `.env`:

```bash
docker compose up --build
```

It is for checking the real container before a PR. Self-hosters use
`compose.prod.yaml`, which pulls the published image; keep the two in step.

## Screenshots

```bash
mise run screenshots   # every screen into docs/images/
mise run showcase      # the README hero, docs/images/feature.webp
```

Desktop shots are dark theme at 1440 by 900, phone shots light theme at 390
by 844. Rerun them when a change moves a screen, and commit the images with it.

## Before you open a PR

- `bun run check`, `bun run lint` and `bun run test` pass on the whole repo,
  with zero warnings.
- A schema change comes with its generated migration. Committed migrations are
  append-only: fix a bad one with a new one.
- A new environment variable is declared in `src/env.ts` with a schema and a
  description, and documented in [docs/configuration.md](https://orochibraru.com/opencount/docs/configuration).
- A user-facing change updates the page of `docs/` that describes it.
- Every screen still works at 375px wide.
- Conventional Commit title. Squash merges turn it into the commit releaser
  reads, and `fix`, `feat` and the rest all ship in the next canary.
