640 lines
31 KiB
Markdown
640 lines
31 KiB
Markdown
# Automation Live Scan Runbook
|
|
|
|
This document is the durable reference for automatic artifact scanning, mouse
|
|
movement, click input, elevation, and live validation status.
|
|
|
|
## Current Known-Good State
|
|
|
|
Validated live with Genshin open in the artifact inventory at 1920x1080,
|
|
English UI:
|
|
|
|
- `npm run dev:admin` starts the app elevated after the user confirms UAC.
|
|
- Runtime status reported `isElevated: true`, `genshinFound: true`, and
|
|
`targetProcess: "GenshinImpact"`.
|
|
- The safe probe endpoint `/automation/probe-click?index=1` focused Genshin,
|
|
moved the cursor to the second visible inventory tile, clicked it, and changed
|
|
the artifact detail panel fingerprint.
|
|
- Probe result: `clicked: true`, `inputBlocked: false`,
|
|
`foregroundProcess: "GenshinImpact"`, and `changed: true`.
|
|
- A bounded live auto-scan via `/scanner/start?limit=2` completed with:
|
|
`clicked: 2`, `attempted: 2`, `verified: 2`, `parsed: 2`, `stored: 2`,
|
|
`review: 2`, `misses: 0`, `status: "done"`.
|
|
- On 2026-07-08, a visible-inventory 50-artifact run completed with `50/50`
|
|
parsed and stored, `0` review, `0` duplicates, and `0` misses. Throughput
|
|
was still slow at `61765 ms` elapsed (`1235 ms/artifact`).
|
|
- On 2026-07-09, the current-engine visible-inventory path completed
|
|
`/scanner/start?entry=visible-inventory&limit=20&engine=current` with
|
|
`20/20` verified and parsed, `19` stored, `1` duplicate, `0` review, and
|
|
`0` misses in `8047 ms` elapsed (`402 ms/artifact`). A same-session artifact
|
|
detail capture also persisted an equipped footer as `equipped: "Citlali"` and
|
|
an unlocked grey lock as `locked: false`.
|
|
|
|
This proves that the current elevated app plus helper path can deliver mouse
|
|
movement and click input to the focused Genshin client in this environment.
|
|
|
|
Latest-source timing is not proven while `/health.appBuild.signature` differs
|
|
from the `APP_RUNTIME_SIGNATURE` in `electron/main.ts`, or after source changes
|
|
that have not been loaded by a fresh elevated runtime. Restart the elevated app
|
|
through `npm run dev:admin` and confirm UAC before collecting new 50/100
|
|
artifact evidence.
|
|
|
|
## Elevation And UAC
|
|
|
|
Use:
|
|
|
|
```powershell
|
|
npm run dev:admin
|
|
```
|
|
|
|
The command runs `scripts/dev-admin.ps1`, which launches a new elevated
|
|
PowerShell window running `scripts/dev-admin-start.ps1`. The elevated start is
|
|
logged to:
|
|
|
|
```text
|
|
outputs/admin-start/admin-dev.log
|
|
```
|
|
|
|
The user must confirm the Windows UAC prompt. The app cannot and must not click
|
|
the Secure Desktop UAC prompt for itself. After confirmation, the app can verify
|
|
its own runtime through the dev status endpoint.
|
|
|
|
Useful checks:
|
|
|
|
```powershell
|
|
Invoke-RestMethod http://127.0.0.1:17317/health
|
|
Invoke-RestMethod http://127.0.0.1:17317/scanner/status
|
|
```
|
|
|
|
Expected runtime facts before automatic scan:
|
|
|
|
- `isElevated: true`
|
|
- `genshinFound: true`
|
|
- `targetProcess: "GenshinImpact"`
|
|
- hotkeys registered
|
|
|
|
## Mouse And Click Validation
|
|
|
|
Use the probe before broad auto-scan work:
|
|
|
|
```powershell
|
|
Invoke-RestMethod "http://127.0.0.1:17317/automation/probe-click?index=1" |
|
|
ConvertTo-Json -Depth 12
|
|
```
|
|
|
|
The probe performs one read-only inventory selection click. It does not delete,
|
|
feed, enhance, lock, unlock, spend, or modify game resources.
|
|
|
|
Interpretation:
|
|
|
|
- `click.ok: true`, `clicked: true`, `inputBlocked: false` means Windows did not
|
|
block SendInput/UIPI in the current configuration.
|
|
- `focused: true` and `foregroundProcess: "GenshinImpact"` means the click was
|
|
sent while Genshin was foreground.
|
|
- `changed: true` means the detail panel changed after the click.
|
|
- `changed: false` can be benign if the target tile was already selected or two
|
|
neighboring artifacts render identically; retry with another `index`, `row`,
|
|
or `col`.
|
|
|
|
Examples:
|
|
|
|
```powershell
|
|
# Second visible tile
|
|
Invoke-RestMethod "http://127.0.0.1:17317/automation/probe-click?index=1"
|
|
|
|
# Specific grid cell
|
|
Invoke-RestMethod "http://127.0.0.1:17317/automation/probe-click?row=0&col=3"
|
|
```
|
|
|
|
## Bounded Live Auto-Scan
|
|
|
|
For live validation, prefer a bounded scan first:
|
|
|
|
```powershell
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/start?entry=visible-inventory&limit=2"
|
|
```
|
|
|
|
The visible-inventory path is the merge-relevant safe path. It requires the
|
|
Artifact inventory to already be open with a visible artifact detail card.
|
|
|
|
The normal Auto-Scan button uses a guided start. It first takes one lightweight
|
|
preflight capture without OCR, full-frame payload, review scoring, or storing.
|
|
If an artifact detail card is already visible, it starts the visible-inventory
|
|
scan. Otherwise it blocks with an operator-facing status and asks the user to
|
|
open the Artifact inventory with a visible detail card. OCR/review/store work
|
|
starts only after the artifact-detail preflight passes.
|
|
|
|
The explicit Dev-Control entry modes below remain available for targeted
|
|
experiments only. They send read-only navigation, but they are not the
|
|
merge-ready default because live testing showed that `auto-entry` can leave the
|
|
app in the Paimon menu when the starting state is not what the choreography
|
|
expects.
|
|
|
|
```powershell
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/start?entry=paimon-menu&limit=2"
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/start?entry=auto-entry&limit=2"
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/start?entry=visible-inventory&limit=2&engine=ik-traineddata"
|
|
```
|
|
|
|
Those paths send only read-only navigation. `ESC` is not a universal "go to
|
|
world" command: from the world it opens the Paimon menu, while from the
|
|
already-open Paimon menu it returns to the world. This is why the normal
|
|
`auto-entry` path first tries `B` directly and uses the IK-style `ESC -> B`
|
|
fallback only when direct entry did not reach an artifact detail card.
|
|
|
|
The scan starts only after a valid lookup package, supported 16:9 layout,
|
|
detected artifact grid, Genshin-client capture, and visual artifact-detail
|
|
markers are all present. If any preflight check fails, keep using the
|
|
visible-inventory path while tuning the entry step.
|
|
|
|
The visual preflight also classifies the Paimon menu. The Paimon profile/card
|
|
grid can look like an inventory grid if only fixed 16:9 coordinates are used, so
|
|
the scanner must reject `paimonMenu.present` before any artifact OCR, review
|
|
sample creation, store write, or grid scan starts. The guided entry may still
|
|
take lightweight skip-OCR captures while navigating, but those captures are only
|
|
state evidence.
|
|
|
|
The same guard also runs inside the scan loop. If the app is on the main game
|
|
screen, a Paimon/menu screen, a generic primary-screen capture, or any screen
|
|
without an artifact detail card, auto-scan must block instead of clicking tiles
|
|
or trying OCR.
|
|
After each click the loop now performs one fast artifact capture/OCR pass and
|
|
uses that capture's detail fingerprint to verify that the selected artifact
|
|
changed. This removes the old separate card-ready capture from the hot path. If
|
|
the detail fingerprint is unchanged, the loop retries once and then follows the
|
|
normal miss/block guards.
|
|
The outer scan start focuses Genshin once; hot-loop fingerprint/OCR captures do
|
|
not re-run the focus helper before every tile, which avoids an OS focus ping on
|
|
each artifact while still relying on click readback, foreground checks, and the
|
|
detail-card guard for safety.
|
|
After a scroll, the loop now uses the same cheap fingerprint polling model for
|
|
the inventory pane: it proceeds as soon as the next page fingerprint changed and
|
|
stabilized instead of always sleeping the old fixed 760 ms settle delay. Changed
|
|
but still animated inventory pages may proceed after 100 ms, again matching IK's
|
|
fast-scroll wait while still blocking unchanged pages.
|
|
|
|
Then poll:
|
|
|
|
```powershell
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/status" |
|
|
ConvertTo-Json -Depth 12
|
|
```
|
|
|
|
Before live timing, verify that the endpoint is the current app instance:
|
|
|
|
```powershell
|
|
Invoke-RestMethod "http://127.0.0.1:17317/health" |
|
|
ConvertTo-Json -Depth 6
|
|
```
|
|
|
|
The response must include `appBuild.signature` and
|
|
`appBuild.expectedOcrWorkerPoolSize`. If `appBuild` is missing, or
|
|
`/scanner/status` still reports the old OCR warmup start time, the local port is
|
|
still owned by a stale elevated Electron process. Close the old Administrator
|
|
window/app and restart with `npm run dev:admin` before running scanner probes.
|
|
|
|
Use the status `stats` timing fields for IK comparisons: `elapsedMs`,
|
|
`activeScanMs`, `writeFlushMs`, `averageMsPerParsed`,
|
|
`activeAverageMsPerParsed`, `averageCaptureMs`,
|
|
`averageCaptureRoundTripMs`, `averageCaptureRoundTripOverheadMs`,
|
|
`averageOcrMs`, `artifactsPerMinute`, and `projectedMsFor100`.
|
|
`elapsedMs` is end-to-end including queued writes; `activeScanMs` is the
|
|
click/capture/OCR loop before the final store/review flush. A run only counts as speed
|
|
evidence when `parsed`, `stored`, `review`, `duplicates`, and `misses` are read
|
|
together; raw click count alone is not scanner throughput. If `averageOcrMs`
|
|
dominates `averageMsPerParsed`, the next speed lever is an IK-style OCR worker
|
|
queue. If `averageCaptureRoundTripOverheadMs` is high, native capture encode,
|
|
Base64 transport, Electron image decode, or IPC/render scheduling is the next
|
|
bottleneck. The current 3 artifacts/second target requires `averageMsPerParsed`
|
|
at or below `333 ms` on a clean 20-artifact iteration.
|
|
|
|
Latest live timing evidence on 2026-07-08:
|
|
|
|
- Probe: `/automation/probe-click?index=1` returned `clicked: true`,
|
|
`inputBlocked: false`, `changed: true`, and `captureTarget:
|
|
"genshin-client"`.
|
|
- Baseline after helper/hot-loop cleanup:
|
|
`/scanner/start?entry=visible-inventory&limit=50&engine=current` completed
|
|
`50/50` parsed and stored with `0` review, `0` duplicates, `0` misses,
|
|
`2` pages, `elapsedMs: 61765`, `averageMsPerParsed: 1235`,
|
|
`averageCaptureMs: 186`, `averageOcrMs: 162`, and
|
|
`averageScrollReadyMs: 844`.
|
|
- Deferred-write experiment:
|
|
the same 50-artifact run completed `50/50` with `0` misses but regressed to
|
|
`elapsedMs: 63616` because 50 single-record writes produced
|
|
`writeFlushMs: 8163`.
|
|
- Current source replaces that experiment with batch persist and quiet
|
|
auto-scan UI captures. Later direct-GDI live runs validated the batch/quiet
|
|
path at limits 20, 45, and 100 with 0 misses in the current environment.
|
|
- Direct GDI hot-path validation:
|
|
after skipping `desktopCapturer.getSources()` in auto-scan artifact captures,
|
|
the 20-artifact iteration baseline improved to `20/20` parsed, `19` stored,
|
|
`0` review, `1` duplicate, `0` misses, `7966 ms` elapsed,
|
|
`398 ms/artifact`, `averageCaptureMs: 193`, `averageOcrMs: 167`,
|
|
`averageClickMs: 2`, and `writeFlushMs: 4`. This is roughly
|
|
`2.5 artifacts/second` on the first visible page.
|
|
- Scroll-path validation with the same direct GDI hot path:
|
|
`/scanner/start?entry=visible-inventory&limit=45&engine=current` completed
|
|
`45/45` parsed, `42` stored, `0` review, `3` duplicates, `0` misses,
|
|
`2` pages, `18625 ms` elapsed, `414 ms/artifact`, `averageCaptureMs: 187`,
|
|
`averageOcrMs: 162`, and one scroll readiness wait of `173 ms`.
|
|
- 100-artifact direct-GDI validation:
|
|
`/scanner/start?entry=visible-inventory&limit=100&engine=current` completed
|
|
on runtime signature `2026-07-08-direct-gdi-hotpath` with `100/100` parsed,
|
|
`97` stored, `0` review, `3` duplicates, `0` misses, `4` pages,
|
|
`42064 ms` elapsed, `421 ms/artifact`, `averageCaptureMs: 179`,
|
|
`averageOcrMs: 154`, `averageClickMs: 2`, `writeFlushMs: 6`, and `3`
|
|
scroll readiness waits averaging `176 ms`.
|
|
- OCR/parser eval after this speed pass: `npm run eval` passed with `23/23`
|
|
exact-match cases, `100%` field accuracy, and `100%` critical fields. This is
|
|
a regression gate, not a substitute for manually checking live artifact values.
|
|
- Ownership and lock proof on 2026-07-09:
|
|
`/scanner/start?entry=visible-inventory&limit=20&engine=current` completed
|
|
`20/20` verified and parsed, `19` stored, `1` duplicate, `0` review, and
|
|
`0` misses in `8047 ms`. Smart Capture parsed equipped footers for `Citlali`
|
|
and `Linnea`, reported an unlocked artifact as `locked: false`, then reported
|
|
a visibly locked artifact as `locked: true` with `lockSignal.ratio:
|
|
0.14797913950456323` over threshold `0.06`. A follow-up bounded scan
|
|
persisted that locked artifact with `equipped: "Citlali"` and `locked: true`.
|
|
- 3 artifacts/second preparation:
|
|
auto-scan artifact captures now also omit the detail-preview payload and
|
|
expose `averageCaptureRoundTripMs` plus
|
|
`averageCaptureRoundTripOverheadMs`. The first live run exposed a false
|
|
`missing-crops-or-ocr` review trigger because the hot path intentionally omits
|
|
`detailDataUrl`; this is fixed in `getAutoReviewReason`.
|
|
- 3 artifacts/second live attempts:
|
|
after the review fix, a clean `limit=20` run completed `20/20` parsed,
|
|
`19` stored, `0` review, `1` duplicate, `0` misses, `7285 ms` elapsed,
|
|
or `364 ms/artifact` (`2.75 artifacts/second`). The stable final run on
|
|
signature `2026-07-08-direct-gdi-reviewfix` completed `20/20`, `18` stored,
|
|
`0` review, `2` duplicates, `0` misses, `7973 ms` elapsed, or
|
|
`399 ms/artifact`. 3 artifacts/second is not proven.
|
|
- Follow-up 3 artifacts/second attempts on 2026-07-09:
|
|
after the distinctive partial piece parser fix, the best clean repeatability
|
|
run reached `336 ms/artifact` with `20/20` parsed, `0` review, `0` misses,
|
|
`318 ms` average capture roundtrip, and `138 ms` roundtrip overhead. Later
|
|
runs with crop priority and image-payload cleanup stayed clean but ranged
|
|
around `346-351 ms/artifact`; the strict `333 ms/artifact` budget remains
|
|
unproven.
|
|
- Rejected speed experiments:
|
|
detail-region capture, `GAA_OCR_WORKERS=5`, DataURL-to-buffer decode, and
|
|
substat OCR `PSM.SINGLE_COLUMN` were all live/benchmark tested and were slower
|
|
than the direct-GDI baseline. Later checks also rejected skipping
|
|
`analyzePaimonMenu`, skipping lock-state as a production shortcut, reducing
|
|
the artifact-level crop scale, and `GAA_OCR_WORKERS=6` as the default.
|
|
Keep `GAA_OCR_WORKERS=4` for current runs.
|
|
- Quality-gated current-vs-IK comparison:
|
|
`npm run scan:goal:compare:validated` produced
|
|
`outputs/live-soak/2026-07-08T18-38-35/scan-performance-assessment.json`
|
|
with `createdAt: 2026-07-08T18:41:11.6120957+02:00`.
|
|
The final validator summary passed at `limit=100` with winner `current`,
|
|
`activeAvg: 378 ms/artifact`, `projected100: 37800 ms`, `missRate: 0`, and
|
|
`reviewRate: 0`. The `current` 100-artifact run parsed `100/100`, stored `97`,
|
|
had `0` review, `0` misses, and crossed `4` pages. The `ik-traineddata`
|
|
100-artifact run parsed `97/100`, had `5` review and `3` misses, and was not
|
|
qualified because it parsed fewer artifacts than requested.
|
|
|
|
The `/scanner/start?limit=N` endpoint sends a renderer command payload with a
|
|
temporary scan limit. It does not change the normal UI setting. The normal
|
|
hotkeys and buttons still use the UI's configured scan limit.
|
|
|
|
Lookup and benchmark utility endpoints:
|
|
|
|
```powershell
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/lookup/status"
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/lookup/regenerate"
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/ocr/warmup"
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/ocr/warmup?engine=ik-traineddata"
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/benchmark-ocr?limit=5"
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/benchmark-ocr?limit=5&engine=ik-traineddata"
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/benchmark-ocr?limit=5&engine=compare"
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/benchmark-ocr?limit=5&profile=full"
|
|
```
|
|
|
|
The benchmark endpoint measures the current Tesseract.js engine and the
|
|
Inventory-Kamera-traineddata Tesseract.js path against the artifact crop set and
|
|
returns timing/field counts, min/p50/p90/max timing, OCR p50/p90 timing,
|
|
20/45/100-artifact projections, skipped-OCR count, and the active OCR worker
|
|
pool size. It also returns per-field OCR timings under
|
|
`ocrFieldAverages`, which is the first place to look before changing crop or
|
|
parser behavior. Individual captures also report whether the artifact was
|
|
detected as `sanctified`; level/substat crops are shifted in that state to match
|
|
Inventory Kamera's crop model. By default it uses the auto-scan `fast` OCR profile,
|
|
which omits the low-value set-effect crop and the main-stat-value crop that can
|
|
be derived from slot, main-stat label, and level. The slot crop remains enabled
|
|
in the fast profile because it improved live-read quality. The fast profile also uses
|
|
Inventory Kamera's tighter substat crop height; full/manual captures keep the
|
|
larger recovery crop for debugging difficult samples. Auto-scan also omits per-crop diagnostic Base64 images from hot-loop OCR
|
|
captures while keeping the detail screenshot, OCR text, crop rect metadata, and
|
|
timings. OCR crops are passed to Tesseract as PNG buffers internally, not as
|
|
Base64 DataURLs, to avoid encode/decode overhead in batch scans. When
|
|
`skipOcrUnlessArtifactDetail` blocks OCR because no artifact detail card is
|
|
visible, OCR crop preprocessing is skipped too. Auto-scan readiness and scroll
|
|
checks use native detail/inventory fingerprints and omit preview DataURLs in
|
|
poll captures. Fast preflight/poll captures also omit crop list construction, crop images, and lock-state
|
|
detection unless a caller explicitly overrides that option; add
|
|
`profile=full` to OCR every artifact detail crop for debugging. It uses the same
|
|
artifact-detail guard as auto-scan: if the current screen is not a confirmed artifact detail view, OCR is
|
|
skipped and the response shows `skippedOcrCaptures` instead of burning time on
|
|
invalid crops.
|
|
The fast auto-scan profile now keeps the optional Equipped footer OCR on real
|
|
artifact-read captures when the footer marker is visible, so stored artifacts
|
|
can record the equipped character without requiring a separate manual capture.
|
|
Name, level, main-stat label, footer, and substats remain in the OCR hot path;
|
|
slot, set, and main-stat value are derived when the lookup/parser can validate
|
|
them. Preflight and readiness poll captures still skip OCR/crops/lock-state
|
|
work because they only prove surface and fingerprint changes.
|
|
Local store/review writes are serialized through an internal queue but no longer
|
|
block the next inventory click. The scan still flushes the queue before it
|
|
returns its final summary, so `stored` and `review` counts remain final-state
|
|
numbers.
|
|
The app warms the default OCR worker pool in the background after startup; check
|
|
`/scanner/status` -> `ocrWarmup.current` before timing the first artifact. Use
|
|
`/scanner/ocr/warmup?engine=ik-traineddata` before comparing Inventory
|
|
Kamera-traineddata timings so the benchmark is not dominated by worker creation.
|
|
`engine=ik-traineddata` uses Inventory Kamera's local
|
|
`genshin_fast_09_04_21.traineddata` through Tesseract.js when the file is found
|
|
in `data/tessdata`, `IK_TESSDATA_DIR`, `work/Inventory_Kamera`, `work/refs`,
|
|
or the local `_ik_ref*` folders.
|
|
`engine=compare` runs `current` and `ik-traineddata` against the same visible
|
|
artifact detail state. The auto-scan default must stay `current` until the IK
|
|
traineddata path wins on the same captures. For a controlled live comparison,
|
|
start the scanner with `engine=ik-traineddata`; this only changes the OCR
|
|
worker language for that run and leaves the default UI/hotkey path on
|
|
`current`.
|
|
The OCR pool defaults to four workers because the fast artifact crop set has
|
|
four useful OCR parameter groups; set `GAA_OCR_WORKERS=1..8` before startup to
|
|
benchmark a different worker count. Inventory Kamera's native engine pool is
|
|
still the reference design, but the current app path remains Tesseract.js until
|
|
native OCR is integrated and measured. Crops are scheduled across the whole
|
|
worker pool and each worker caches its last Tesseract parameter profile; this is
|
|
closer to Inventory Kamera's multi-engine field OCR than the earlier
|
|
parameter-group-serial scheduler.
|
|
|
|
## Diagnostic Evidence
|
|
|
|
The Diagnose page contains a compact evidence timeline for scanner work. It logs
|
|
runtime pings, focus attempts, key presses, entry captures, artifact-tab clicks,
|
|
preflight failures, grid/count metadata, detail fingerprints, and detail/inventory
|
|
screenshots. The same last events are also published through:
|
|
|
|
```powershell
|
|
Invoke-RestMethod "http://127.0.0.1:17317/scanner/status" |
|
|
ConvertTo-Json -Depth 18
|
|
```
|
|
|
|
Use this before changing scanner behavior: run the smallest failing action, read
|
|
the evidence timeline, then decide whether the failure is focus/input, entry
|
|
navigation, grid detection, capture quality, OCR, or parser validation.
|
|
|
|
If Paimon entry shows `entry key ESC` or `entry key B` with `eventsSent: 0`, the
|
|
running `InputHelper.exe` probably predates keyboard support or is blocked. Stop
|
|
the elevated app/helper, run `npm run helper:build`, then restart with
|
|
`npm run dev:admin` so the app loads the rebuilt helper.
|
|
|
|
## Soak-Test Helper
|
|
|
|
After the elevated app is running and Genshin is open on the artifact inventory,
|
|
the non-elevated terminal can drive the local dev-control endpoints and save a
|
|
full evidence bundle:
|
|
|
|
```powershell
|
|
npm run scan:soak
|
|
```
|
|
|
|
The helper writes timestamped JSON snapshots and a transcript to:
|
|
|
|
```text
|
|
outputs/live-soak/<timestamp>/
|
|
```
|
|
|
|
Default sequence:
|
|
|
|
1. `/health`
|
|
2. `/scanner/status`
|
|
3. `/capture/smart?skipOcr=1`
|
|
4. `/automation/probe-click?index=1`
|
|
5. `/automation/probe-click?index=3`
|
|
6. `/scanner/start?entry=visible-inventory&limit=2`
|
|
7. `/scanner/start?entry=visible-inventory&limit=5`
|
|
8. `/scanner/start?entry=visible-inventory&limit=10`
|
|
9. `/scanner/start?entry=visible-inventory&limit=20`
|
|
10. `/review/samples?limit=30`
|
|
|
|
For the actual Inventory-Kamera speed target, use the explicit goal run after
|
|
`/health` shows the current `appBuild`:
|
|
|
|
```powershell
|
|
npm run scan:live:preflight
|
|
npm run scan:live:preflight:wait
|
|
npm run scan:goal
|
|
npm run scan:goal:current
|
|
npm run scan:goal:ik
|
|
npm run scan:iterate:compare:validated
|
|
npm run scan:iterate:compare:validated:wait
|
|
npm run scan:goal:compare
|
|
npm run scan:goal:compare:validated
|
|
npm run scan:goal:compare:validated:wait
|
|
```
|
|
|
|
`scan:live:preflight` checks `/health`, `/scanner/status`, the current
|
|
`APP_RUNTIME_SIGNATURE`, elevation, and whether Genshin is visible to the helper
|
|
before a long live scan is attempted.
|
|
Use `npm run scan:live:preflight:wait` during manual startup after `npm run
|
|
dev:admin`; it waits up to 120 seconds for the elevated dev-control server and
|
|
runtime checks to become ready. The non-waiting command remains the default for
|
|
validated scan chains so automation fails fast on a missing runtime.
|
|
|
|
Use `npm run scan:iterate:compare:validated` for fast iteration while tuning OCR,
|
|
parser, capture, or readiness behavior. It runs the same preflight, compares
|
|
`current` vs. `ik-traineddata` at `limit=20`, and validates the newest assessment
|
|
with `--limit=20 --summary`. This is the preferred loop while debugging because
|
|
it gives quality-gated feedback without waiting for the full `2, 5, 20, 45, 100`
|
|
goal sequence. Use `npm run scan:iterate:compare:validated:wait` directly after
|
|
UAC if the elevated runtime may still be starting.
|
|
|
|
The goal run first warms/benchmarks `current` vs. `ik-traineddata`, then scans
|
|
limits `2, 5, 20, 45, 100` with the selected scan engine, and writes
|
|
`scan-run-summary.json` plus `scan-run-summary.csv`. `npm run scan:goal`
|
|
uses the default `current` scan engine; use `scan:goal:ik` for a native
|
|
IK-traineddata scan pass. Use `scan:goal:compare` to run both scan engines
|
|
back-to-back with the same limits and one combined CSV. The CSV is the quickest evidence for
|
|
`averageMsPerParsed`, `activeAverageMsPerParsed`, `averageCaptureMs`,
|
|
`averageCaptureRoundTripMs`, `averageCaptureRoundTripOverheadMs`,
|
|
`captureP50Ms`, `captureP90Ms`, `averageOcrMs`, `ocrP50Ms`, `ocrP90Ms`,
|
|
`averageCardReadyMs`, `averageScrollReadyMs`, `artifactsPerMinute`, and
|
|
`projectedMsFor100`.
|
|
The run also writes `scan-performance-assessment.json`, which groups results by
|
|
limit, picks the best qualified engine, and labels the dominant bottleneck as
|
|
OCR, capture-roundtrip-overhead, capture, card-ready, or scroll-ready. A
|
|
qualified winner must finish the run, parse the requested count, keep miss rate
|
|
under 2%, and keep review rate at or below 15%; review and miss rates are
|
|
penalized before active average speed is used as the tie-breaker. For IK-target
|
|
claims, check `goal100Decision`; it
|
|
must read `qualified-comparison: winner=<engine>`, and
|
|
`goal100.comparisonComplete` must be `true` so a single-engine 100-artifact run
|
|
is not mistaken for a current-vs-IK comparison.
|
|
|
|
Validate the saved assessment before using it as final evidence:
|
|
|
|
```powershell
|
|
npm run scan:assessment:validate -- --latest
|
|
npm run scan:assessment:validate -- --input=<run-dir>\scan-performance-assessment.json
|
|
```
|
|
|
|
`--latest` searches `outputs/live-soak/` for the newest
|
|
`scan-performance-assessment.json`. Use explicit `--input` when comparing older
|
|
or archived runs. Add `--expect-winner=current` or
|
|
`--expect-winner=ik-traineddata` when validating a specific engine claim instead
|
|
of accepting any qualified winner. Add `--limit=20` for a short iteration run
|
|
instead of the final 100-artifact proof. Add `--summary` when you want a short
|
|
report-ready PASS/FAIL output that includes the input assessment path and
|
|
assessment `createdAt` timestamp.
|
|
|
|
Optional budget flags are useful for the current speed work:
|
|
|
|
```powershell
|
|
npm run scan:assessment:validate -- --latest --summary --limit=20 --max-active-average-ms=333 --max-capture-roundtrip-overhead-ms=120
|
|
```
|
|
|
|
`--max-active-average-ms=333` is the strict 3 artifacts/second check. Use a
|
|
separate `--max-capture-roundtrip-overhead-ms` budget when deciding whether the
|
|
next optimization belongs in native capture transport instead of OCR.
|
|
Single-engine repeatability runs may be validated with `--allow-single-engine`,
|
|
but that mode is only for repeatability evidence and must not be used for IK
|
|
parity claims.
|
|
|
|
`npm run scan:goal:compare:validated` is the preferred final command: it runs
|
|
the live preflight first, then the full comparison, and then validates the
|
|
newest assessment with `--summary`. Use
|
|
`npm run scan:goal:compare:validated:wait` for the same final flow when starting
|
|
immediately after UAC.
|
|
|
|
For later-session repeatability without changing OCR engines, use:
|
|
|
|
```powershell
|
|
npm run scan:repeatability:wait
|
|
```
|
|
|
|
That command runs the current visible-inventory engine at 20, 45, and 100
|
|
artifacts, then validates the 100-artifact result as single-engine evidence.
|
|
It uses the `live-soak.ps1 -RepeatabilityRun` switch instead of passing a
|
|
comma-separated `-Limits` value through npm/cmd, because Windows argument
|
|
parsing can collapse `20,45,100` into one unsafe number. The script also refuses
|
|
limits above 1800 as a final guard.
|
|
|
|
The assessment ranking can be verified without Genshin or the Electron app:
|
|
|
|
```powershell
|
|
npm run scan:assessment:test
|
|
```
|
|
|
|
This self-test rejects synthetic runs that are fast but have too many misses or
|
|
too many review samples, so the final IK comparison cannot be won by speed alone.
|
|
|
|
## Review-To-Eval Quality Loop
|
|
|
|
After any live scan that creates review samples, export candidates before adding
|
|
anything to the permanent eval corpus:
|
|
|
|
```powershell
|
|
npm run eval:review-candidates -- --limit=80
|
|
```
|
|
|
|
Read `outputs/review-eval-candidates/review-eval-candidates.md`. It is a review
|
|
worklist, not ground truth. Only after the expected fields are confirmed or
|
|
corrected against the real artifact should a case be moved into
|
|
`src/eval/corpus/confirmedReviewCorpus.ts`. This prevents the parser from
|
|
grading itself and keeps `npm run eval` meaningful. The exporter deduplicates
|
|
samples, puts complete modern OCR captures first, and marks missing fast-profile
|
|
fields so stale or partial captures are easier to ignore. Unconfirmed exporter
|
|
output must stay in `outputs/review-eval-candidates/`.
|
|
|
|
For a manually checked candidate, generate a paste-ready confirmed-case snippet:
|
|
|
|
```powershell
|
|
npm run eval:prepare-confirmed -- --candidate=<candidate-id> --expect-file=.\path\to\expect.json
|
|
```
|
|
|
|
The command requires explicit labels and writes only to the ignored outputs
|
|
folder. Review the snippet before adding it to
|
|
`src/eval/corpus/confirmedReviewCorpus.ts`.
|
|
|
|
For the current implementation summary and IK comparison rationale, see
|
|
[scanner-ik-progress-report.md](scanner-ik-progress-report.md).
|
|
|
|
Use the readiness timings to compare against Inventory Kamera's fixed waits:
|
|
IK waits about 200 ms after selecting the next inventory item and about 100 ms
|
|
after fast scrolls. If `averageCardReadyMs` or `averageScrollReadyMs` dominates
|
|
the active average while OCR is already low, tune the fingerprint gate before
|
|
touching OCR again.
|
|
|
|
The runner reads `APP_RUNTIME_SIGNATURE` from `electron/main.ts` and refuses
|
|
to run against a stale Electron process when `/health.appBuild.signature` does
|
|
not match the current source. Use `-AllowStaleBuild` only for deliberate
|
|
debugging of an older instance.
|
|
Current dev builds also expose `/dev/shutdown` on localhost. The start cleanup
|
|
script calls it before falling back to `Stop-Process`, so a previous elevated
|
|
app can shut itself down cleanly even when the caller cannot terminate an
|
|
administrator process directly. Older builds without that endpoint still need
|
|
manual close or a confirmed `npm run dev:admin` restart.
|
|
|
|
Review samples are saved as a compact summary by default so Vite does not try to
|
|
watch large Base64 payloads under `outputs/`. Full review payloads can be saved
|
|
with `-SaveFullReviewSamples` when needed.
|
|
|
|
It stops on a failed probe, blocked scan, stopped scan, or timeout unless
|
|
`-ContinueAfterBlocked` is supplied directly:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\live-soak.ps1 -Limits 2,5 -ContinueAfterBlocked
|
|
```
|
|
|
|
## Anti-Cheat And Safety Boundary
|
|
|
|
Do not describe the current implementation as bypassing anti-cheat. The app
|
|
does not read memory, hook the process, inject code, modify game files, inspect
|
|
packets, or interact with kernel drivers. It uses normal Windows screen capture,
|
|
focus, cursor movement, wheel, and click input.
|
|
|
|
The practical finding is narrower:
|
|
|
|
- A non-elevated app can be blocked by Windows integrity/UIPI when the target
|
|
process is elevated or protected.
|
|
- Running the app elevated fixed input delivery in the tested environment.
|
|
- Genshin's anti-cheat may still affect behavior on other machines, game modes,
|
|
overlays, or future versions. Re-run the probe before trusting broad scans.
|
|
|
|
Never add automation that deletes, feeds, enhances, locks/unlocks, spends
|
|
resources, reads memory, hooks, injects, or modifies Genshin.
|
|
|
|
## Live Layout Facts
|
|
|
|
The current 16:9 layout profile is calibrated from a 1920x1080 English
|
|
artifact-inventory capture:
|
|
|
|
- detail rect approximately `x=1308`, `y=120`, `width=492`, `height=838`
|
|
- inventory grid: `8 x 4` safe automated targets, matching Inventory Kamera's
|
|
32-artifact full-page model. The apparent lower fifth row is in the bottom
|
|
control band and is intentionally not clicked during auto-scan.
|
|
- first tile center: `x=179`, `y=254`, `row=0`, `col=0`
|
|
- second tile center: `x=325`, `y=254`, `row=0`, `col=1`
|
|
- inventory count crop successfully read `2059/2400` in the live session
|
|
|
|
The profile is resolution-scaled for 16:9. Off-profile setups should be treated
|
|
as higher risk and validated with Smart Capture plus the probe.
|
|
|
|
## Validation Checklist
|
|
|
|
Before marking an automation change done:
|
|
|
|
1. Run `npm run lint`.
|
|
2. Run `npx tsc -p tsconfig.electron.json` when Electron/preload/main changed.
|
|
3. Run `npm test`.
|
|
4. Run `npm run build`.
|
|
5. If Genshin is available, run `/automation/probe-click?index=1`.
|
|
6. For scan-loop changes, run
|
|
`/scanner/start?entry=visible-inventory&limit=2` before any broader scan.
|
|
7. Record new live findings in this file and in `docs/scanner-rework-status.md`.
|
|
8. For IK-target claims, attach or cite `scan-performance-assessment.json` from
|
|
a non-stale `npm run scan:goal:compare:validated` run.
|