Processes all 7 CLAUDE.md files according to the 3-bucket classification. Migration targets (CONTRIBUTING.md, docs/ARCHITECTURE.md, docs/DEPLOYMENT.md, domain READMEs) are introduced by DOC-2/4/5/6 — this PR must merge last. ### scripts/CLAUDE.md → scripts/README.md New `scripts/README.md` with full script documentation (preserving the ⚠️ destructive-operation warning on reset-db.sh). `scripts/CLAUDE.md` reduced to a pointer + "document new scripts in README.md" reminder. ### .devcontainer/CLAUDE.md → .devcontainer/README.md New `.devcontainer/README.md` with all configuration, usage, and limitations. `devcontainer/CLAUDE.md` reduced to a single pointer line. ### docs/CLAUDE.md → docs/README.md New `docs/README.md` covering the folder structure, ADR guide, infrastructure docs, and specs folder. `docs/CLAUDE.md` reduced to pointer + ADR reminder. ### ocr-service/CLAUDE.md Reduced to pointer to `ocr-service/README.md` (content migrated in DOC-6). Kept LLM reminders: single-node constraint, ALLOWED_PDF_HOSTS SSRF risk. ### backend/CLAUDE.md - Layering Rules → pointer to docs/ARCHITECTURE.md - Error Handling → pointer to CONTRIBUTING.md + reminder - Security/Permissions → pointer to docs/ARCHITECTURE.md + reminder - Package Structure → tagged TODO post-REFACTOR-1 - Fixed errors.ts path to frontend/src/lib/shared/errors.ts - Added ANNOTATE_ALL + BLOG_WRITE to permission list - Key Entities, Entity Code Style, Services → kept (Bucket-2) ### root CLAUDE.md - Stack, Infrastructure, Dev Container → pointers - Layering Rules, Error Handling, Security, OpenAPI, API Client, Date Handling, UI Components, Frontend Error Handling → pointers + reminders - Package Structure → tagged TODO post-REFACTOR-1 - Domain Model, Entity Code Style, Form Actions, Styling → kept (Bucket-2) ### frontend/CLAUDE.md - API Client Pattern, Date Handling → pointers + reminders - Key UI Components → pointer to domain READMEs - Styling, Form Actions, How to Run, Vite Proxy, i18n → kept (Bucket-2) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
For a human-readable project overview, see README.md.
Project Overview
Familienarchiv is a family document archival system — a full-stack web app for digitizing, organizing, and searching family documents. Key features: file uploads (stored in MinIO/S3), metadata management, Excel/ODS batch import, full-text search, conversation threads between family members, and role-based access control.
Collaboration
See COLLABORATING.md for the full rules: issue tracking workflow, commit message conventions, and the Research → Plan → Implement → Validate cycle.
See CODESTYLE.md for coding standards: Clean Code, DRY/KISS trade-offs (KISS wins), and SOLID principles applied to this stack.
Stack
→ See README.md §Tech Stack
- Backend: Spring Boot 4.0 (Java 21, Maven, Jetty, JPA/Hibernate, Flyway, Spring Security, Spring Session JDBC)
- Frontend: SvelteKit 2 with Svelte 5, TypeScript, Tailwind CSS 4, Paraglide.js (i18n: de/en/es)
- Database: PostgreSQL 16
- Object Storage: MinIO (S3-compatible)
- Infrastructure: Docker Compose
Common Commands
Running the Full Stack
docker-compose up -d
Backend (Spring Boot)
cd backend
./mvnw spring-boot:run # Run locally
./mvnw clean package # Build JAR (with tests)
./mvnw clean package -DskipTests
./mvnw test # Run all tests
./mvnw test -Dtest=ClassName # Run a single test class
Frontend (SvelteKit)
cd frontend
npm install
npm run dev # Dev server (port 3000)
npm run build # Production build
npm run preview # Preview production build
npm run lint # Prettier + ESLint check
npm run format # Auto-fix formatting
npm run check # svelte-check (type checking)
npm run test # Vitest unit tests
npm run generate:api # Regenerate TypeScript API types from OpenAPI spec
# (requires backend running with --spring.profiles.active=dev)
Backend Architecture
Package Structure
backend/src/main/java/org/raddatz/familienarchiv/
├── audit/ Audit logging
├── config/ Infrastructure config (Minio, Async, Web)
├── dashboard/ Dashboard analytics + StatsController/StatsService
├── document/ Document domain (entities, controller, service, repository, DTOs)
│ ├── annotation/ DocumentAnnotation, AnnotationService, AnnotationController
│ ├── comment/ DocumentComment, CommentService, CommentController
│ └── transcription/ TranscriptionBlock, TranscriptionService, TranscriptionBlockQueryService
├── exception/ DomainException, ErrorCode, GlobalExceptionHandler
├── filestorage/ FileService (S3/MinIO)
├── geschichte/ Geschichte (story) domain
├── importing/ MassImportService
├── notification/ Notification domain + SseEmitterRegistry
├── ocr/ OCR domain — OcrService, OcrBatchService, training
├── person/ Person domain
│ └── relationship/ PersonRelationship sub-domain
├── security/ SecurityConfig, Permission, @RequirePermission, PermissionAspect
├── tag/ Tag domain
└── user/ User domain — AppUser, UserGroup, UserService, auth controllers
Layering Rules
→ See docs/ARCHITECTURE.md §Layering rule
LLM reminder: controllers never call repositories directly; services never reach into another domain's repository — always call the other domain's service instead.
Domain Model
| Entity | Table | Key relationships |
|---|---|---|
Document |
documents |
ManyToOne sender (Person), ManyToMany receivers (Person), ManyToMany tags (Tag) |
Person |
persons |
Referenced by documents as sender/receiver |
Tag |
tag |
ManyToMany with documents via document_tags |
AppUser |
app_users |
ManyToMany groups (UserGroup) |
UserGroup |
user_groups |
Has a Set<String> permissions |
DocumentStatus lifecycle: PLACEHOLDER → UPLOADED → TRANSCRIBED → REVIEWED → ARCHIVED
PLACEHOLDER: created during Excel import, no file yetUPLOADED: file has been stored in S3
Entity Code Style
All entities use these Lombok annotations:
@Entity
@Table(name = "table_name")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) // marks field as required in OpenAPI spec
private UUID id;
// ...
}
@Schema(requiredMode = REQUIRED)must be added to every field the backend always populates (id, non-null fields). This drives the TypeScript type generation.- Collections use
@Builder.Defaultwithnew HashSet<>()as the default. - Timestamps use
@CreationTimestamp/@UpdateTimestamp.
Services
Services are annotated with @Service, @RequiredArgsConstructor, and optionally @Slf4j.
- Write methods are annotated
@Transactional. - Read methods are not annotated (default non-transactional is fine).
- Each service owns its domain's repository. Cross-domain data access goes through the other domain's service.
DTOs
Input DTOs live flat in the domain package. Response types are the model entities themselves (no response DTOs).
@Schema(requiredMode = REQUIRED)on every field the backend always populates — drives TypeScript generation.
Error Handling
→ See CONTRIBUTING.md §Error handling
LLM reminder: use DomainException.notFound/forbidden/conflict/internal() from service methods — never throw raw exceptions. When adding a new ErrorCode: (1) add to ErrorCode.java, (2) mirror in frontend/src/lib/shared/errors.ts, (3) add i18n keys in messages/{de,en,es}.json.
Security / Permissions
→ See docs/ARCHITECTURE.md §Permission system
LLM reminder: @RequirePermission(Permission.WRITE_ALL) is required on every POST, PUT, PATCH, DELETE endpoint — not optional. Do not mix with Spring Security's @PreAuthorize. Available permissions: READ_ALL, WRITE_ALL, ADMIN, ADMIN_USER, ADMIN_TAG, ADMIN_PERMISSION, ANNOTATE_ALL, BLOG_WRITE.
OpenAPI / API Types
→ See CONTRIBUTING.md §Walkthrough B — Add a new endpoint
LLM reminder: always run npm run generate:api in frontend/ after any backend model or endpoint change — this is the most common cause of TypeScript type errors.
Frontend Architecture
Route Structure
frontend/src/routes/
├── +layout.svelte Global header (sticky), nav links, logout
├── +layout.server.ts Loads current user, injects auth cookie
├── +page.svelte Home / document search
├── +page.server.ts Load: search documents; no actions
├── documents/
│ ├── [id]/+page.svelte Document detail (view + file preview)
│ └── [id]/edit/ Edit form (all metadata + file upload)
│ └── new/ Create form (same fields, empty)
├── persons/
│ ├── +page.svelte Person list with search
│ ├── [id]/+page.svelte Person detail (inline edit + merge)
│ └── new/ Create person form
├── conversations/ Bilateral conversation timeline
├── admin/ User + group + tag management
└── login/ logout/ Auth pages
API Client Pattern
→ See CONTRIBUTING.md §Frontend API client
LLM reminder: check !result.response.ok (not result.error — breaks when spec has no error responses defined); cast errors as result.error as unknown as { code?: string }; use result.data! after an ok check.
Form Actions Pattern
// +page.server.ts
export const actions = {
default: async ({ request, fetch }) => {
const formData = await request.formData();
const name = formData.get("name") as string;
// ...
return fail(400, { error: "message" }); // on error
throw redirect(303, "/target"); // on success
},
};
Date Handling
→ See CONTRIBUTING.md §Date handling
LLM reminder: always append T12:00:00 when constructing new Date() from an ISO date string — prevents UTC timezone off-by-one errors.
UI Component Library
→ See per-domain READMEs: frontend/src/lib/person/README.md, frontend/src/lib/tag/README.md, frontend/src/lib/document/README.md, frontend/src/lib/shared/README.md
Styling Conventions (Tailwind CSS 4)
Brand color utilities (defined in layout.css):
| Class | Value | Usage |
|---|---|---|
brand-navy |
#002850 |
Primary text, buttons, headers |
brand-mint |
#A6DAD8 |
Accents, hover underlines, icons |
brand-sand |
#E4E2D7 |
Page background, card borders |
Typography:
font-serif(Merriweather) — body text, document titles, namesfont-sans(Montserrat) — labels, metadata, UI chrome
Card pattern for content sections:
<div class="bg-white shadow-sm border border-brand-sand rounded-sm p-6">
<h2 class="text-xs font-bold uppercase tracking-widest text-gray-400 mb-5">Section Title</h2>
<!-- content -->
</div>
Back button pattern — use the shared <BackButton> component from $lib/shared/primitives/BackButton.svelte. Do not use a static <a href> for back navigation.
Error Handling (Frontend)
→ See CONTRIBUTING.md §Error handling
LLM reminder: when adding a new ErrorCode: (1) add to ErrorCode.java, (2) add to ErrorCode type in frontend/src/lib/shared/errors.ts, (3) add a case in getErrorMessage(), (4) add i18n keys in messages/{de,en,es}.json.
Infrastructure
→ See docs/DEPLOYMENT.md
API Testing
HTTP test files are in backend/api_tests/ for use with the VS Code REST Client extension.
Dev Container
→ See .devcontainer/README.md