# Conventions

File-size limits, structural conformity, conventional commits, and i18n.

## File size limits

No file in the repository may exceed 500 lines of prose (code, comments, and blank lines combined). This is enforced by `scripts/check-file-size.sh` in CI. Large files are a sign that a domain needs to be split.

Exceptions (whitelisted in the script):
- Generated files (`internal/db/gen/`, `frontend/src/lib/api/types.ts`)
- Migration files
- Test fixture files

## Structural conformity

Before writing any new code, find an existing example of the same pattern and follow it exactly. The AGENTS.md lookup table maps each kind of change to its canonical example file.

New patterns without precedent require human approval before implementation.

## Conventional commits

All commits use [Conventional Commits](https://www.conventionalcommits.org/):

```
<type>(<scope>): <description>

[optional body]
```

**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `ci`

**Scopes:** match the affected package or component, e.g. `cards`, `auth`, `frontend`, `db`, `ci`

**Examples:**
```
feat(cards): add bulk-delete endpoint
fix(auth): handle expired OIDC state parameter
docs(contribute): add E2E setup instructions
```

## i18n

All user-facing strings in the frontend must be in `frontend/messages/en.json`. Hard-coded strings in Svelte components are rejected by the i18n lint check (`scripts/check-i18n.sh`).

Access translated strings via:

```svelte
<script>
  import * as m from '$lib/paraglide/messages';
</script>

<p>{m.card_title()}</p>
```

Backend error messages are in English and do not go through the i18n system — they are developer-facing, not user-facing.

## Import ordering

Go imports follow the standard three-group convention:
1. Standard library
2. External packages
3. Internal packages (`gitlab.com/yotoshelf/yotoshelf/internal/`)

`goimports` (run by golangci-lint) enforces this automatically.
