diff --git a/CLAUDE.md b/CLAUDE.md index 87783c6..0024d80 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -216,6 +216,16 @@ python test_extract.py test_extract_docx.py test_genotype.py test_merge_resol `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`. +- **C-Locus-Zygotie (GEN-6, Tickets bde4ec70/f89e95ad — Regel der Züchterin):** Platin, + Saphir und Platin-Hell unterscheiden sich **nur** in der C-Zygotie: `aa CC …pp` = **Platin**, + `aa Cc[chm] …pp` = **Saphir**, `aa Cc[h] …pp` = **Platin-Hell**. Der Katalog-Match kann + deshalb pro Locus ein **exaktes, ungeordnetes Allelpaar** verlangen — Token-Form `"x/y"` + (z. B. `C: 'C/cchm'`), sonst reduziert `locusToken()` auf das dominante Allel und der erste + Treffer (Platin) verschluckt die anderen. Gilt in **beiden** Mirrors (`catalog.ts` + + `genotype.py`) und im Seed (`colorVarietySeed.*`, `ApplicationContext.SeedColorVarieties`, + dort in Kompaktnotation `Ccchm`/`Cch` ohne Klammern). +- **`c[hm]` ist ein Schreibfehler-Alias für `c[chm]`** (fehlendes c in einigen Stammbaum-Charts): + beide Parser normalisieren ihn, sonst ist der Gencode unparsebar bzw. ergibt „…-Hell". - **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` @@ -330,6 +340,33 @@ python test_extract.py test_extract_docx.py test_genotype.py test_merge_resol - **EMF/WMF sind keine Fotos**: Excel legt neben dem Foto teils ein Vektor-Metafile ab; Browser können es nicht darstellen (kaputte Bildkachel in der Akte) → `extract._attach_photos` überspringt `.emf`/`.wmf` (Regressionstest in `test_extract.py`). +- **Resolution-Schlüssel in `conflict-decisions.json` (Stand 2026-08-19)**: neben + `father/mother(+Dob)`, `isResident`, `notes`, `receiver`, `dateOfDeath`, `deceased`, + `correctDob`, `renameTo`, `mergeExternalRefs`, `suppressExternalRefs`, `farbschlag` auch + **`goHomeDate`** (autoritativ, überschreibt auch einen vorhandenen/kaputten Wert und zieht + Status `GivenAway` nach, sofern nicht `Deceased`) und **`spottingType`** (Scheckungsart — + der Importer leitet sie aus keiner Quelle ab; `IngestResolvedService` überträgt sie nur, + wenn der Payload einen Wert hat, sonst bliebe eine von Hand erfasste Angabe nicht stehen). + **`genotype`** gewinnt bei **präzisem** Match (externalRef ODER name+dob) auch gegen einen + bereits gefüllten Wert — bei Wurfchronik-Tieren ist der meist nur der Kanon-Fill des + Farbschlags; ein name-only-Match füllt weiterhin nur leere Felder (Namensvetter-Schutz). +- **Pro Tier greift nur EINE Resolution** (first match: externalRef → endswith → name+dob → + name-only). Nie einen zweiten Eintrag mit derselben externalRef/demselben (name,dob) anlegen, + sondern den bestehenden **in place** erweitern — sonst gewinnt still der letzte und Felder + gehen verloren. **Wurfchronik-Jungtiere haben zur Override-Zeit noch kein DOB** (das kommt + später aus dem Wurfdatum) → dort ist `externalRef` der einzige zuverlässige Schlüssel. +- **`renameTo` zieht durch Freitext-Notizen** (`apply_rename_note_sweep`, letzter Schritt vor + dem Payload-Dump): der alte Name blieb sonst in Wurf-Notizen stehen („Blacky + Kruke v. …"). + Wortgenau, mit Guards — Quellname < 4 Zeichen oder > 2 Wörter wird ignoriert, kein Ersetzen, + wenn der alte Name im neuen Rufnamen steckt, und Notizen, die die Umbenennung selbst + dokumentieren („umbenannt"/„ursprünglich"), bleiben unberührt. Ohne diese Guards trifft der + Sweep Abnehmer-Namen und Gewichtsnotizen (im Probelauf 16 von 22 Ersetzungen falsch). +- **`parse_date` verwirft implausible Jahre** (< 1900 oder > aktuelles Jahr + 1) → `None`; + Wurfchronik-Transkriptionen bauen Daten sonst falsch zusammen (JackJack: „1310-05-13"). +- **Sternloses Geburtsdatum in Stammbaum-Charts**: `extract.py` normalisiert Zellen wie + `22.08.2019/+09.04.2024` (ohne `*`) zu `*…`, wenn Block-Kontext vorliegt (Genotyp ≤3 Zeilen + darunter, Namenszelle ≤3 darüber). Ohne das erkennt die Blockheuristik das Tier nicht und + `_reconstruct_parents` greift in den Nachbar-Ast (Ticket 65266679 Bijou). - **`renameTo` ist eltern-verknüpfungssicher**: umbenannte Tiere werden im Eltern-Namensindex zusätzlich unter ihrem **Quell-Namen** (`_pre_rename_name`) geführt, sonst verlieren Würfe, die den alten Namen referenzieren, ihren Eltern-Link (Ticket e0a0c304 Kruke→Kuke). `dateOfDeath` diff --git a/docs/ticket-triage.md b/docs/ticket-triage.md index aac4a96..e649225 100644 --- a/docs/ticket-triage.md +++ b/docs/ticket-triage.md @@ -9,6 +9,40 @@ die beklagten Dubletten stecken also schon in `resolved_import.json`. Mehrere Ti sind Folge-Tickets aus dem Juli-Lauf, deren Entscheidungen die neuen Chart-Varianten nicht mehr greifen (Wurfchronik-Elternstubs ohne Geburtsdatum). +## Ergebnis des Laufs (2026-08-19/20) + +**Alle 16 Tickets sind Resolved**, jeweils mit Changelog fuer die Zuechterin; in Prod sind +0 Tickets offen. Umsetzung in 5 datei-disjunkten Workstreams (Importer-Logik + Ingest-Backend, +Stammbaum-Extraktion, Genetik-Engine, Frontend, Daten-Entscheidungen), Verifikation zentral: +Python-Tests, 158 vitest, 325 Playwright, 290 dotnet-Tests gruen; Migration +`ReseedColorVarietiesGen6Saphir`; Deploy via CI; Prod-Ingest per Upload +(`-4 stale gerbils`, `-4 stale litters`, +1 Kontakt, 9 Freeze-Overrides respektiert); +27/27 Nachpruefungen gegen die Prod-API gruen. + +### Offene Folgeaufgaben (bewusst NICHT in diesem Lauf) + +- **H2-Wurf 13.04.2013**: listet 6 Jungtiere, die Wurfchronik nennt WS=5 — „Akina von den Kleinen + Chaoten" und „Alkina" sind vermutlich dasselbe Tier (Merge braucht eine Entscheidung der Zuechterin). +- **`tools/import/genotype.py` kann die interne Kompaktnotation nicht parsen** (`Ccchm`, `cchmcchm`, + `Cch`, `efef`) und `c[-]`/`e[-]`-Bracket-Unknowns: 88 Tiere laufen deshalb in den C/E-Guard von + `resolve_color_and_genotype` und behalten ihr Text-Label; 29 distinkte Gencodes liefern in TS und + Python unterschiedliche Namen. Ein Parser-Fix aktiviert die Farbherleitung von ~55 Tieren auf + einmal → eigenes Ticket mit Vorher/Nachher-Liste. +- **5 vorbestehende Doppel-Keys in `conflict-decisions.json`** (`('lotta','')`, `('sunny','2014-04-10')`, + `('jd','')`, `('blue','')`, `('danny','2009-10-09')`): dort gewinnt still der letzte Eintrag → + potenzieller Datenverlust, Aufraeum-Ticket. +- **Chart-Zeilenversatz bei tieferen Ahnen** (Akane-Muster) in den neuen Charts: Arya Stark erhaelt + Vance x Milka statt Vance x Sansa, Nani erhaelt Udo x Milka statt Udo x Hedwig. +- **Ophelie-Dublette**: „Stammbaum von Valentino Firehearts Kids.xlsx" fuehrt K79 „Ophelie von den + Kleinen Chaoten" mit Kathlins Geburtsdatum (*20.04.2021) → zweite Ophelie neben der echten + (*06.05.2023), vermutlich DOB-Verschreiber in der Quelle. +- **Kukes Gencode**: Chart sagt `aa CC DD Ee Gg Pp Spsp` (Schwarz-Schecke), rpro3 sagt + Anthrazit-Schecke (`gg`). Es gilt der Chart-Wert; im Ticket-Changelog ist die Rueckfrage vermerkt. +- **Merle**: zweiter Wurf mit Smoke vom 19.08.2024 (Tochter Speedy) ist noch nicht erfasst — im + Changelog erfragt. +- Die drei Merle-Tickets zeigen auf eine tote `gerbilId` (ce05fad7) → Links in der Ticketliste + laufen ins Leere; Umhaengen waere ein reiner Feedback-Update. + ## Cluster-Matrix | Tickets | Thema | Layer | Cluster | Aufwand | Konfidenz |