ba-auto-daily/plan.md
Nik Afiq 68a7ed2529 feat: cafe lowest-affection invite for room 2, exit_game AP-left-unspent alert, login window-flicker fix
- cafe.py: room 2 now invites the lowest-affection candidate instead of
  highest (room 1 unchanged), plus OCR'd invited-student name logging
- exit_game.py: alert (AP_ALERT: log tag, picked up by ba_cron_run.sh's
  Discord alerting) if AP is still unspent above threshold when the game
  closes
- login.py: re-check window_exists() after the stabilization wait to catch
  a startup window-flicker race that could crash a run
- config.py/mapping.md: supporting constants and reference-mapping updates

Also rewrites plan.md: retires the phase-by-phase changelog (now fully
superseded by CLAUDE.md's own documentation) down to forward-looking
backlog items, and adds a performance-improvement plan from a cron-log
time analysis (lesson.py's per-cell screenshot redundancy, event_sweep's
multi-call design).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 19:28:59 +09:00

101 lines
8.7 KiB
Markdown
Raw 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.

# ba-auto-daily implementation plan
Personal Blue Archive JP daily-automation project. `CLAUDE.md` is the authoritative, actively-maintained reference for architecture, deployment, conventions, and the incident history behind current design decisions — read it first. This file is intentionally scoped to **forward-looking work only**: the not-yet-implemented backlog and active improvement plans. It no longer carries a phase-by-phase changelog; that history (30+ phases of live-testing retrospectives) has been retired now that every feature it covered has shipped and CLAUDE.md's "Existing features" section carries the load-bearing lessons from it.
The reference implementation lives at `~/repo/baas-reference/` (read-only — study and adapt, never edit). See `ba_auto/reference_notes/mapping.md` for the current, maintained local-feature → reference-module mapping table.
## Currently implemented (for context, not a todo list)
`login`, `mailbox`, `cafe`, `stamina`, `gem_shop`, `circle`, `battle_pass`, `story_sweep`/`story_sweep_force`, `story_sweep_hard`/`story_sweep_hard_force`, `event_sweep`, `shop_common`, `shop_tactical`, `lesson`, `arena`, `bounty`, `scrimmage`, `exit_game` — see `ba_daily.py`'s `TASKS` dict for the exact CLI surface and `CLAUDE.md`'s "Existing features" section for behavior/known gaps.
## Backlog — not yet implemented
### Commissions (Base Defense + Item Retrieval)
Reference: `~/repo/baas-reference/module/clear_special_task_power.py`
Local target: `ba_auto/tasks/commission.py`
Two sub-dungeons. OCR: port the reference's approach if it uses OCR here — do not default to a fixed-target workaround just to avoid OCR (see `CLAUDE.md`'s OCR policy).
### Crafting
Reference: `~/repo/baas-reference/module/create.py`
Local target: `ba_auto/tasks/crafting.py`
Very complex — material selection, priority lists, rarity tiers, stepper/quantity UI, filtering/sorting, OCR-like decision points. Do not start until there's a concrete need; this is the most complex remaining reference module.
### Momo Talk
Reference: `~/repo/baas-reference/module/momo_talk.py`
Local target: `ba_auto/tasks/momo_talk.py`
Runs on a different cadence from daily reset, which is why it's still worth doing despite being low-value. Likely no OCR — mostly state scanning and click flow.
### Main story push (new-stage clearing, not sweeping)
Reference: `~/repo/baas-reference/module/main_story.py`, `~/repo/baas-reference/module/explore_tasks/explore_task.py`
Distinct from `story_sweep`/`story_sweep_hard` (which only re-sweep already-cleared stages). Low priority — full grid-mode support needs a lot of per-stage scripting; a simple auto-fight-only mode could be a smaller first cut. No reusable auto-fight primitive exists yet — `arena.py` implements its battle-commit/result-detection inline rather than as a shared module, so this would either need extracting that or building fresh.
### Group Story / Mini Story
Reference: `~/repo/baas-reference/module/group_story.py`, `~/repo/baas-reference/module/mini_story.py`
Convenience only, low priority.
### Event content beyond the AP sweep panel
`event_sweep.py` already covers the sweep panel for a currently-running event (see `CLAUDE.md`). Still not built: `explore_activity_story`/`explore_activity_mission`/`explore_activity_challenge` (walking the event's map / fighting stages manually — only needed for a stage that hasn't been SSS-cleared yet) and `exchange_reward` (the reward-exchange shop). Event-specific content expires, so any calibration work here (stage-count, row positions) is only valid for whichever event is running at the time.
### Daily Free Power
Reference: `~/repo/baas-reference/module/collect_daily_free_power.py`
Deliberately deferred, not merely unstarted — its entry point is a real-money purchase menu, and automating it needs explicit user confirmation before any implementation attempt.
## Skip list
| Feature | Why skip |
|---|---|
| Total Assault / Raid | Low value and risky to automate. Reference support may be limited/stubbed. |
| Joint Firing Drill | Not worth prioritizing for JP if reference has server-specific limitations. |
| De-clothes localization toggle | CN-only / irrelevant. |
| Restart / refresh-uiautomator2 | Android/ADB backend maintenance, not applicable to PC/Steam/Proton. |
| Auto-unfriend | Risky, low value, destructive. |
| Daily minigame dispatcher | Event-specific and unstable. Handle ad hoc only. |
## Performance improvement plan (opened 2026-08-14)
Prompted by a request to review real cron run logs (`scratchpad/ba_logs/{daily,q4h}.log`, pulled from nik-gpu, covering 3 `q4h` runs + 2 `daily` runs on 2026-08-14) for where automation time actually goes and what's worth speeding up. Timestamps come from the per-line logging added in the "timestamped log output" work (see git history / `CLAUDE.md`'s log-format notes).
### Measured totals
| Run | Total | Notes |
|---|---|---|
| q4h #1 (10:00) | 9m56s | |
| q4h #2 (13:00) | 8m45s | |
| q4h #3 (17:00) | 9m13s | |
| daily #1 (03:30) | 15m32s | |
| daily #2 (04:30) | **27m20s** | full run, 7 lesson tickets spent |
### Finding 1 (top priority, not yet implemented): `lesson.py` re-screenshots per cell instead of per region
In the 27m20s `daily` run, `lesson` alone took 9m50s (36% of the run), and ~7 minutes of that is the "scanning all regions" phase — 12 regions × ~30-38s each — that runs *before any ticket is spent*, just to build the priority queue.
Root cause, confirmed by reading the code: `_scan_open_grid_cells` (`ba_auto/tasks/lesson.py:196`) loops 3 rows × 3 cols × 3 slots = 27 cells per region, and `_read_slot_affection` (`lesson.py:185`) calls both `detector.region_contains_color` (checkmark check) and, if not done, `detector.read_int_on_heart_badge` (OCR). Both independently call `driver.read_screenshot()`, and `read_screenshot()` fires a **brand-new `scrot` capture every call** (`ba_auto/driver.py:97-127`) — there's no reuse of a prior frame. That means a single region scan can trigger up to ~50 fresh `scrot` processes against a screen that hasn't changed at all between them (no clicks happen mid-scan, per `_scan_all_regions`'s own docstring: "a pure read, spends no tickets").
**Proposed fix:** take one screenshot per region (or even once for the whole scan pass, if nothing changes on-screen until a click happens), decode it once, and pass the decoded image into the per-cell checkmark/OCR reads instead of each one re-capturing. This is a pure efficiency fix — the detection logic itself (color masking thresholds, heart-badge OCR contamination handling) doesn't change at all, so it doesn't touch anything CLAUDE.md's OCR policy cares about. Expected to cut the scan phase from ~7 minutes to well under a minute.
**Scope note:** `_run_queue`'s actual ticket-spending clicks (`lesson.py:306`) happen between scans, so only the *scan* phase's screenshots are provably safe to batch this way — anything read after a click still needs a fresh capture.
### Finding 2 (secondary, informational — do not touch without care): `event_sweep` is called 2-4x per run by design
`ba_daily.py`'s `daily` preset calls `event_sweep` four times, `q4h` twice (`ba_daily.py:132-144`) — deliberately, per the preset's own comment, "to re-check a rotating target" as AP regenerates from `circle`/`gem_shop`/`battle_pass` claims during the run. Each call that doesn't short-circuit on insufficient AP costs 56s-153s in the logs, mostly navigation to the event badge/page. Total `event_sweep` cost across a run's multiple calls ranged from ~112s (2 skips + 2 clean 56s sweeps) to ~430s (3 calls, one hitting the slow "wrong page" retry path) in the sampled runs.
This is **not recommended as a fix target right now**: the retry/settle budget the "wrong page" path uses (badge-carousel navigation, cold-start OCR settle time) was tuned across many rounds of real live-testing failures before it reached its current reliability — shrinking it risks reintroducing missed/silently-failed sweeps, which is a strictly worse outcome than a slow-but-correct run. If run time here ever needs to come down, the safer lever is reducing *how many times* `event_sweep` is invoked per run (e.g. consolidating to fewer, better-timed calls once AP-granting tasks have run) rather than shrinking any per-call retry/wait constant.
### Other observations (not action items)
- `cafe`'s pat-loop panning cost (~4-5 min/run) is inherent to the sparkle-hunt approach and matches reference behavior — not a target.
- `shop_common`'s full-grid OCR name scan costs ~100-120s/run when most/all configured items are sold out — inherent to the OCR-based item-identification fix from the sold-out-reordering incident (see `CLAUDE.md`'s Common Shop notes); not a target without changing correctness guarantees.