# Decisions This document contains Architecture Decision Records. ## ADR Index | ID | Title | Status | Date | | --- | --- | --- | --- | | ADR-001 | Build a local Electron app first | Accepted | 2026-07-04 | | ADR-002 | Use screen capture as the primary scanner source | Accepted | 2026-07-04 | | ADR-003 | Keep APIs and GOOD compatibility optional | Accepted | 2026-07-04 | | ADR-004 | Treat in-game marking as a later opt-in feature | Accepted | 2026-07-04 | | ADR-005 | Use a generated Genshin data package for OCR matching | Accepted | 2026-07-04 | | ADR-006 | Persistent input helper and JSON artifact store before SQLite | Accepted | 2026-07-04 | | ADR-007 | Measure OCR accuracy with a labeled eval harness before reworking the scanner | Accepted | 2026-07-05 | | ADR-008 | Replace the PowerShell input/capture helper with a C# sidecar | Accepted | 2026-07-05 | | ADR-009 | Resolution-anchored layout profiles and OCR preprocessing over color detection | Accepted | 2026-07-05 | | ADR-010 | Elevated dev runner and bounded live automation probes | Accepted | 2026-07-07 | | ADR-011 | Retire alternate OCR comparison paths after current scanner baseline | Superseded | 2026-07-09 | | ADR-012 | Separate live scan results, artifact inventory, and value evaluation | Accepted | 2026-07-09 | | ADR-013 | Move high-speed scanning into the native helper and vendor IK inventorylists | Accepted | 2026-07-09 | | ADR-014 | Separate roll efficiency from build fit and project only legal roll bounds | Accepted | 2026-07-10 | | ADR-015 | Adopt a scanner-first Galaxy UI and explicit feedback primitives | Accepted | 2026-07-10 | | ADR-016 | Default to the owned inventory and keep scan feedback session-bound | Accepted | 2026-07-10 | | ADR-017 | Stream native capture jobs into bounded evaluation workers | Accepted | 2026-07-10 | | ADR-018 | Gate Build-Fit behind a versioned evidence contract before scoring | Accepted | 2026-07-10 | | ADR-019 | Repair five-star +0 main values only from structural rarity evidence | Accepted | 2026-07-10 | | ADR-020 | Target only exact supported Genshin process names | Accepted | 2026-07-10 | | ADR-021 | Require explicit profile assumptions and source-bound aggregate context | Accepted | 2026-07-10 | | ADR-022 | Rank only source-bound Build-Fit evidence with profile expiry | Accepted | 2026-07-11 | | ADR-023 | Fail-closed visual rarity gate and read-only non-five-star inventory | Accepted | 2026-07-11 | | ADR-024 | Default renderer copy to English while preserving the English scanner boundary | Accepted | 2026-07-11 | | ADR-025 | Make artifact deletion an app-local tombstone workflow | Accepted | 2026-07-11 | | ADR-026 | Persist first-observed native run timing on one shared clock | Accepted | 2026-07-11 | ## ADR-001: Build A Local Electron App First ### Status Accepted ### Context The product needs a Windows desktop UI, local screen capture, possible overlay windows, and future optional input automation. ### Decision Use Electron with React and TypeScript for the MVP. ### Consequences - Fast UI iteration and easy local packaging. - Electron main-process code must be treated as a separate boundary from renderer code. - Native or Rust sidecars can be added later for high-performance capture/OCR work. ## ADR-002: Use Screen Capture As The Primary Scanner Source ### Status Accepted ### Context The user wants an app that works without optimizer imports, external scanner tools, Enka, or HoYoLAB as core dependencies. ### Decision Use local screen capture as the primary source. Current Smart Capture focuses Genshin, hides the app, captures the primary screen through Windows GDI, detects the artifact detail panel, then OCRs focused crops. ### Consequences - The app remains offline-first. - OCR and crop reliability are core product risks. - UI language, resolution, HDR, and game layout changes need explicit test coverage. ## ADR-003: Keep APIs And GOOD Compatibility Optional ### Status Accepted ### Context External APIs and existing optimizer formats can speed up setup, but should not define the main user workflow. ### Decision Keep Enka, HoYoLAB, Akasha, Genshin Optimizer, and GOOD import/export as optional future compatibility layers. ### Consequences - The app can work without external accounts or cookies. - Data package and scanner quality become more important. - Compatibility can be added when it helps testing, migration, or export. ## ADR-004: Treat In-Game Marking As A Later Opt-In Feature ### Status Accepted ### Context Locking or marking artifacts in game may save time, but input automation increases ToS and misclick risk. ### Decision Do not ship in-game marking in the scanner MVP. If implemented later, it must be off by default, reversible, whitelisted, previewed before execution, and stoppable with ESC or user mouse movement. ### Consequences - Early scanner work stays lower risk. - App-internal triage remains the first decision layer. - No delete, feed, enhance, or resource-spending automation is allowed. ## ADR-005: Use A Generated Genshin Data Package For OCR Matching ### Status Accepted ### Context Hardcoded arrays for characters and artifact sets caused repeated scanner failures whenever the user tested a newer character, set, or artifact name. ### Decision Generate `src/data/genshinGameData.json` from `genshin-db` and use it as the local matching dictionary for artifact sets, artifact piece names, characters, slots, main stats, and substats. ### Consequences - The scanner can recognize new characters and sets as soon as the local data package is regenerated from an updated `genshin-db`. - Parser logic stays generic and testable instead of growing one-off fixes. - OCR still needs good crops and text quality; the data package improves recognition but cannot solve unreadable screenshots by itself. ## ADR-006: Persistent Input Helper And JSON Artifact Store Before SQLite ### Status Accepted ### Context Per-action PowerShell scripts recompiled the Win32 interop for every click, scroll, and capture (1-2s each) and one `Marshal::SizeOf` call was broken in Windows PowerShell 5.1, so SendInput clicks silently never executed. Scan results were also not persisted anywhere; only review samples reached disk. Adding `better-sqlite3` (native module) was considered too heavy for this step. ### Decision Run one persistent PowerShell helper process (compiled once, JSON protocol over stdin/stdout) for focus, cursor/ESC state, click, scroll, and GDI capture. Persist parsed artifacts into `artifact-store.json` in userData, deduplicated by a content signature that excludes the equipped character. Keep SQLite as the planned future store; the JSON store is the migration source. ### Consequences - Batch scans become fast enough to be testable and the failsafe (ESC or user mouse movement aborts) can poll cheaply between actions. - Automated clicks are verified by checking that the parsed detail signature changed; repeated failures abort with a diagnosis hint instead of clicking blindly. - Leveling an artifact changes its signature and creates a new record; rescan-merge is an open follow-up. - If the helper process dies it is respawned on the next request; pending requests fail loudly instead of hanging. ## ADR-007: Measure OCR Accuracy With A Labeled Eval Harness Before Reworking The Scanner ### Status Accepted ### Context OCR and crop reliability are the core product risk (ADR-002), but there was no way to measure field-level accuracy. Every OCR, crop, or parser change was a blind change - regressions could only be caught by a human re-testing against the live game, and there was no baseline to compare a new engine or preprocessing step against. ### Decision Add a field-level eval harness (`src/eval/`) that runs the real `parseArtifactCandidate` over a labeled corpus and reports per-field and per-case accuracy. Seed the corpus from the existing parser test cases (verified labels), and grow it from human-confirmed review samples via `reviewSampleToEvalCase`. Run it as a gate under `npm test` (must stay 100% on the verified seed) and as a full report via `npm run eval`. ### Consequences - OCR/layout/preprocessing changes are measured, not guessed at; a new engine or a preprocessing step has to beat a recorded baseline. - The review queue does double duty: it flags artifacts for human correction and feeds the eval corpus. Review-sample `parsed` blocks are label *candidates*, never ground truth, to avoid the parser grading itself. - The corpus label is the source of truth. If a code change intentionally alters a correct output, the label is updated in the same commit. ## ADR-008: Replace The PowerShell Input/Capture Helper With A C# Sidecar ### Status Accepted ### Context The persistent PowerShell helper (ADR-006) still carries Windows PowerShell 5.1 quirks (the `Marshal::SizeOf` interop bug), compiles Win32 interop at startup, and captures each frame by writing a PNG to the temp directory and reading it back. A C#/.NET sidecar with SendInput and direct GDI/BitBlt capture is a cleaner fit for the validated Windows automation path. ### Decision Replace the PowerShell helper with a self-contained .NET (C#) sidecar that speaks the same JSON-over-stdin/stdout protocol, so the Electron-side `InputHelperService` interface stays stable. The sidecar does per-monitor DPI-aware SendInput click/scroll, BitBlt client-rect capture returning bytes without a temp file, and elevation detection. ### Consequences - No PS 5.1 marshalling bugs, no per-call interop compile, no temp-PNG churn; lower latency makes batch scans and the ESC/mouse failsafe polling cheaper. - Adds a .NET build/publish step and ships a compiled exe with the app. - The migration is behind the existing service interface, so the renderer and scan loop do not change. ## ADR-009: Resolution-Anchored Layout Profiles And OCR Preprocessing Over Color Detection ### Status Accepted ### Context The current pipeline finds the artifact detail panel with hardcoded orange/green color thresholds (`inferDetailRect`) and then crops fixed percentages of that guessed rectangle. This is brittle against HDR, color profiles, UI scale, aspect ratio, and game UI updates. A stricter borderless 16:9 profile with fixed crop coordinates from a reference resolution is easier to validate and reproduce than per-frame color hunting. ### Decision Adopt the same approach: require borderless 16:9, drive crops from resolution-anchored layout profiles scaled from a reference resolution (color detection only as a fallback), and add a per-region preprocessing pass (grayscale, upscale, threshold, invert) plus a digit-whitelist mode for numeric fields. The set name stays derived from the static piece-to-set data package (no dedicated set-effect crop). English-only OCR is accepted. Every change is validated against the ADR-007 eval harness. ### Consequences - Crop positions become deterministic per resolution instead of per-frame guesses. - Preprocessing is expected to lift accuracy enough that a custom OCR engine is only pursued if the eval harness shows Tesseract-plus-preprocessing is insufficient. - Non-16:9 or non-borderless setups are explicitly unsupported for the auto scanner; the app should detect and warn rather than silently misread. ## ADR-010: Elevated Dev Runner And Bounded Live Automation Probes ### Status Accepted ### Context Automatic grid scanning needs read-only mouse movement, click, and wheel input to reach the focused Genshin window. A lower-integrity app can fail to deliver input to an elevated or protected target because of Windows UIPI/integrity boundaries. During live testing, `npm run dev:admin` originally printed that a new Administrator window was started, but the elevated PowerShell received no arguments, so the intended dev process did not reliably start. The project also needed a smaller live validation path than a full inventory scan. A full scan is too risky as the first proof of input delivery because it can click many tiles before a bad coordinate, focus issue, or blocked input is understood. ### Decision Keep automatic scan input automation read-only and require an elevated runtime when Windows reports that automation would otherwise be blocked. Replace the old `dev-admin.cmd` entry with `scripts/dev-admin.ps1`, quote the elevated PowerShell arguments explicitly, and log elevated startup to `outputs/admin-start/admin-dev.log`. Add dev-only HTTP checks: - `/automation/probe-click?index=N` or `?row=R&col=C` performs one safe inventory selection click and verifies whether the detail panel changed. - `/scanner/start?limit=N` sends a temporary scan-limit payload to the renderer, so live auto-scan validation can start with two items instead of the UI default. Document the workflow in [AUTOMATION_LIVE_SCAN.md](AUTOMATION_LIVE_SCAN.md). ### Consequences - The user still has to approve Windows UAC manually; the app must not try to click the Secure Desktop prompt. - We can distinguish input delivery from OCR/parser quality with a one-click probe before running any broader scan. - Live validation now has a low-risk path: check elevation and Genshin detection, run a single probe click, then run a bounded `limit=2` scan. - The implementation remains inside the allowed safety boundary: no memory reads, hooks, injection, game-file modification, deleting, feeding, enhancing, locking/unlocking, or spending resources. ## ADR-011: Retire Alternate OCR Comparison Paths After Current Scanner Baseline ### Status Superseded ### Context The project previously carried alternate OCR-engine and comparison paths to decide whether the current scanner should change OCR defaults. The current visible-inventory path has now produced clean 100-artifact evidence, and the next product phase is result clarity, artifact inventory, detail evaluation, and corpus growth. Keeping an alternate engine path increases app size and maintenance surface without serving the current user workflow. ### Decision Retire the alternate OCR-engine and current-vs-reference comparison paths. Keep a single current OCR/capture path and preserve the quality-gated live soak assessment. A qualified scan result must: - finish cleanly, - parse at least the requested count, - keep miss rate at or below 2%, - keep review rate at or below 15%, - report active scan timing and bottlenecks, - come from a runtime whose `/health.appBuild.signature` matches the current source. `scripts/live-soak.ps1` writes the evidence bundle and `scan-performance-assessment.json`. `npm run scan:assessment:test` verifies that the ranking logic rejects fast but low-quality synthetic runs without needing Genshin. `npm run scan:assessment:validate -- --summary` prints the assessment path and `createdAt` timestamp so reports can cite the exact evidence file. ### Consequences - Speed claims cannot be based on click count or elapsed time alone. - A new OCR engine is out of scope until scanner UX, inventory, detail evaluation, and corpus growth justify reopening that work. - Stale elevated Electron instances are treated as invalid evidence, not as a harmless warning. - The validated `:wait` scan scripts are acceptable for manual post-UAC startup; non-waiting scripts remain useful when automation should fail fast. ## ADR-012: Separate Live Scan Results, Artifact Inventory, And Value Evaluation ### Status Accepted ### Context The visible-inventory scanner baseline is fast enough for the current product phase. The next user value comes from better artifact content extraction and a clearer surface for scanned artifacts, not from another broad speed pass. The scan page currently has more diagnostic/result detail than the operator needs during a live scan. Showing every stat, confidence value, and evaluation while the scanner is running makes the main workspace noisy and increases the risk that a user treats uncertain OCR as a final artifact judgment. The desired product flow is: - left side: screenshot/preview, - right side: compact live list of finished artifact results, - later menu item: browsable scanned artifact inventory, - click-through detail: screenshot, parsed stats, OCR confidence, evaluation reasons, and upgrade projection. ### Decision Adopt a split scan/result/inventory model: - The active scan page shows the latest screenshot/preview and a compact live result rail. - The live rail shows only scan number, artifact name or compact fallback, artifact value score `0-100`, and a result pill after evaluation has finished. - Extraction confidence and artifact value are separate data concepts. A low confidence read becomes `Review`; it must not be silently displayed as a normal weak artifact. - Debug stats, raw OCR, confidence breakdowns, and detailed evaluation belong in diagnostics, scan summary, or artifact detail. - Add a scanned artifact inventory view for browsing, filtering, sorting, and opening details. - Add artifact detail evaluation before promoting broad recommendations. - Add upgrade projection only as a detail-level feature, with worst/middle/best projected value scores and clear uncertainty labeling. - Use one game-control producer for click/scroll/capture and bounded downstream OCR/parse/evaluation workers. Publish only fully evaluated entries into the current-session result rail. ### Consequences - The scan page becomes calmer and more task-focused. - The app can surface useful artifact outcomes without hiding OCR uncertainty. - Inventory and detail views become the natural place for richer analysis. - Recommendation work has a cleaner dependency chain: trusted scans, compact results, artifact inventory, detail evaluation, then build recommendations. - Renderer-loop speed work remains secondary unless measured timings show a real regression; native capture speed work is handled by ADR-013. ## ADR-013: Move High-Speed Scanning Into The Native Helper And Vendor IK Inventorylists ### Status Accepted ### Context Inventory Kamera is fast because the game-control loop is native and the UI does not perform per-artifact work. The Electron renderer path paid for focus, capture, OCR, parse, persistence, status updates, and UI state inside one loop. The user also has Inventory Kamera 1.4.4 locally, including `inventorylists` that can be used 1:1 as scanner reference data. ### Decision Use the C# input helper as the high-speed scanner service. Electron starts, stops, packages, and reports status. React remains a visual control surface. The IK `inventorylists` are copied into `data/ik-inventorylists` and packaged as extra resources. The first native milestone captures artifact detail card crops quickly; OCR, parsing, GOOD persistence, and evaluation are separate downstream steps. Each native run writes `manifest.json`, `capture-jobs.jsonl`, and `status.json`; downstream workers must consume those files instead of asking the renderer to do per-artifact scanner work. The first downstream worker is a native result processor that tails `capture-jobs.jsonl` and writes both `scan-results.json` and `processing-report.json`. `scan-results.json` is the durable result contract: one entry per captured artifact card with extraction status, artifact identity when parsed, review state, and versioned value evaluation. `processing-report.json` remains the diagnostic OCR/parse report. The worker may OCR, parse, and evaluate while capture is still producing jobs, then drains the remaining queue after a done or stopped producer. It does not persist by default; DB writes require explicit opt-in after native crop OCR is validated with live evidence. ### Consequences - Capture throughput can be optimized without renderer roundtrips per artifact. - The scanner data source now matches IK's artifact names and set/piece keys. Other vendored IK lists are data only until those values are actually scanned. - Evaluation remains downstream and cannot block capture speed. - Live safety stays read-only: no memory reads, hooks, injection, game-file modification, deleting, feeding, enhancing, or spending resources. - Non-16:9 layouts currently block in native preflight until layout support is expanded. ## ADR-014: Separate Roll Efficiency From Build Fit And Project Only Legal Roll Bounds ### Status Accepted ### Context A universal Artifact quality score is misleading without a character, role, team, and build target. However, the visible substat values do contain one objective signal: how efficient their legal roll tiers were. Extraction confidence is a third concept and must not be collapsed into either one. Under-leveled Artifacts also invite false precision. A projection cannot know which substat receives the next roll or which legal tier is selected. ### Decision Implement `roll-efficiency-v1` as a context-free `0-100` score derived only from rarity-specific legal roll values, legal total roll counts, and displayed rounding. Persist reason codes and per-substat breakdown. Keep build fit explicitly `deferred`; do not add set, main-stat preference, locked state, or equipped state to roll efficiency. Review, incomplete, or inconsistent results receive `review` or `unknown` and no normal score. For under-leveled Artifacts, show a detail-only Worst/Middle/Best bound from remaining +4 steps and legal roll tiers, only for an unambiguously identified 5-star Artifact when all four current substats are known. Label it as non-guaranteed; 4-star or rarity-ambiguous data stays unavailable in V1. ### Consequences - Users can compare roll-tier luck without being told that every high-roll Artifact fits every build. - Build-aware recommendation scoring remains a separate future contract. - Existing saved native results can be evaluated without rescanning. - Offline replay can hash derived results and detect non-determinism. - Projection is conservative and unavailable when the fourth substat or clean extraction is missing. ## ADR-015: Adopt A Scanner-First Galaxy UI And Explicit Feedback Primitives ### Status Accepted. The original Builds lock is narrowed by ADR-022: the surface may now show only source-bound, read-only evidence suggestions, never a general build or optimizer claim. ### Context The application exposed scanner setup, technical pipeline state, inventory, review, prototype recommendations, overlay, and diagnostics with similar visual weight. The default scan path was harder to understand than the underlying workflow, while the Builds and triage surfaces could imply confidence that the current Build-Fit contract and review data do not support. The renderer also lacked one consistent contract for loading, mutation feedback, focus, and motion. Local status text alone made successful promotion, review, import, or export actions easy to miss. ### Decision Adopt a scanner-first information architecture and tokenized Galaxy visual system: - Group Scanner, Artifacts, Review, and the explicitly labeled Builds preview as the main workspace. Keep Overlay and Diagnostics as secondary tools. - Lead the Scanner page with app/Genshin readiness and one primary start/stop action. Keep source selection, scan scope, and manual capture under advanced options while preserving all preflight and safety gates. - Treat Review as scan-data correction. Do not derive or display trash, feed, deletion, or universal quality judgments from extraction uncertainty. - Keep Builds clearly bounded until the versioned Build-Fit contract exists. Under ADR-022 it may show only source-bound, read-only evidence suggestions; `roll-efficiency-v1` remains visible in Artifact detail but is not relabeled as build suitability. - Centralize semantic colors, surfaces, focus, depth, spacing, and motion in Galaxy design tokens. Use shared skeleton, spinner, and toast primitives for content loading and explicit mutation outcomes. - Preserve durable progress and error state in the owning view. Toasts are transient confirmation only. Respect `prefers-reduced-motion` and keep state understandable through text/icons without relying on animation or color. ### Consequences - A first-time user can follow the scan-to-review journey without opening diagnostics or understanding the native pipeline. - Technical evidence remains available but no longer dominates the default inventory and scan surfaces. - Loading and mutation feedback becomes consistent across features without changing the scanner, review, or persistence authority boundaries. - New renderer views must reuse the shared tokens and feedback semantics and pass keyboard, reduced-motion, overflow, and desktop visual acceptance. ## ADR-016: Default To The Owned Inventory And Keep Scan Feedback Session-Bound ### Status Accepted. The real Settings-UI Artifact-limit path is live accepted at 5/5, the packaged row-limit path at one detected eight-column row is accepted at 8/8, and the packaged full-owned-inventory baseline is accepted at 2,211/2,211 over 70 pages with current-package reprocessing at 136 Review (6.15%). ### Context A small numeric default makes the main scan action behave like a sample even when the user's intent is to import the whole owned Artifact collection. At the same time, using the inventory capacity as the target would be incorrect: it is not the number of owned Artifacts, and the native scanner does not yet detect the physical end of the list reliably enough to stop an oversized target without risking repeated bottom rows. The Scanner also showed progress in a separate card while the larger left work surface displayed onboarding or a capture. That split made the most important running state harder to follow. Loading previous stored/native results into the right rail at startup further blurred the distinction between this run and the long-lived Artifact collection. Reopening the inventory with `B` could theoretically replace many upward wheel events, but it also introduces menu-toggle state, tab-selection coordinates, and another guarded key/click sequence. It has not been live benchmarked as a more reliable reset. ### Decision - Default the user-facing scan scope to the entire currently owned Artifact inventory. - Read and validate the current owned count at scan start. Use that `current` value as the hard target basis; never substitute the inventory capacity `total`. - If the current owned count is unavailable or invalid, block a full-inventory scan with a reviewable reason instead of guessing. - Offer one optional limit with either Artifacts or rows. Convert rows through the detected grid column count and clamp both forms to the owned count and the 2,400-Artifact safety bound. - Keep scope selection session-local so a new app launch returns to the full owned inventory default. - During a run, use the left main work surface for capture phase and captured-versus-target progress. Keep the independently advancing OCR/parse/ evaluation progress beside the live results on the right. Remove the duplicate standalone progress card. - Keep the right result rail current-session only and empty on app launch. Persisted history remains available in the Artifacts view. - Retain the bounded, foreground-guarded wheel-to-top reset as the production path. Treat `B` plus Artifact-tab reopening only as a future live A/B experiment. ### Consequences - The primary action matches the common collection-import intent without hiding a partial numeric default. - A row limit remains understandable while the helper still receives one bounded Artifact count. - Full-inventory start now depends on a trustworthy owned-count read and fails safely when that evidence is missing. - The Scanner separates ephemeral run feedback from the durable Artifact collection and has one clear progress surface. - The accepted 5/20/50/100 evidence validates the wheel reset and bounded native engine. Real packaged Settings-UI runs additionally validate Artifact limit 5, one detected row at 8/8, streaming overlap at 20/20, and the complete owned target at 2,211/2,211 over 70 pages. The complete corpus passes the current Review gate at 136/2,211 (6.15%), zero errors, and zero writes. A `B`-based reset remains only an unaccepted experiment. ## ADR-017: Stream Native Capture Jobs Into Bounded Evaluation Workers ### Status Accepted. Packaged 20-result and 2,211-result runs prove that evaluated result cards become visible while native capture is still running; deterministic service/UI tests cover ordering, deltas, reconciliation, and reduced motion. ### Context Native capture already appends and flushes one line per crop to `capture-jobs.jsonl`, but Electron previously opened that queue only after the helper had captured the complete target. Users therefore saw no results during long runs and then waited for a second, fully sequential phase. The right-side result rail also could not explain evaluation independently from capture. Starting the whole processor repeatedly on every poll would duplicate OCR, race durable files, and make ordering non-deterministic. Treating a temporary end-of-file as completion would lose jobs that the still-running producer adds later. ### Decision - Start one single-flight processor as soon as the active native run first exposes a non-empty `runDir`. - Tail complete JSONL records while the native producer is running. A temporary empty queue means `waiting`, not completion; a partial final line is retried. - Deduplicate jobs by sequence, run at most four OCR/parse/evaluation workers, and publish completed results in canonical capture-sequence order. - A result reaches the renderer only after extraction review gating and `roll-efficiency-v1` evaluation have produced its final safe status. - Keep capture and evaluation counters independent. The left workspace reports capture progress; the right result surface reports evaluated/target plus parsed, review, and error counts. - Load live results as monotonic sequence deltas and reconcile against the full durable result set before declaring a successful session complete. - Do not finish processing until capture is terminal and every observed job is drained. A user stop ends game input immediately but keeps already captured jobs available for evaluation; the final session outcome remains `stopped`. - Persist intermediate ordered `scan-results.json` snapshots so a renderer refresh or process boundary does not make completed work disappear. ### Consequences - Long scans provide useful, reviewable results before capture ends. - Capture speed is isolated from OCR latency while bounded concurrency prevents unbounded CPU/memory growth. - Single-flight processing, sequence deltas, and final reconciliation prevent duplicate cards, stale-run leakage, and false-complete summaries. - The current-session rail can keep all results in top-to-bottom capture order; newly mounted cards use motion only as an insertion cue and respect reduced motion. - Live acceptance must measure actual phase overlap, not infer streaming from a correct final result count. - A streaming end-to-end claim must use one shared wall clock from native start through final result reconciliation. Capture and processing durations are retained as separate diagnostics and must never be added when they overlap. ## ADR-018: Gate Build-Fit Behind A Versioned Evidence Contract Before Scoring ### Status Accepted. The contract, validator, native-result adapter, deterministic five- piece evidence verifier, four sourced profiles, safety tests, legacy production- path separation, and the separate read-only ranker are implemented. ADR-022 defines the visible ranking/expiry policy; the V1 assessments remain score-free. ### Context The repository already contained an early preset scorer that could emit character recommendations and builds from local Artifact records. Its weights, thresholds, inferred characters, and partial builds were useful as a prototype, but they had no versioned provenance or validated Build-Fit semantics. Hiding those suggestions in the Builds UI was not enough while real account snapshots still calculated them internally. Roll-Efficiency, OCR confidence, and character/build suitability are different concepts. A high legal-roll score does not prove that a Set, Main Stat, or Substat mix fits a target, while low or Review extraction data must never enter downstream fit logic. ### Decision - Introduce `build-fit-contract-v1`, `build-fit-input-v1`, and `build-fit-assessment-v1` as separate shared contracts. - Require stable identity/revision, game-versioned provenance, explained legal Set plans, variable-slot Main-Stat priorities, relative Substat weights, optional full-build aggregate targets, and explicit conflict policies. - Enforce V1 safety floors of 80% overall extraction confidence and 70% on all required identity/value fields. Contracts may be stricter but cannot weaken those floors. - Require final parsed/non-Review data and a canonical IK Set key. Missing or uncertain evidence produces reason-coded blockers. - Map safe facts and conflicts into structured evidence. Verify complete five-piece slot/Set shape and sourced aggregate context without ranking it, and keep V1 output at `score: null` plus `recommendationStatus: deferred` until a sourced profile corpus and ranking formula are validated. - Record Roll-Efficiency as separate context and never fold it into Build-Fit eligibility or imply that it is a fit score. - Stop running real local account snapshots through the legacy scorer. Keep that implementation limited to explicit demo/test fixtures. ### Consequences - Sourced profiles and ranking fixtures can evolve on the combination verifier without changing the scanner/OCR authority boundary. - Review, low-confidence, incomplete, or non-canonical data fails closed before any character recommendation. - The Builds page can show source-bound evidence candidates while remaining explicit about scope, uncertainty, and missing non-Artifact context. - No user-facing fit score exists yet; `eligible` means only safe input for a future ranker. - Complete-build aggregate targets stay `pending` unless an explicit non- Artifact contribution references known contract sources; no character, weapon, or team baseline is guessed. - The detailed contract and next gates are documented in [BUILD_FIT_CONTRACT_V1.md](BUILD_FIT_CONTRACT_V1.md). ## ADR-019: Repair Five-Star +0 Main Values Only From Structural Rarity Evidence ### Status Accepted. The rule is implemented in the OCR parser, covered by positive and ambiguity-preserving tests, and validated against the complete 2,211-crop corpus. ### Context The first full-inventory parser pass routed 490 results to Review. Of the 388 low-confidence Main-Value cases, 360 were level 0 and already had four parsed Substats. A lower-rarity Artifact cannot begin at level 0 with four Substats, so that combination is structural evidence for five-star rarity. Without that evidence, a +0 Main Value can be valid at several rarities and must stay reviewable. ### Decision - Derive the canonical five-star +0 Main Value only when `level === 0` and exactly four Substats were parsed. - Mark the repaired Main Value with explicit parser confidence; do not silently generalize the rule to three, two, or incomplete Substats. - Keep all ambiguous rarity cases in Review and retain reason-coded analysis of the saved corpus. ### Consequences - Current-package reprocessing reduced Review from 490 to 136 without reclassifying any previously clean result into Review. - 360 Main Values were repaired from structural evidence; ambiguous three- Substat +0 Artifacts remain visible for review. - Future rarity extraction can replace this inference, but it must preserve the same fail-closed behavior when rarity evidence is uncertain. ## ADR-020: Target Only Exact Supported Genshin Process Names ### Status Accepted. Both the native helper and PowerShell fallback use the exact whitelist; the final packaged no-Genshin safety smoke passed with no source and no errors. ### Context A broad process-name match containing `Genshin` allowed the assistant process itself to look like a game target after the real client closed. Reusing a cached HWND without validating its owning process could preserve the same unsafe state. ### Decision - Recognize only `GenshinImpact` and `YuanShen` as supported game processes. - Revalidate every cached HWND against the exact whitelist before returning it; clear invalid cache state immediately. - Apply the same behavior to the PowerShell fallback and keep the allow-list covered by helper/static tests. ### Consequences - Closing Genshin produces `genshinFound=false`, HWND `0`, an empty target process, and no capture source even while the assistant remains open. - New official client process names require an explicit reviewed allow-list change instead of being picked up accidentally. ## ADR-021: Require Explicit Profile Assumptions And Source-Bound Aggregate Context ### Status Accepted. The contract requires target assumptions and source-bound aggregate context; the profile corpus, expiry gate, and Golden/adversarial context tests are implemented. ### Context Character recommendations vary with constellation, weapon, teammates, rotation, and game version. A single ER threshold or Main-Stat order without those assumptions would look authoritative while silently applying the wrong build context. Artifact contributions alone also cannot prove a total-build stat such as Energy Recharge because character and weapon values are outside the scan. ### Decision - Require every V1 target to list non-empty, unique assumptions alongside its role and description. - Store curated candidates in a versioned corpus with game version, review time, source version, and direct reference. - Start narrowly with Furina off-field/Solo Hydro C0-C1 from the KQM Furina Quick Guide labeled `Luna II`; do not generalize its 200%+ ER requirement to other Hydro counts, C2+, no-Burst teams, weapons, or rotations. - Treat Substat weights as documented ordinal source order only, never as damage multipliers or a ranking formula. - Evaluate a full-build aggregate target only when a complete non-Artifact value references an allowed curated/user/imported source. Otherwise keep it `pending`. - Cross-check profile character and Set keys against vendored IK 6.7.0 data. ### Consequences - Profiles remain auditable without turning hidden team/weapon assumptions into Artifact-derived facts. - Team, weapon, Favonius, and rotation inputs cannot be smuggled in as hidden constants; callers must provide them explicitly. - Profile expansion now carries a clear maintenance cost: source review, version update, catalog validation, Golden fixtures, and adversarial safety cases. ## ADR-022: Rank Only Source-Bound Build-Fit Evidence With Profile Expiry ### Status Accepted. `build-fit-evidence-ranking-v1`, four profile fixtures, explicit source expiry, session-only aggregate context, and the read-only Builds surface are implemented and offline-tested. Packaged renderer acceptance remains open. ### Context The score-free V1 verifier can prove that five scanned Artifacts are structurally safe and satisfy a profile's facts, but it intentionally cannot emit a damage score. Leaving the Builds page locked forever would strand the validated evidence, while turning Roll-Efficiency, raw OCR confidence, or arbitrary substat weights into a fit score would overstate what the data proves. Guide advice also changes with game versions. A version label and review date alone do not prevent an old profile from silently continuing to rank an account. Finally, native results are the only renderer-visible data source that preserves both canonical IK Set IDs and per-field confidence; store records do not. ### Decision - Keep `build-fit-assessment-v1` and `build-fit-combination-assessment-v1` score-free (`score: null`). - Add `build-fit-evidence-ranking-v1` as a separate, bounded, deterministic, read-only ranker. It considers only complete eligible five-piece combinations and returns at most three non-overlapping profile suggestions. - Make the visible `evidenceCoverage` a product-rule coverage measure: sourced Set-plan tier, variable Main-Stat priority, and ordinal Substat presence. It is not a DPS, optimizer, Roll-Efficiency, or OCR-confidence score. - Treat OCR confidence strictly as a hard gate. Do not use roll values, roll efficiency, character ownership, team, weapon, rotation, or unrecorded constants as ranking inputs. - Require `provenance.validUntil` on every profile. Expired profiles fail closed until a maintainer refreshes their direct source review. - Permit required aggregate targets only from explicit, user-confirmed, session-only non-Artifact context that references a declared local/user source. Missing context keeps the profile deferred; it is never guessed or persisted. - Read Builds data only from the newest complete native result run. The Builds surface does not scan, mutate review/store data, or send game input. ### Consequences - The app can expose useful, inspectable profile candidates without pretending that it is a complete optimizer. - Users see why a candidate ranked and why a profile is deferred, including missing full-build context or conflicts. - Profile maintenance has an explicit renewal deadline and source fixture cost. - A renderer/package acceptance is still required before this UI is called live-accepted; scanner live evidence is not reused as UI evidence. ## ADR-023: Visuelle Seltenheit fail-closed und Nicht-5★-Ansicht ohne Spielaktion ### Status Accepted ### Context Die bisherige Roll-Efficiency-Auswertung kennt rarity-spezifische Regeln, hatte bei neuen nativen Kartencrops aber keine direkte, persistierte Evidenz für die sichtbare Sternreihe. Eine aus Level, Main-Value oder Substats abgeleitete Seltenheit ist für einzelne etablierte Legacy-Regeln nützlich, darf aber eine fehlende oder widersprüchliche aktuelle Sternreihe nicht stillschweigend in ein 5★-Ergebnis verwandeln. Gleichzeitig sind explizit erkannte 1–4★-Artefakte für die Sammlung und manuelle Sichtung relevant. Daraus folgt jedoch keine sichere Bergungsentscheidung: Sperr-/Ausgerüstet-Status, Crop, Detaildaten und die Spieloberfläche müssen weiterhin durch den Nutzer geprüft werden. Automatische Bergung oder Quick Select wäre eine irreversible Spielaktion und widerspricht dem Scanner-Sicherheitsrahmen aus ADR-004, ADR-010 und ADR-013. ### Decision - Aktuelle native Capture-Jobs schreiben die direkte visuelle Stern-Evidenz als `starCount`, `starConfidence` und `starSource` in den dauerhaften Ergebnispfad. - Nur explizit bestätigte 5★-Evidenz darf in `roll-efficiency-v1` eingehen. Explizit bestätigte 1–4★-Ergebnisse erhalten `valueStatus: "excluded"` und keinen Wert, keine Projektion und keine Store-Promotion. - Wenn visuelle Stern-Evidenz vorliegt, aber `starCount` fehlt, ungültig oder widersprüchlich ist, wird nicht auf 5★ geraten. Das Ergebnis bleibt `Review` und seine Evidenz bleibt sichtbar. - Legacy-Ergebnisse ohne das neue visuelle Sternfeld werden durch diese Entscheidung nicht rückwirkend als visueller Beweis ausgegeben; für sie gelten bis zum Rescan die bestehenden strukturellen Regeln. - Die Inventory-Ansicht bietet einen separaten schreibgeschützten Filter `Nicht 5★` nur für explizit erkannte 1–4★-Ergebnisse. Er zeigt Crop/Bild und Detaildaten zur manuellen Prüfung, nicht die Behauptung "sicher bergbar". - Es gibt weder eine Scan-Checkbox noch einen Ablauf für automatische Bergung oder Quick Select. Diese Aktion bleibt verboten und aufgeschoben; ein zukünftiger Vorschlag bräuchte eine neue explizite Sicherheits- und Produktentscheidung und dürfte den bestehenden Nicht-Mutationsrahmen nicht umgehen. ### Consequences - Nicht-5★-Artefakte bleiben als vollständige, nachvollziehbare Scan-Ergebnisse sichtbar, ohne die Wertung oder Empfehlungen zu verzerren. - Die UI trennt klar zwischen `excluded` (direkt erkannte 1–4★) und `Review` (unsichere Evidenz), statt beide als schwache 5★-Artefakte darzustellen. - Replay-, Detail- und Promotion-Pfade müssen Stern-Evidenz, Ausschluss und Review deterministisch erhalten. - Der einzige zulässige nächste Schritt für einen Nutzer in der `Nicht 5★`- Ansicht ist die manuelle Prüfung im Spiel; die App sendet dafür keine Bergungs-, Auswahl- oder sonstige mutierende Eingabe. ## ADR-024: Default Renderer Copy To English While Preserving The English Scanner Boundary ### Status Accepted ### Context The application previously exposed a largely German renderer while its only validated OCR/crop profile is the English Genshin UI. Treating the two language concerns as one setting would either make a German-speaking user unable to choose their application language or incorrectly imply that German Genshin OCR is supported. ### Decision - Make English the default renderer locale for a fresh, missing, or invalid local preference. - Offer German as a user-selectable Settings preference and persist it locally under one application-scoped key. - Keep message catalogs structurally aligned and route renderer copy through the locale provider rather than hard-coded feature strings. - Keep the scanner contract explicit: changing the application language changes application copy only; Smart Capture remains validated for English Genshin. - Preserve stable semantic UI hooks for renderer acceptance so tests do not depend on a particular translated label. ### Consequences - New users receive an English interface by default while German remains available without a restart. - The document language and accessible renderer labels track the selected locale. - Future Genshin OCR-language support requires separate crop, parser, corpus, and live-acceptance evidence; it cannot be inferred from this setting. ## ADR-025: Make Artifact Deletion An App-Local Tombstone Workflow ### Status Accepted ### Context A wrong OCR read or wrong screenshot needs a way to disappear from the local Artifact inventory. Directly mutating the game's inventory is prohibited, and rewriting native `scan-results.json` would destroy the raw evidence needed for replay, debugging, and corpus review. A delete control also needs to prevent an active scan from racing a local cleanup. ### Decision - Expose a confirmed local-delete control only in artifact detail. - For native results, append an immutable entry to `deleted-results.jsonl` and filter it at load time instead of rewriting `scan-results.json`. - Delete a crop only after its resolved path is proved to be a PNG contained by the selected native run directory. - Block deletion while the target native run or its processor is active; make a repeat deletion idempotent. - Allow Store deletion only for one exact local record: either a Store-only row or an explicitly selected record linked to the deleted native result. - Keep the whole workflow behind main-process IPC. It must never focus Genshin, call the input helper, or dispatch a game action. ### Consequences - Users can correct local inventory mistakes without jeopardizing the game or losing raw scan evidence. - Tombstones become part of the local run contract and loaders/replay tooling must apply them deliberately. - The UI must communicate that the action removes app-local data only, and confirmation remains mandatory because local data and screenshots can be removed. ## ADR-026: Persist First-Observed Native Run Timing On One Shared Clock ### Status Accepted ### Context Native capture and downstream OCR/parse work overlap. Earlier reports could show phase rates but did not retain one durable, validated endpoint-to-endpoint clock for the current run. Adding capture and processing durations would double count their overlap and produce a misleading end-to-end claim. ### Decision - Write atomic `run-timing.json` records using `native-scanner-run-timing-v1` in each native run directory. - Record the first valid observation of request start, native scanner start, capture start/end, processing start/end, durable result write, and final result reconciliation. Later polling or retries cannot replace a marker. - Derive request-to-durable and request-to-reconciled milliseconds directly from their timestamps and validate marker ordering. - Require a complete valid timing record and final count reconciliation for a live streaming/end-to-end claim. ### Consequences - Capture, processing, and end-to-end rate can be reported as distinct metrics without double counting overlap. - A historical timing observation cannot stand in for current-build live evidence. - Timing files are durable diagnostic evidence and must be included in native smoke validation, not merely displayed in the renderer.