Brand colors, font name, dev port, route tree, and card pattern were all outdated relative to layout.css and the current route structure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11 KiB
CLAUDE.md
For a human-readable project overview, see README.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 5173)
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 / +layout.server.ts Global layout, auth cookie
├── +page.svelte / +page.server.ts Home / document search dashboard
├── documents/
│ ├── [id]/ Document detail (view + file preview)
│ ├── [id]/edit/ Edit form (all metadata + file upload)
│ ├── new/ Upload form
│ └── bulk-edit/ Multi-document edit
├── persons/
│ ├── [id]/ Person detail
│ ├── [id]/edit/ Person edit form
│ └── new/ Create person form
├── briefwechsel/ Bilateral conversation timeline (Briefwechsel)
├── aktivitaeten/ Unified activity feed (Chronik)
├── geschichten/ Stories — list, [id], [id]/edit, new
├── stammbaum/ Family tree (Stammbaum)
├── enrich/ Enrichment workflow — [id], done
├── admin/ User, group, tag, OCR, system management
├── hilfe/transkription/ Transcription help page
├── profile/ User profile settings
├── login/ logout/ register/
├── forgot-password/ reset-password/
└── demo/ Dev-only demos
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 |
#012851 |
Primary text, buttons, headers |
brand-mint |
#a1dcd8 |
Accents, hover underlines, icons |
brand-sand |
#f0efe9 |
Page background (palette alias) |
Typography:
font-serif(Tinos) — body text, document titles, namesfont-sans(Montserrat) — labels, metadata, UI chrome
Card pattern for content sections:
<div class="rounded-sm border border-line bg-surface shadow-sm p-6">
<h2 class="text-xs font-bold uppercase tracking-widest text-ink-3 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