# 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 (implemented 2026-08-14): `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"). **Fix:** `detector.py` gained an optional `image=` parameter on `region_contains_color`/`read_int_on_heart_badge` (default `None`, so every other caller — `shop_utils.py`, `story_sweep.py`, `story_sweep_hard.py`, `cafe.py` — is unaffected) plus a `capture_screen()` helper. `lesson.py`'s `_scan_open_grid_cells` now takes one screenshot per region and threads it through all 27 checkmark/OCR reads instead of each one re-capturing. Pure efficiency fix — the detection logic itself (color masking thresholds, heart-badge OCR contamination handling) is untouched. **Scope respected:** `_run_queue`'s actual ticket-spending clicks (`lesson.py:306`+) still take a fresh screenshot per check, exactly as before — batching only applies to the pure-read scan phase. **Live-verified 2026-08-21** against a full week of real `daily` cron runs (2026-08-15 through 2026-08-21, 14 runs, all exit 0, zero `[lesson]` warnings/errors). Full 12-region scan phase now consistently takes ~3m27s, down from the ~7m2s baseline — a ~51% cut, exactly from removing the redundant per-cell `scrot` calls (per-region time dropped from ~30-38s to ~17-18s). ### Finding 1b (implemented 2026-08-14): stop scanning once triples alone cover the ticket count Added alongside Finding 1: `_scan_all_regions` now takes `tickets` and tracks a running triple count, breaking out of the region loop once `triple_count >= tickets`. This is provably behavior-preserving, not just a heuristic: `_build_priority_queue` never reorders within a tier (triples/doubles stay in scan order) and `_run_queue` stops the instant tickets hit 0, so once enough triples are found, any cell in an unscanned region could only ever land after the ticket budget is already exhausted — `_run_queue` would never reach it either way. The queue actually executed is identical to a full scan's; only the number of regions looked at changes. When tickets exceed available triples, the condition never fires and behavior is unchanged (full scan, as today). One cosmetic side effect: the "N triple(s), M double(s), K single(s)" summary log line under-reports M/K when this fires, since those regions were never examined — expected, not a bug. **Live-verified 2026-08-21**, same week of runs: fired correctly on 2 of 7 ticket-available days (2026-08-18: 7 triples found by region 9/12, skipped the remaining 3; 2026-08-21: 7 triples by region 10/12, skipped 2), and correctly stayed a full 12-region scan on the other 5 days where triples never reached the ticket count. Combined with Finding 1, total `lesson` task duration (start to `Done.`) ranged 5m6s-6m41s across the week, down from the 9m50s baseline. ### 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.