Files
GerbilManager/CLAUDE.md

12 KiB

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)

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.

# 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):

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:

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):

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. Rezessiver Fuchs ist zwingend homozygot → e- ist ungültig (wirft), ee[-]ee.
  • Genetik-Engine = korrektheitskritisch: IMMER vollständig mit Tests absichern. Jede Änderung an src/genetics/** (+ Backend-Mirror + genotype.py) braucht pro Use-Case/Ticket einen Regressionstest (src/genetics/__tests__/genetics.test.ts und tools/import/test_genotype.py), damit nie eine Regression entsteht. npx vitest run + python test_genotype.py müssen grün sein, bevor „fertig".
  • 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-mcp19 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.