Files

1056 lines
48 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 14★-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 14★-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 14★-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 14★) 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.