GEN-3f: reconcile cchm colourpoint catalog to the breeder's CP- naming
Computed Farbschlag now == the breeder's dropdown/seed names for colourpoints, so the mismatch hint stops and the live re-import name-matches her data (god: she uses the CP- prefix form + Marder/Siam/Zobel/Zobel-Hell; ZERO of the old portal suffix names). FULL collapse (god-approved, data-safe). Catalog (BASE_COLORS) 73 -> 61: the 24 portal cchm rows collapse to 12 breeder varieties — Marder, Siam, Zobel, Zobel-Hell (aa marten/sable group; E & D irrelevant per the merged GEN-3e rule) and CP-Agouti, CP-Silberagouti, CP-Algierfuchs, CP-Polarfuchs, CP-Fuchs, CP-Fuchs-Hell, CP-Blaufuchs, CP-Orangeschimmel (A- 'CP-<base>'; -Hell shade variants collapsed). - representativeGenotype() now splits a heterozygous 'x/y' token, so the het colourpoints Siam & Zobel-Hell (C: 'cchm/ch') carry the correct het canonical genotype and the engine computes their own names (verified). - Engine covers all fox(ee)/dilute(dd) A- cchm bases via the E-family fallback (no Unbekannt). KNOWN BEST-EFFORT GAP: CP-Fuchs-Hell and CP-Blaufuchs both compute 'CP-Fuchs' (the het-lighter / blue-dilute distinction isn't encoded); their distinct rows remain for hand-pick + import name-match. Closing this fully needs the breeder's exact fox-CP genotype rule. Regenerated colorVarietySeed.generated.json (61 rows) for Pam's re-seed migration. Gate green: build, eslint, vitest 78, e2e 80. de.ts untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -179,8 +179,10 @@ describe('Farbschlag catalog', () => {
|
||||
expect(match.name).toBe('Unbekannter Farbschlag')
|
||||
})
|
||||
|
||||
it('has the expected catalogue coverage (GEN-2: frozen 18 + 55 portal varieties)', () => {
|
||||
expect(CATALOG_SIZE).toBe(73)
|
||||
it('has the expected catalogue coverage (GEN-3f: 61 after cchm CP reconciliation)', () => {
|
||||
// GEN-3f collapsed the 24 portal cchm colourpoint rows to 12 breeder-named
|
||||
// varieties (Marder/Siam/Zobel/Zobel-Hell + CP-<base>), so 73 -> 61.
|
||||
expect(CATALOG_SIZE).toBe(61)
|
||||
})
|
||||
|
||||
it('frozen contract names round-trip to themselves (DB-key guard)', () => {
|
||||
@@ -206,7 +208,11 @@ describe('Farbschlag catalog', () => {
|
||||
expect(new Set(names).size).toBe(names.length)
|
||||
for (const entry of BASE_COLORS) {
|
||||
for (const [locus, value] of Object.entries(entry.tokens)) {
|
||||
expect(LOCI[locus as LocusKey].alleles).toContain(value)
|
||||
// GEN-3f: a token may be a heterozygous pair "x/y" (e.g. C: 'cchm/ch'
|
||||
// for the het colourpoints Siam/Zobel-Hell) — validate each allele.
|
||||
for (const allele of value.split('/')) {
|
||||
expect(LOCI[locus as LocusKey].alleles).toContain(allele)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -429,3 +435,46 @@ describe('GEN-3e: C-locus colourpoint naming', () => {
|
||||
expect(name('aa cchmcchm DD EE GG PP Spsp rere')).toBe('Marder Schecke')
|
||||
})
|
||||
})
|
||||
|
||||
describe('GEN-3f: CP catalog reconciled to the breeder CP- naming (matches her live data)', () => {
|
||||
const name = (s: string) => genotypeToFarbschlag(fromDisplayString(s))
|
||||
const has = (n: string) => BASE_COLORS.some((e) => e.name === n)
|
||||
|
||||
it('every CP- name her data uses exists as a catalog row (import name-match)', () => {
|
||||
for (const n of [
|
||||
'CP-Agouti', 'CP-Silberagouti', 'CP-Algierfuchs', 'CP-Polarfuchs',
|
||||
'CP-Fuchs', 'CP-Fuchs-Hell', 'CP-Blaufuchs', 'CP-Orangeschimmel',
|
||||
'Marder', 'Siam', 'Zobel', 'Zobel-Hell',
|
||||
]) {
|
||||
expect(has(n)).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('the removed portal suffix-names are gone (collapsed)', () => {
|
||||
for (const n of [
|
||||
'Agouti CP', 'Agouti CP-Hell', 'Silberagouti CP', 'Algierfuchs CP',
|
||||
'Polarfuchs CP', 'Blaufuchs CP', 'Kohlfuchs CP', 'Kohlfuchsschimmel CP',
|
||||
'Marder dd', 'Zobel dd', 'Siam (Marder-Hell)', 'Agouti dd CP',
|
||||
]) {
|
||||
expect(has(n)).toBe(false)
|
||||
}
|
||||
})
|
||||
|
||||
it('the engine computes the agouti CP- names her data uses (E-family fallback covers fox/dilute)', () => {
|
||||
expect(name('AA cchmcchm DD EE GG PP spsp rere')).toBe('CP-Agouti')
|
||||
expect(name('AA cchmcchm DD EE gg PP spsp rere')).toBe('CP-Silberagouti')
|
||||
expect(name('AA cchmcchm DD ee GG PP spsp rere')).toBe('CP-Algierfuchs')
|
||||
expect(name('AA cchmcchm DD ee gg PP spsp rere')).toBe('CP-Polarfuchs')
|
||||
// dd agouti fox has no dedicated base -> the eFamily fallback yields 'CP-Fuchs'.
|
||||
expect(name('AA cchmcchm dd ee GG PP spsp rere')).toBe('CP-Fuchs')
|
||||
// A- cchm efef -> CP-Orangeschimmel (Schimmel base under full C).
|
||||
expect(name('AA cchmcchm DD efef GG PP spsp rere')).toBe('CP-Orangeschimmel')
|
||||
})
|
||||
|
||||
it('het cchm/ch colourpoints (Siam, Zobel-Hell) resolve to their own names', () => {
|
||||
const siam = BASE_COLORS.find((e) => e.name === 'Siam')!
|
||||
const zh = BASE_COLORS.find((e) => e.name === 'Zobel-Hell')!
|
||||
expect(genotypeToFarbschlag(representativeGenotype(siam))).toBe('Siam')
|
||||
expect(genotypeToFarbschlag(representativeGenotype(zh))).toBe('Zobel-Hell')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user