Removes scaffolding pages from initial Paraglide setup that were never navigated to in production. Shrinks the measured coverage surface and removes dead code from the production bundle. CLAUDE.md route tables updated to drop the demo/ entry. Refs #496. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12 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
├── users/[id]/ Public user profile page
├── login/ logout/ register/
└── forgot-password/ reset-password/
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 tokens (defined in layout.css):
| Token / Utility | CSS variable | Usage |
|---|---|---|
brand-navy |
--palette-navy |
Tailwind utility — buttons, headers, primary text |
brand-mint |
--palette-mint |
Tailwind utility — accents, hover underlines, icons |
--palette-sand |
--palette-sand |
Palette constant only — use bg-canvas or bg-surface |
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