# AGENTS.md

Machine-readable rules for AI coding agents (Claude Code, Copilot, Cursor, …) working in
this repository. Read this on every invocation. These are **executable constraints**, not
aspirations. The full rationale lives in [constitution.md](./constitution.md) and the docs
it links — this file does not duplicate it, it points to it.

If anything here conflicts with the user's explicit instruction, the user wins. Otherwise,
constitution > this file > convenience.

---

## Stack & Versions

| Layer | Tech | Version |
|---|---|---|
| Backend | Spring Boot (Java, Maven, Jetty, JPA/Hibernate, Flyway, Spring Security, Session JDBC) | Boot 4.0.6 / Java 21 |
| API docs | springdoc-openapi (webmvc-ui), served at `/v3/api-docs` (dev profile only) | — |
| Frontend | SvelteKit / Svelte | 2.60 / 5.43 |
| Frontend lang/style | TypeScript / Tailwind CSS / Paraglide i18n (de/en/es) | TS 5.9 / TW 4.1 |
| API client | `openapi-fetch` + `openapi-typescript` (types generated from the live spec) | — |
| DB | PostgreSQL | 16 |
| Object storage | MinIO (S3-compatible) | — |
| Sidecars | `ocr-service`, `nlp-service` (Python / FastAPI) | Python 3.11 |
| Tests | JUnit + Mockito + `@WebMvcTest` + Testcontainers (backend); Vitest + `vitest-browser-svelte` + Playwright (frontend); Pytest (services) | — |
| Lint/format | ESLint 9 (+ `eslint-plugin-boundaries`) + Prettier; Semgrep (backend) | — |
| CI | Gitea Actions (`.gitea/workflows/`) | — |

App port `8080`; management port `8081`. Backend app id: `org.raddatz.familienarchiv` / `0.0.1-SNAPSHOT`.

## Architectural Constraints

- Controllers call services only — never a repository. (constitution §1.2)
- A service uses only its own domain's repository; reach other domains via their service. (constitution §1.3)
- A new backend domain goes in its own package AND is added to `ArchitectureTest`'s allow-lists in the same change. (constitution §1.7)
- Frontend cross-domain imports are allowed only where `frontend/eslint.config.js` permits; otherwise move shared code to `$lib/shared/`. (constitution §1.4)
- Never serialize a lazy-collection entity across the controller boundary — assemble a view in-transaction. (constitution §1.6 / ADR-036)
- `Person` ≠ `AppUser`; do not add account guards to Person-domain operations. (constitution §1.5)
- Every `POST/PUT/PATCH/DELETE` endpoint has `@RequirePermission(Permission.X)`. Use the enum, never `@PreAuthorize`. (constitution §2.1–2.2)
- Throw only `DomainException.notFound/forbidden/conflict/internal()` from services, each with an `ErrorCode`. (CONTRIBUTING §Error handling)
- Set `createdBy`/`updatedBy` from the session principal in the service — never bind them from a request body. (constitution §2.4)
- Add an `@Schema(requiredMode = REQUIRED)` to every always-populated field. (constitution §3.5)
- Never introduce a new runtime dependency without an ADR in `Accepted` status. (constitution §5.1)
- Render untrusted text with `{...}`; never `{@html}` on user/import data. (constitution §2.5)
- Build dates from ISO strings with a `T12:00:00` suffix. (constitution §3.7)

## Workflow Rules

- Always write a failing test before implementation code; confirm it fails, then make it pass, then refactor. (constitution §3.1)
- Run only the specific test file/class locally — never the full suite (it crashes the machine); leave the full sweep to CI.
- Run `npm run generate:api` (in `frontend/`) after ANY backend model or endpoint change — most common cause of TS errors.
- Run `npm run lint` before every commit; a fresh frontend worktree needs `npm install` first or the pre-commit hook fails.
- When adding a new `ErrorCode`, update all four sites at once (constitution §3.6).
- One logical change per commit; reference the Gitea issue (`Closes #n` / `Refs #n`) on the last line.
- Create a git worktree for new issue work — never `git checkout -b` in the main repo while another branch has in-flight work. Avoid `+` in worktree/branch names (breaks vitest browser mode).
- Pull `main` as a separate explicit step before creating a branch.
- Track work as Gitea issues (`http://192.168.178.71:3005`, repo `marcel/familienarchiv`), not todo files.
- Verify ADR and Flyway migration numbers against disk before using one — parallel worktrees make issue-body numbers go stale.

## Do Not Touch

- Generated: `frontend/src/lib/generated/api.ts`, `frontend/src/lib/paraglide/`, `frontend/.svelte-kit/`, `frontend/build/`, `backend/target/`.
- Shipped Flyway migrations — add a new forward-only migration instead.
- An `Accepted` ADR — supersede it with a new one.
- `actions/(upload|download)-artifact` version — stays at `@v3` (ADR-014).
- CI guard steps — do not remove/weaken without an ADR.
- `main` — never commit directly; branch + PR only.
- Worktree copies (`familienarchiv-*`, `.worktrees/`) and `data/` — never commit.

## Spec-Driven Development

A feature's spec is its **Gitea issue body** — there is no committed `spec.md`. The issue's
EARS requirements (`REQ-NNN`) and acceptance criteria are the contract; each maps to a test,
traced in [`.specify/rtm.md`](./rtm.md) (`REQ-ID → issue # → test`). Read the issue before
implementing. The committed [`.specify/features/_example/`](./features/_example/) is a
template/reference showing the full artifact set, not a live feature. Full workflow:
[SPEC_DRIVEN_DEVELOPMENT.md](../SPEC_DRIVEN_DEVELOPMENT.md).
