fix(stammbaum): Geschwister-Verpaarung erkennen und im Diagramm zusammenführen

Vollgeschwister-Verpaarung (Vater & Mutter aus demselben Wurf) ist in der
Gerbil-Zucht häufig. Bisher zeigten Diagramm und Import sie nicht korrekt.

Import (merge_and_resolve.py):
- Wurf-Dedup gehärtet: asymmetrischer Merge nur bei kompatiblen Eltern-Namen;
  bei Date=None nur mit positivem Namens-Match (verhindert blindes Verschmelzen
  unverwandter datumloser Stubs). Falsch-Merges 112 -> 70.
- Parent-Resolver robust: Gender ist Präferenz statt hartem Filter, sodass
  vertauschte Eltern (z. B. weibliches Tier in der Vater-Position) trotzdem
  auflösen. Aufgelöste Eltern 200/206 -> 240/260.
- Neue Rollen-Normalisierung: weist jede Maus rollenrichtig nach Geschlecht zu,
  entfernt Selbst-Verpaarungen und unmögliche Doppelrollen.
  Selbst-Verpaarungen 16 -> 0, Gender-Rollen-Fehler 30 -> 0.
- Reine Helfer (litter_compatible/assign_parent_roles/names_*) auf Modulebene
  extrahiert und in test_merge_resolve.py (26 Tests) abgesichert.

Frontend (pedigree):
- buildAnimal markiert den Mutter-Knoten, wenn beide Eltern dieselbe litterId
  teilen (Vollgeschwister). toRawNodeDatum führt dessen Vorfahren-Ast zu einem
  Verweis-Knoten zusammen ("Geschwister von <Vater>, Eltern siehe oben"); die
  Vaterlinie zeigt die gemeinsamen Großeltern einmal.
- Druck-Ahnentafel bleibt vollständig (ignoriert die Marke).
- Build-Tests (+3) und e2e-Test (Inzucht-Kind-Fixture) ergänzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 14:02:45 +02:00
parent cb2ee03a14
commit 3715e32303
11 changed files with 666 additions and 75 deletions

View File

@@ -231,6 +231,44 @@
cursor: default;
}
/* Verweis-Knoten der Geschwister-Verpaarung: Vater & Mutter aus demselben
Wurf — der Vorfahren-Ast verweist auf die Vaterlinie statt ihn zu doppeln. */
.pedigree-card--sibling {
border-style: dashed;
border-color: var(--color-accent);
background: var(--color-accent-soft);
cursor: default;
}
.pedigree-card--sibling__icon {
flex: none;
font-size: 1.4rem;
line-height: 1;
color: var(--color-accent);
}
.pedigree-card--sibling__body {
display: flex;
flex-direction: column;
gap: 0.15rem;
min-width: 0;
}
.pedigree-card--sibling__label {
font-size: 0.82rem;
font-weight: 600;
color: var(--color-text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.pedigree-card--sibling__hint {
font-size: 0.72rem;
font-style: italic;
color: var(--color-text-muted);
}
.pedigree-card__photo {
flex: none;
width: 42px;