Files
GerbilManager/CLAUDE.md
Gulum e1299a1a63 docs: CLAUDE.md mit Architektur, Import-Workflow, Konventionen & Domänenwissen
Leitfaden für künftige Arbeit: Projekt-/Architektur-Überblick, Start über Aspire
(Ports/Dashboard/persistentes Volume), 3-stufiger Import→DB-Workflow (Ingest
wischt+lädt, Feedback überlebt), Build/Test je Schicht, Konventionen (de.ts,
Migrationen, Provenance-JSON, FK-freie Survivor-Spalten), Domänenwissen
(Lebensspanne/Geschwisterverpaarung/Genotyp/Chart-Layout), Stolperfallen
(Aspire-Container vs. Volume, Migrations-Historie-Mismatch, psql-Quoting) und
eine Feature-Landkarte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 17:44:18 +02:00

159 lines
8.4 KiB
Markdown

# CLAUDE.md — GerbilManager
Leitfaden für die Arbeit an diesem Repo. Bitte vor Änderungen lesen.
## Was ist das?
Verwaltungs-App für eine **Rennmaus-Zucht** („Zucht der kleinen Chaoten"): Tiere,
Würfe, Stammbäume/Ahnentafeln, Genetik (Farbschläge/Genotypen), Gehege, Kontakte
(Züchter/Abnehmer), Abgabeverträge, Statistik und eine öffentliche CMS-Webseite.
Single-User (die Züchterin), Daten größtenteils aus bestehenden Quelldateien
importiert.
## Architektur / Projekte
| Projekt | Zweck |
|---|---|
| `GerbilManager.AppHost` | **.NET Aspire** Orchestrator — startet PostgreSQL (Container) + WebAPI |
| `GerbilManagerWebAPI` | Backend: ASP.NET **Minimal API**, EF Core + Postgres, Endpoints in `Endpoints/`, Entities in `Models/`, DTOs in `Dtos/` |
| `GerbilManager.ServiceDefaults` | Aspire-Defaults (Telemetry/Health) |
| `GerbilManager.Tests` | Backend-Tests (xUnit) |
| `gerbil-manager-web` | Frontend: **React + Vite + TypeScript** (React Router, react-d3-tree für den Stammbaum) |
| `tools/import` | **Python**-Import-Pipeline (zero-dep), wandelt Quelldateien → `resolved_import.json` |
## App starten (IMMER über Aspire, nicht nur die WebAPI)
```bash
dotnet run --project GerbilManager.AppHost
```
- WebAPI: http://localhost:5179 · OpenAPI: `/openapi/v1.json`
- Aspire-Dashboard: https://localhost:17004 (Login-Token steht im AppHost-Log)
- EF-Migrationen werden beim Start automatisch angewandt (`db.Database.Migrate()` in `Program.cs`).
- Postgres läuft als Container (Name wechselt pro Run, z. B. `postgres-xxxx`); die
**Daten liegen in einem persistenten Volume** → überleben Neustarts. DB-Name: `gerbilmanager`.
## Import → DB (3-stufig, WICHTIG)
Die DB wird **nicht** direkt von Python beschrieben. Nach jeder Import-Code-Änderung
neu generieren UND neu einspielen, sonst zeigt die App den alten Stand.
```bash
# 1. Stammbäume + Wurfchronik → animals.json/litters.json (+ Fotos)
python tools/import/extract.py
# 2. Abgabeverträge (DOCX) → contracts.json
python tools/import/extract_contracts.py
# 3. Dedup, Eltern-Resolver, Provenance, Vertrags-Anreicherung → resolved_import.json
python tools/import/merge_and_resolve.py
# 4. In die laufende DB laden (API muss laufen): WISCHT + lädt neu
curl -X POST http://localhost:5179/import/ingest-resolved
```
- **`IngestResolvedService` wischt** Gerbils/Litters/Contacts/Photos/Health/Weights/
**SaleContracts** und lädt aus `resolved_import.json` neu. Importierte SaleContracts
sind Teil des Payloads → werden bei jedem Ingest neu erzeugt (idempotent, IDs deterministisch).
- **Feedback überlebt den Ingest** (wird nicht gewischt) — daher hat `Feedback` nur
**lose, nullable Guid-Spalten ohne FK** (GerbilId/LitterId/ContactId). Dieses Muster
für alles nutzen, was Re-Ingests überleben soll.
- `resolved_import.json` ist **gitignored** (Runtime-Output); `review-report.md` ist getrackt.
- Deterministische GUIDs via `generate_guid("...")` → stabile IDs über Re-Ingests.
### Quelldateien (lokal/Netzwerk)
- Stammbäume: `C:\Users\gulum\dev\Sttammbäume\*.xlsx`
- Wurfchronik: `C:\Users\gulum\dev\Wurfchronik der Kleine Chaoten Teil1.xlsx`
- Abgabeverträge: `\\truenas\Datengrab\Rennmäuse\Verträge\*.docx` (~1,4k; kann zeitweise offline sein)
## Build & Test
**Frontend** (`cd gerbil-manager-web`):
```bash
npx tsc --noEmit # Typecheck (muss sauber sein)
npx vitest run # Unit-Tests
npx eslint <dateien> # Lint
npx playwright test [spec]# e2e (Projekte: desktop + phone), läuft gegen Mock-API
```
- e2e nutzt einen **Mock-Modus** (`e2e/mock-api.ts` + `e2e/mock-data.ts`), kein echtes Backend.
Neue API-Routen/Felder dort spiegeln. `skipUnlessMock()` am Testanfang.
**Backend**:
```bash
dotnet build
dotnet test # GerbilManager.Tests
dotnet ef migrations add <Name> --project GerbilManagerWebAPI # neue Migration
```
- Zum Bauen muss eine laufende WebAPI ggf. gestoppt werden (Datei-Lock auf `bin`).
**Python** (`cd tools/import`, zero-dep, exit 0 = pass):
```bash
python test_extract.py test_extract_docx.py test_genotype.py test_merge_resolve.py test_extract_contracts.py
```
## Konventionen (bitte einhalten)
- **Alle deutschen UI-Texte in `gerbil-manager-web/src/strings/de.ts`** — niemals in
Komponenten hartkodieren.
- **EF-Migration** für jede Schema-Änderung; wird beim Aspire-Start angewandt.
- **Provenance/Datenherkunft** liegt als **JSON-Text-Spalte** (`Provenance`) auf
Gerbil/Contact/Litter → JSON-Feld-Erweiterungen brauchen **keine** Migration.
- **Lose nullable Guid-Spalten ohne FK** für Tabellen, die den Ingest-Wipe überleben.
- Commits: nur wenn der Nutzer es verlangt; auf `main` wird gearbeitet (Remote: `http://truenas:13000/Gulum/GerbilManager.git`). Commit-Messages auf Deutsch, Format `feat(scope): …`.
- Subagenten committen NICHT — Haupt-Loop verifiziert und committet.
## Domänenwissen (Rennmaus-Zucht)
- **Lebensspanne ~6 Jahre** → ein Elternteil kann max. ~6 Jahre älter als sein Kind
sein und muss vorher geboren sein (`parent_age_plausible` im Import; verwirft
unplausible Eltern-Links).
- **Geschwisterverpaarung** (Vollgeschwister als Eltern): erkannt, wenn Vater & Mutter
**dieselbe `litterId`** ODER **dasselbe Geburtsdatum** teilen (gleiches DOB = starkes
Indiz). UI: Diagramm führt den Vorfahren-Ast zusammen (Verweis-Knoten); Akte zeigt
„⚭ Geschwisterverpaarung"-Chip.
- **Box-Farbe im Stammbaum-xlsx = Geschlecht**: weiß = weiblich, blau = männlich.
- **Genotyp/Farbschlag**: 8-Locus-Notation (siehe `tools/import/genotype.py`,
`gerbil-manager-web/src/genetics`). Unbekanntes Allel = `-` (nicht `?`).
E-Locus: `ee`=Fuchs, `eef`=Fuchsschimmel, `efef`=Schimmel.
- **Stammbaum-Charts**: Generationen = Spaltenbänder (Proband links in Spalte B/2,
je Generation +3 Spalten); Eltern-Position: Vater oben, Mutter unten. Fotos liegen
je nach Datei links/auf der Namenszelle — die Seite wird heuristisch über die
geringste Spalten-Fehlausrichtung bestimmt (`extract._attach_photos`).
## Stolperfallen
- **Aspire-Containername wechselt** pro Run, das Daten-Volume bleibt → DB-Inhalt
überlebt Neustarts (auch alte Schema-Stände → Migrationskonflikte möglich, s. u.).
- **Migrations-Historie vs. committete Migration-IDs**: Wird eine Migration angewandt
und danach mit neuem Zeitstempel neu generiert, sieht EF sie als „pending" und der
Start crasht („column … already exists"). Schema ist dann korrekt — nur die
`__EFMigrationsHistory`-IDs müssen an die committeten Dateinamen angeglichen werden.
- **PowerShell + psql**: PascalCase-Identifier (`"__EFMigrationsHistory"`) über
PowerShell→docker→psql zerschießen das Quoting. Stattdessen Bash-Heredoc:
`docker exec -i -e PGPASSWORD=$PW <container> psql -U postgres -d gerbilmanager <<'SQL' … SQL`.
Passwort: `docker exec <container> printenv POSTGRES_PASSWORD`.
- **Direkte DB-Schreibzugriffe** (psql) werden vom Auto-Mode-Classifier blockiert —
vorher beim Nutzer rückversichern.
- Bash-Tool ist **Git-Bash (POSIX)**, das andere Tool ist **PowerShell** — je eigene Syntax.
## Wo liegt was (Feature-Landkarte)
- **Stammbaum-Viewer**: `gerbil-manager-web/src/pages/StammbaumPage.tsx`,
`src/pedigree/*` (build.ts = Baum-Aufbau + Geschwister-Erkennung; types.ts).
Desktop passt den ganzen Baum ein; Touch: Long-Press öffnet das Kontextmenü
(ID kopieren / Fehler melden).
- **Datenherkunft/Nachverfolgung**: `src/components/ProvenanceDialog.tsx`; erzeugt im
Import via `build_entity_provenance`/`_build_*_history` (chronologische, datei-
attribuierte `history[]`, inkl. „⚠ … verworfen — Grund; … verwendet").
- **Fehler melden (Feedback)**: `src/components/ReportErrorDialog.tsx`,
`src/api/feedback.ts`, Backend `Endpoints/FeedbackEndpoints.cs` + `Models/Feedback.cs`.
- **Verträge**: `Models/SaleContract.cs`, `Endpoints/ContractEndpoints.cs`,
`ContractGenerator.cs`, Frontend `src/pages/Vertraege*`; Import: `extract_contracts.py`
+ `enrich_from_contracts` in `merge_and_resolve.py`.
- **Import-Kernlogik**: `tools/import/merge_and_resolve.py` (Dedup, Eltern-Resolver mit
Gender-/Alters-Plausibilität, Litter-/Sibling-Dedup, Provenance, Vertrags-Anreicherung).
## Verifikations-Checkliste vor „fertig"
1. `npx tsc --noEmit`, `npx vitest run`, `npx eslint` sauber.
2. Betroffene `npx playwright test`-Specs grün.
3. `dotnet build` + `dotnet test` grün.
4. Bei Import-Änderungen: `python test_*.py` grün + `resolved_import.json` plausibel
(Stichprobe), danach App neu starten + `POST /import/ingest-resolved`.