# 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`.

## Gedächtnis (MemPalace) — IMMER nutzen

Dieses Projekt nutzt **MemPalace** als KI-Langzeitgedächtnis (lokal, kein API-Key).
Die Anbindung ist im Repo persistiert und lädt automatisch:
- `.mcp.json` (committet) registriert den MCP-Server `mempalace-mcp` → **19 MCP-Tools**
  (`mempalace_search`, `mempalace_kg_query/add/invalidate/timeline`, `mempalace_add_drawer`,
  `mempalace_diary_*` …). Aktiv ab Claude-Neustart (MCP-Server einmal bestätigen).
- `.claude/settings.json` (committet) verdrahtet die Hooks
  `mempalace hook run --hook {session-start,stop,precompact} --harness claude-code`:
  **SessionStart** lädt relevanten Kontext automatisch, **Stop/PreCompact** persistieren.
- Voraussetzung: `mempalace` ist installiert und auf PATH (`~/.local/bin`). Nach `git clone`
  ggf. MemPalace installieren; `mempalace.yaml` (Raum-Config) ist bewusst **gitignored**
  (per-project, lokal). Der Palast selbst liegt lokal unter `~/.mempalace` (nicht im Repo).

**Arbeitsweise pro Session:** zu Aufgabenbeginn relevantes Wissen abrufen
(`mempalace_search` / `mempalace_kg_query`), bevor du im Code suchst; am Ende neue
durable Erkenntnisse ablegen.

### Memory-Policy — WANN / WIE / WO eine Memory anlegen

**WANN** (anlegen): nur **dauerhaft** nützliches, **nicht-offensichtliches** Wissen, das
künftige Sessions braucht — Architektur-/Design-Entscheidungen und ihr *Warum*, Domänenregeln
(z. B. 6-Jahre-Lebensspanne, Geschwisterverpaarung), Konventionen, Stolperfallen, bestätigte
Nutzer-Präferenzen, wiederkehrende Workflows.
**NICHT anlegen**: transienter Task-Status; was Code/Git/CLAUDE.md ohnehin festhalten;
Dinge, die nur für das aktuelle Gespräch zählen.

**WIE** (anlegen/pflegen):
- Fakt mit Gültigkeit → `mempalace_kg_add`. Freitext-Notiz/Erkenntnis → `mempalace_add_drawer`
  (vorher `mempalace_check_duplicate`).
- Ändert/erledigt sich ein Fakt → `mempalace_kg_invalidate` (setzt End-Datum, **nicht löschen** —
  der Graph ist temporal). So bleibt nichts veraltet stehen.
- Repo-Dateien werden über `mempalace mine <dir>` automatisch gefilet; `mempalace sync --apply
  --wing gerbilmanager` entfernt Drawers gelöschter/verschobener Dateien.

**WO** (welcher Speicher):
- **MemPalace-Palast** (`~/.mempalace`, lokal): reichhaltiges, durchsuchbares Recall — der Default
  für Memories.
- **CLAUDE.md** (Repo, committet): die **kanonischen** Projektfakten/Konventionen, die geteilt und
  versioniert sein müssen. Wichtige durable Erkenntnisse hier spiegeln, damit sie im Repo (nicht nur
  lokal) liegen.

Faustregel: durable + projektweit kanonisch → **CLAUDE.md** *und* Palast; sonstiges durable Wissen →
Palast; veraltetes → invalidieren, nicht stehen lassen.
