feat(ocr): substat-roll validation + rarity inference for GOOD export

Adds the accuracy check yas / Genshin Optimizer use: a substat value is only
legitimate if it equals round(sum of 1..6 rolls) from that stat's roll table.
Values that fit no combination at either rarity are guaranteed OCR misreads.

- src/lib/substatRolls.ts: 5-star roll tables (+ 4-star %/crit tables to tell
  rarities apart), pure isPlausibleSubstat/implausibleSubstats, and inferRarity
  (level > 16 or roll-table fit; conservative, defaults to 5). Validates against
  the union of rarities so valid 4-star pieces are not false-flagged.
- Wired in: shouldFlagArtifactForReview routes implausible substats to review;
  the parser adds an explanatory note; goodInterop export replaces the hardcoded
  rarity:5 with inferRarity (fixes wrong 4-star exports to GO/IK).
- 10 new unit tests; 131 total green; eval still 100%.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
AzuTear
2026-07-06 16:21:28 +02:00
parent b8309af377
commit 7930e369a7
5 changed files with 221 additions and 1 deletions
+2 -1
View File
@@ -2,6 +2,7 @@ import type { GoodExportArtifact } from "../types/global";
import type { StoredArtifactRecord } from "../types/storage";
import { knownSets, mainStatValueReferences, pieceToSet, pieceToSlot } from "./genshinData";
import { simplifyForMatch } from "./fuzzyMatch";
import { inferRarity } from "./substatRolls";
// GOOD (Genshin Open Object Description) interop for scanned artifacts, so the
// local store can round-trip with Genshin Optimizer / Inventory Kamera / Akasha
@@ -127,7 +128,7 @@ export function storedArtifactToGood(record: StoredArtifactRecord): GoodExportAr
return {
setKey: setNameToKey(record.setName),
slotKey: SLOT_TO_GOOD[record.slot] ?? "",
rarity: 5,
rarity: inferRarity(record.level ?? 0, record.substats),
level: record.level ?? 0,
mainStatKey: statDisplayToGoodKey(record.mainStat),
substats,