{"record":{"id":"37b277dab932a738","repo":"can1357/oh-my-pi","slug":"ambiguous-class-for-lower-tied-0-and","errorCode":null,"errorMessage":"ambiguous class for `${lower}`: `${tied[0]}` and `${tied[1]}` tie","messagePattern":"ambiguous class for `(.+?)`: `(.+?)` and `(.+?)` tie","errorType":"exception","errorClass":"AmbiguousIdentityError","httpStatus":null,"severity":"critical","filePath":"packages/catalog/src/compat/taxonomy.ts","lineNumber":155,"sourceCode":"\tconst lower = model.trim().toLowerCase();\n\tconst bare = bareOf(lower);\n\tlet winner: { rank: readonly [number, number]; cls: CompiledClass } | undefined;\n\tlet tied: readonly [string, string] | undefined;\n\tfor (const cls of rules.taxonomy.classes) {\n\t\tfor (const matcher of cls.matchers) {\n\t\t\tif (!matcherMatches(matcher, lower, bare)) continue;\n\t\t\tconst rank = [MATCHER_RANK[matcher.kind], matcher.token.length] as const;\n\t\t\tif (winner && winner.rank[0] === rank[0] && winner.rank[1] === rank[1] && winner.cls.id !== cls.id) {\n\t\t\t\ttied = [winner.cls.id, cls.id];\n\t\t\t} else if (!winner || winner.rank[0] < rank[0] || (winner.rank[0] === rank[0] && winner.rank[1] < rank[1])) {\n\t\t\t\twinner = { rank, cls };\n\t\t\t\ttied = undefined;\n\t\t\t}\n\t\t}\n\t}\n\tif (tied) {\n\t\tif (lenient) return { class: \"unknown\" };\n\t\tthrow new AmbiguousIdentityError(lower, tied[0], tied[1], \"class\");\n\t}\n\tif (!winner) return { class: \"unknown\" };\n\tconst ranks: ClassRanks = { class: winner.cls.id };\n\tconst family = classifyFamily(winner.cls, bare, lower, lenient);\n\tif (family !== undefined) ranks.family = family;\n\tconst revision = extractRevision(winner.cls, bare);\n\tif (revision !== undefined) ranks.revision = revision;\n\treturn ranks;\n}\n\nfunction ranksInClass(classId: string, model: string, lenient: boolean): Omit<ClassRanks, \"class\"> {\n\tconst cls = rules.taxonomy.classes.find(candidate => candidate.id === classId);\n\tif (!cls) return {};\n\tconst lower = model.trim().toLowerCase();\n\tconst bare = bareOf(lower);\n\tconst out: Omit<ClassRanks, \"class\"> = {};\n\tconst family = classifyFamily(cls, bare, lower, lenient);\n\tif (family !== undefined) out.family = family;","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/catalog/src/compat/taxonomy.ts#L137-L173","documentation":"AmbiguousIdentityError from classifyRanks: two class selectors in the taxonomy match a lowercased model id with equal rank, so class classification is indeterminate. Lenient mode degrades to class \"unknown\"; strict mode throws, naming both tied classes. Family and revision are only extracted after a unique class winner exists.","triggerScenarios":"cls()/ranks() on a model id where two class patterns (e.g. a generic \"openai\" class and a specific \"o-series\" class) both match with the same rank and neither is more specific.","commonSituations":"Introducing a new class KDL rule with a pattern that also matches ids claimed by an existing class; wildcard classes catching specific ids at equal rank.","solutions":["Differentiate the two class patterns so only one matches the colliding id.","Add an explicit rank/priority to one class rule to break the tie.","Regenerate via `bun run gen:compat` and verify classification of the previously ambiguous id."],"exampleFix":"// before: \"claude\" and \"claude-opus\" classes tie on \"claude-opus-4\"\nclass \"claude\" { match \"claude\" }\n// after: exclude the specific ids from the broad class\nclass \"claude\" { match \"claude\"; skip \"claude-opus\" }","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  const ranks = classifyRanks(lower, /* lenient */ false);\n} catch (err) {\n  if (err instanceof AmbiguousIdentityError && err.kind === \"class\") {\n    return { class: \"unknown\" };\n  }\n  throw err;\n}\n// or pass lenient=true to get { class: \"unknown\" } instead of throwing","preventionTips":["Use lenient mode for exploratory classification paths; strict mode in CI to catch KDL mistakes.","Keep wildcard class patterns disjoint from specific ones via skip lists.","Add explicit ranks to broad classes so specific ones win."],"tags":["catalog","taxonomy","classification","kdl"],"backgroundTag":"ambiguous-model-class-classification","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}