Files
familienarchiv/CLAUDE.md
Marcel e2c86626f7 docs(legibility): migrate CLAUDE.md rules into human docs — DOC-7
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>
2026-05-05 23:33:41 +02:00

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 yet
  • UPLOADED: 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.Default with new 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, names
  • font-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