Statt fest http://localhost:5179 (auf dem Handy = das Handy selbst) nutzt der
Client im Dev-Modus den Host, von dem die Seite geladen wurde → 192.168.x.y:5179.
e2e/Rechner bleiben bei localhost.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Klarstellung für die Züchterin: übernommen werden Tiere/Stammbäume/Genotypen/
Würfe/Kontakte/Gehege+Reinigung/Erwerb/Reservierung/Rücknahmen/Warteliste/
Ausstellungen; (noch) nicht: Finanzen, Termine, Tierarzt-Stammdaten, Tagebuch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die KI kann im Text `tier:<8hex>` / `kontakt:` / `wurf:` / `gehege:` schreiben;
Frontend löst per POST /refs/resolve (Präfix-Match, DB-agnostisch) zu Name +
internem Link auf. Reine KI-Schreibhilfe — kein Nutzer-Button.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Ticket-Texte (Frage/Antwort/Nachricht/Changelog/Verlauf) rendern eine
Markdown-Teilmenge: **fett**, `code`, Aufzählungen, Links, Tier-/Ticket-IDs
- Offene Rückfrage (NeedsInfo): nicht bearbeitbar — die Züchterin soll antworten
- Beantwortet (Answered): Antwort nachträglich änderbar, bevor die KI übernimmt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bisher landeten beantwortete Tickets (Answered) in „Rückfragen". Sobald die
Züchterin geantwortet hat, ist sie fertig → das Ticket gehört in „Offen"
(von der KI zu erledigen). „Rückfragen" zeigt jetzt nur noch NeedsInfo
(wartet auf ihre Antwort).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Web-App-Manifest, App-Icons (192/512 + maskable, Rennmaus-Motiv) und
apple-touch-icon/theme-color in index.html → "Zum Startbildschirm hinzufügen".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Da Subagenten sitzungsgebunden sind, dient ein internes, in der Züchterin-UI
NICHT sichtbares Feld `agentContext` als Arbeitsgedächtnis: der Agent legt dort
Befunde/Ursache/Plan/„worauf gewartet wird"/erledigte Teilschritte ab und setzt
beim Wiederaufgreifen (Answered) genau dort an — statt neu zu untersuchen.
Trennung: agentContext technisch/intern; Frage/Antwort/fixNote bleiben
laienverständlich. PUT {agentContext} aktualisiert ohne Status-Wechsel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Mehrrundiger Dialog (Open→NeedsInfo→Answered→…→Resolved): jede weitere Antwort
bringt das Ticket wieder in Bearbeitung. Kontext-Träger ist das TICKET (voller
Verlauf + Triage-Eintrag), nicht der Subagent (sitzungsgebunden) — ein
wiederaufgreifender Agent rekonstruiert denselben Kontext; echte Agent-
Fortsetzung nur innerhalb einer Session via SendMessage. Datenmodell:
Frage/Antwort als Thread (mehrere Runden), nicht nur ein Paar.
- fixNote-Changelog: harte Regel „nur für die Züchterin, kein Technik-Geschwafel"
+ Gut/Schlecht-Beispiele (Tiernamen statt IDs, keine Datei-/Feldnamen).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Skill präzisiert: jeder bearbeitende Subagent schließt seinen Vorgang selbst ab —
Rückfrage ordentlich stellen (eine konkrete Frage, einfache Sprache, Kontext →
NeedsInfo) ODER nach verifiziertem + eingespieltem Fix mit verständlichem
fixNote-Changelog schließen (Resolved). Abschluss-Regel ergänzt: kein
Open/Answered-Ticket bleibt offen liegen — Endzustand immer NeedsInfo oder Resolved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Skill kapselt den erprobten Ablauf: Tickets aus /feedback holen, nach Status
einteilen (Open/Answered = KI dran, NeedsInfo/Resolved überspringen), je Ticket
ein Triage-Subagent (Workflow-Fan-out) mit strukturierter Empfehlung + Cluster,
Rückfragen via PUT question (→ NeedsInfo) anhängen, beantwortete Tickets wieder
aufgreifen, Fixes gebündelt nach datei-disjunkten Clustern umsetzen, Import neu
einspielen und Tickets MIT fixNote (verständlicher Changelog) auf Resolved setzen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- uw/Uwuw[d] (dichtes Underwhite) wird vor dem uw→G-Locus-Alias korrekt geparst
(Vance: nicht mehr „Unbekannter Farbschlag", zeigt G-Locus statt international uw). [#34,#32]
- ee[-] = sichtbarer Fuchs (rezessiv homozygot) → korrekt als Fuchs; bare „e-"
(rezessiv + unbekannt) ist genetisch unmöglich → genotypeInvalid statt still falsch. [#42]
- aa-Colourpoint-Zweig berücksichtigt jetzt D (Dilute) und E (Fuchs), statt fix
Zobel/Marder/Siam zu erzwingen. [#3]
- Wurf-Farbprognose: unbekanntes Allel (?) wird nicht mehr über die volle Dominanz
expandiert → keine unmöglichen Dilute-/Schimmel-/„Unbekannt"-Nachkommen. [#37,#39,#40,#41,#38]
Tests: test_genotype.py erweitert; genetics.test.ts +5 Blöcke (135 vitest grün, tsc/eslint sauber).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tickets können jetzt eine Rückfrage (Question) tragen und die Züchterin kann
in-app antworten. Feedback um FK-freie Felder Question/Answer/AnsweredAt erweitert
(überlebt Ingest-Wipe); Status-Lebenszyklus Open → NeedsInfo (Rückfrage gestellt)
→ Answered (beantwortet) → Resolved. Migration AddFeedbackQuestionAnswer.
PUT /feedback/{id}: question → NeedsInfo, answer → Answered+AnsweredAt; DTO gibt
die Felder zurück. Tickets-Seite (/hilfe/tickets) zeigt die Rückfrage hervorgehoben
und bietet ein Antwort-Feld + „Antworten"; Status-Badges Offen/Rückfrage offen/
Beantwortet/Gelöst.
Tests: FeedbackEndpointTests (Frage→NeedsInfo, Antwort→Answered, übersteht Ingest),
e2e tickets.spec.ts. dotnet/vitest/playwright grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Automatische Triage (ein Subagent pro Ticket): pro Ticket Ursache, Empfehlung,
Fix-Ebene, betroffene Dateien, Aufwand, Konfidenz + Überschneidungs-Matrix nach
Cluster und offene Rückfragen an die Züchterin. Grundlage für die gebündelte
Implementierung nach Cluster.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bindet den lokal installierten MemPalace-Server (mempalace-mcp) als projekt-
gescopten MCP-Server ein, damit das KI-Gedächtnis ab der nächsten Claude-Session
verbunden ist und die Integration im Repo (geteilt/versioniert) statt nur lokal
liegt. Aktiviert nach Claude-Neustart (Bestätigung des MCP-Servers nötig).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die ~1095 geparsten Verträge werden jetzt als echte SaleContract-Records erzeugt,
sodass die Verträge-Seite gefüllt ist (statt 0). enrich_from_contracts() liefert
zusätzlich eine saleContracts-Liste (deterministische Id aus Dateiname, Käufer-
ContactId, Preis, Abgabe-/Vertragsdatum, FileName, gematchte Tier-Ids); main()
schreibt sie als Top-Level-Key in resolved_import.json. IngestResolvedService legt
SaleContract + SaleContractAnimal an (FK-sicher nach Kontakten/Tieren, unbekannte
Links übersprungen) — Tabelle wird wie gehabt gewischt und aus dem Payload neu
befüllt (idempotent).
Ergebnis: 1033 Verträge (611 mit ≥1 Tier, alle datiert; 54 Dateinamen-Dubletten
zusammengefasst, 7 datumlose + 1 ohne Käufer übersprungen). Kein Schema-Change
(datumlose übersprungen statt Spalten nullable → keine Migration).
DOCX-Download: importierte Verträge haben keine Word-Datei im contract-storage →
DTO.HasFile=false, Frontend blendet den Word-Button aus (Hinweis „Keine Word-
Datei"), PDF wird weiterhin generiert. Download-Endpoint liefert sauber 404 statt
500 bei fehlender Datei.
Tests: dotnet 213, vitest 129, playwright 16 (neuer vertraege.spec.ts), Python grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neuer Parser extract_contracts.py liest die ~1,4k Abgabevertrags-DOCX
(\truenas\…\Verträge): er extrahiert aus dem Dokument-Body (zuverlässiger als
die Dateinamen) Käufer, Tier(e), Farbschlag, Abgabedatum und Preis — robust
gegen Word-Run-Splits (z. B. „F r au"/„3 0,00"); überspringt Vorlage,
Abstammungsnachweise und als .docx getarnte .doc.
enrich_from_contracts() in merge_and_resolve.py: Käufer werden als Kontakte
(IsReceiver) angelegt/zusammengeführt; Tiere werden KONSERVATIV per Rufname
(+ DOB-Jahr bei Mehrdeutigkeit) auf eigene Bestandstiere gematcht und erhalten
ReceiverContactId, GoHomeDate und Status „abgegeben" — nur wo nicht bereits
gesetzt; Konflikte werden geloggt, nicht überschrieben. Jede Übernahme bekommt
eine Herkunfts-Zeile („Abgabe an … aus Vertrag … übernommen.").
Ergebnis: 1095 Verträge → 783 Tier-Treffer (400 mehrdeutige übersprungen),
274 neue Abnehmer-Kontakte, 153 Tiere mit Abnehmer, 49 mit Abgabedatum,
23 neu „abgegeben". Keine Backend-/Frontend-Änderung nötig (Akte zeigt Abnehmer/
Abgabedatum/Herkunft bereits). SaleContract-Records bewusst nicht erzeugt
(bräuchte Migration + ingest-sichere Id — späterer Schritt).
Tests: test_extract_contracts.py (Dateiname/Body/Run-Split/Skip-Regeln) + alle
bestehenden grün; dotnet 212.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Herkunfts-Verlauf nennt jetzt auch, wenn ein Wert aus einer Quelle VERWORFEN
wurde — mit Grund und was stattdessen verwendet wurde (⚠-Markierung), z. B.:
- „⚠ Vater-Kandidat ‚Danielle' (*04.03.2020) verworfen — falsches Geschlecht für
die Vaterrolle; ‚Hagrid Rubeus' verwendet." (Molly)
- „⚠ Geschlecht weiblich aus ‚Wurfchronik.docx' verworfen — abweichend; männlich
aus ‚Alberto Kids.xlsx' verwendet (Mehrheit)." (Solice)
- „⚠ Vater ‚Jasper Jacob' (*2018) verworfen — unplausibel (7 Jahre älter); kein
Ersatz." / DOB-Remap: „⚠ Geburtsdatum 22.08.2018 verworfen — per manueller
Entscheidung korrigiert; 14.07.2019 verwendet." (Kazuya)
Import: generischer _format_discard()-Helfer; Verwerfungen aus Mehrheitsentscheid,
pick_parent_ref-Ablehnungen (explain_pick_rejections), Rollen-Normalisierung,
Eltern-Alter-Sanity-Check und DOB-Remaps werden über _discarded/_prov_args an die
Tiere gehängt; Gerbil-Provenance wird in einem finalen Pass gebaut (nachdem alle
Verwerfungen feststehen). Frontend hebt ⚠-Zeilen ab (+ Legende).
Tests grün (python merge/extract, vitest 129, playwright 8, dotnet 212).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auf dem Desktop wurde der Baum bei festem Zoom 1 rechts abgeschnitten (man sah
nur 1–2 Generationen, der Rest lag außerhalb) und viel Fläche blieb leer. Die
Standard-Ansicht passt jetzt den gesamten geladenen Baum ein — gedeckelt auf
Zoom 1, damit Karten bei kleinen Bäumen nicht vergrößert/unscharf werden. Mobil
bleibt es bei „Wurzel links + Pan" (sonst würden die Karten winzig). Zeichen-
fläche von 62dvh auf 72dvh erhöht (füllt die Desktop-Höhe besser).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Herkunft liest sich jetzt wie eine History: pro Feld wird genannt, aus
welcher Quelldatei der Wert kam, plus Merge-/Entscheidungs-/Wurfchronik-Schritte.
Beispiel: „In ‚X.xlsx' gefunden." → „Geburtsdatum (…) aus ‚X.xlsx'." → „Auch in
‚Y.docx' gefunden → zusammengeführt." → „Eltern über Position erkannt (Quelle …)."
Import: build_entity_provenance() um history[] erweitert; ein field_source-
Tracking im Gerbil-Dedup hält fest, welcher Datensatz/welche Datei jedes Feld
(DOB/Genotyp/Geschlecht/Farbschlag/Sterbedatum) lieferte — auch über Empty-Fill
und Mehrheitsentscheid hinweg. Kontakte und Würfe erhalten analoge, datei-
attribuierte Verläufe. Kein DB-Migration nötig (history liegt im Provenance-JSON).
Frontend: ProvenanceDialog rendert den Verlauf als nummerierte Timeline.
Tests: history wird erzeugt, nennt Dateien, überlebt Ingest (dotnet 212,
vitest 129, playwright 17, python merge/extract grün).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Das Rechtsklick-Kontextmenü (ID kopieren / Fehler melden) war auf Mobil nicht
erreichbar (onContextMenu feuert auf Touch nicht zuverlässig). Jetzt öffnet ein
langer Druck (~500 ms ohne Verschieben) auf eine Ahnenkarte dasselbe Menü; der
darauf folgende synthetische Klick (Umwurzeln) wird unterdrückt. Bewegung >10 px
bricht den Long-Press ab (kollidiert nicht mit Pan/Zoom). Legenden-Hinweis
ergänzt. e2e: Long-Press öffnet das Menü (desktop; phone-Projekt übersprungen,
da synthetischer Touch dort instabil).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neuer „Datenherkunft"-Button in der Rennmausakte öffnet einen Dialog, der zeigt,
aus welchen Quellen der Eintrag erzeugt wurde: Quelldateien (Stammbäume +
Wurfchronik), Anzahl zusammengeführter Datensätze, Eltern-Herleitung
(Methode/Konfidenz) und Hinweise (z. B. „per manueller Entscheidung zugeordnet",
„Konflikt gelöst", „aus N Datensätzen zusammengeführt").
Import: build_provenance() in merge_and_resolve.py sammelt die Herkunft über alle
deduplizierten Datensätze und schreibt sie als Provenance-JSON je Tier in
resolved_import.json. Backend: Gerbil.Provenance (nullable text) + Migration
AddGerbilProvenance, gemappt im Ingest und im GerbilDto zurückgegeben. Frontend:
ProvenanceDialog + Typen + Strings.
Tests: Ingest-Round-trip (vorhanden/abwesend), e2e provenance.spec.ts.
dotnet(212)/vitest(129)/playwright/tsc/eslint grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rechtsklick auf eine Maus im Stammbaum öffnet ein Kontextmenü mit „ID kopieren"
(Clipboard + Toast) und „Fehler melden". Zusätzlich „Fehler melden"-Buttons in
der Rennmausakte und der Wurf-Ansicht. Der Dialog erfasst eine Beschreibung und
sendet sie samt Debug-Kontext (Ansicht, Tier-/Wurf-ID + Name, URL, Zeitstempel,
User-Agent) an POST /feedback; gespeichert in einer neuen Feedback-Tabelle.
Backend: Feedback-Entity (lose nullable GerbilId/LitterId ohne FK), Endpoints
POST/GET /feedback, EF-Migration AddFeedback. Die Tabelle wird vom Import-Ingest
NICHT geleert — Feedback überlebt Re-Ingests (Test deckt das ab).
Tests: FeedbackEndpointTests (persistiert, 400 bei leer, übersteht Ingest-Wipe);
e2e feedback.spec.ts. tsc/eslint/vitest(129)/playwright(6)/dotnet(212) grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die in 03ad9e6 eingeführte DOB-Bevorzugung war zu grob: ein datierter, aber
geschlechts-falscher Ref (z. B. weibliche „Danielle") gewann den Vater-Slot
gegen einen undatieren männlichen/unbekannten (z. B. „Hagrid Rubeus") — wodurch
Mollys korrekte Mutter Arya Stark in der Rollen-Normalisierung verloren ging.
pick_parent_ref wertet jetzt zuerst das (aus den Tierdaten ermittelte) Geschlecht
für die Rolle: ein klar falsches Geschlecht wird stark abgewertet, erst danach
zählt plausibles DOB > kein DOB > unplausibles DOB. Fixt Molly (Hagrid × Arya)
UND behält Solice (Lui × Molly = Geschwister von Silvain). Keine Abdeckungs-
Regression (0 Selbst-Verpaarungen / 0 Gender-Fehler / 0 Alter-Verletzungen).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>