# Shared rules

Rules that apply to both backend and frontend work in this repo. Stack-specific
guides (`backend-context.md`, `frontend-context.md`) extend these.

## Process (TDD)

0. Before editing any file, ensure you are on a feature branch
   (`git status` to confirm). If on master/main, create a branch
   first.
1. Write the test first
2. Run the test to confirm it fails
3. Commit the failing test (the "tests committed first" rule in
   action - the test commit precedes the implementation commit, not
   merely the implementation lines)
4. Implement the code to make the test pass
5. Run the test to confirm it passes
6. Commit the implementation
7. Repeat this red/green commit cycle for each new behavior. Do not
   batch multiple behaviors into one failing-test commit and one
   implementation commit when they can be reviewed separately.

## Runtime assumptions

- Assume every process defined in `process-compose.yml` is already running
  for local development and verification unless a command proves otherwise.
  Do not start duplicate PostgreSQL, backend, or frontend processes just to
  run checks.

## Code style

- Lines should not exceed 80 columns, but should use up to 80 columns when
  possible - do not split lines unnecessarily. Applies to PHP, TypeScript,
  and Vue SFCs.
- Variable names: use explicit, descriptive names - never single-letter or
  abbreviated variables (e.g. `$text` not `$t`, `node` not `n`)
- Method/function/constructor parameters: do not use default values - every
  call site must pass every argument explicitly. This eliminates a class of
  bugs where an unintended default silently slips through (e.g. an
  `isAdmin=false` or an empty `passwordHash`). Apply the same rule in tests
  and fakes - if a helper accepts a value, every caller must supply it.
- First, explore the codebase to understand existing patterns - look at similar
  files for reference before writing anything
- Never use em dash characters in code, comments, or docblocks - use
  hyphens (-) instead

## Git commit style

- Present tense, imperative mood (add, create, wire, fix, test)
- Lowercase
- Short (3-6 words)
- Match patterns found in git history
- Do not add any section mentioning claude as a coauthor
- Add a commit body when the subject alone cannot convey the change - e.g.
  non-obvious motivation, multi-file coordination, or notable complexity
- Body: wrap at ~72 columns, separated from subject by a blank line, explain
  the why and any non-obvious what
- Skip the body for trivial or self-explanatory commits

## Git commits

- Tests should be committed first, before implementation
- Prefer small, reviewable commits. Commit each meaningful step as soon as
  it is green and the pre-commit checklist passes.
- One logical change per commit - a commit may span multiple files when they
  form a single logical unit (e.g. a use case with its request and exception,
  or a Vue SFC with its Pinia store and route entry)
- Split commits by behavior, public contract, migration, wiring, docs, or
  mechanical formatting when those parts can be reviewed independently.
- A multi-file commit is okay only when the files form one inseparable
  change. Do not use "one logical change" to justify batching adjacent work.
- Do not batch several behaviors into one "tests" commit and one
  "implementation" commit. Repeat the red/green cycle per behavior.
- Do not bundle backend and frontend changes unless both are required for
  the same user-facing behavior.
- Do not bundle cleanup, refactors, renames, or formatting with feature
  behavior.
- Keep commits focused: not one file per commit, not unrelated work batched
- Make commits frequent - commit each meaningful logical step as you go
- Commits are for reviewing and documenting the development of code
- When the formatter or linter modifies files outside your intended
  change, either `git restore` them or land them as a separate
  `format <area>` / `lint <area>` commit - never bundle drive-by
  formatter churn into a feature commit
- If pre-commit lint fails on code you did not touch, do not bundle
  the fix - either land the unrelated fix as its own commit first, or
  note the pre-existing failure and proceed

## Branching

- Use kebab-case (e.g. `set-page`, `element-tree`, `media-upload`)
- Use descriptive feature names
- Or use type/description: `feature/set-page`, `fix/bug-name`
- NEVER work directly on master/main - always create and work on a branch

Do not push anything. Make commits as you go.

## Pre-commit checklist

Before EVERY commit (no exceptions), verify each item. Treat this as
mechanical, not aspirational - a "yes" to all is required.

**Branch + scope:**
- [ ] On a feature branch (not master/main).
- [ ] This commit is one logical change. If it spans unrelated changes,
      stop and split it.
- [ ] This commit has been split as far as it can be while staying
      reviewable. If any part can land independently, split it.
- [ ] Tests for new behavior were committed BEFORE this implementation
      (or this commit IS the failing-test commit).

**Code rules** (see `backend-context.md` PHP rules,
`frontend-context.md` Vue/TS rules):
- [ ] No arrow functions in PHP (`fn () =>`).
- [ ] No inline FQCNs in type hints, return types, or `::class`
      references (`\App\Foo\Bar` -> hoist to `use App\Foo\Bar;`).
- [ ] No default parameter values on methods/functions/constructors
      (PHP or TS).
- [ ] Find/lookup repository methods return new instances, not stored
      references.
- [ ] Backend repository methods that find by a foreign key accept the
      related entity, not a raw id.
- [ ] No em dashes (use hyphens).
- [ ] Variable names are explicit (no `$t`, `n`, `res`, `req`, `e`, etc.).
- [ ] No `any` in TypeScript - use concrete types or `unknown` with a
      narrowing guard.
- [ ] Vue SFCs use `<script setup lang="ts">` and `<style scoped>`.

**Mechanical checks (backend, when PHP changed):**
- [ ] `phpcs <touched dirs>` reports clean (config to be added; flake
      provides `php-codesniffer`).
- [ ] `./vendor/bin/phpunit tests` is green.

**Mechanical checks (frontend, when frontend changed; run from
`frontend/rabbi_gerzi/`):**
- [ ] `npm run format` (oxfmt) reports clean (or any fixes are
      committed).
- [ ] `npm run lint` (oxlint + eslint) passes.
- [ ] `npm run type-check` (vue-tsc) passes.

**Commit metadata:**
- [ ] Subject is lowercase, imperative, 3-6 words.
- [ ] No claude/AI coauthor lines.
- [ ] Body present iff the subject alone cannot convey the change.

If any item fails, fix it before committing - do not bundle the fix
into a future commit.
