# 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).