10 KiB
Raw Permalink Blame History

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.