fix(event_sweep): add retry logic for stage-row-enter click to prevent missed clicks
This commit is contained in:
parent
af0a784bcd
commit
63ee47d6e7
@ -8,7 +8,7 @@ Maps each local feature to the corresponding `~/repo/baas-reference/module/...`
|
||||
| Cafe | `module/cafe_reward.py` | `to_cafe` (its `relationship_rank_up` popup-handling now also ported, see below), `interaction_for_cafe_solve_method3`, `collect` | `ba_auto/tasks/cafe.py` | `picture.co_detect`/`color.rgb_in_range` → `driver.color_at` pixel-probe checks; sparkle template match ported in-process into `ba_auto/detector.py` (`find_cafe_sparkle`, now multi-scale) | Migrated: real Python, state-verified via color probes (no legacy bridge). Pat loop now polls for the full attempt budget instead of stopping on the first miss (see `plan.md` Phase 6 follow-up) — not yet confirmed against a live sparkle since none was available during testing. `_dismiss_rank_up_if_shown` reuses `navigation.is_on_subscreen` to detect and clear the full-screen bond-rank-up cutscene after a pat (see `plan.md` Phase 6 follow-up: rank-up popups) — not yet live-confirmed against a real trigger |
|
||||
| Stamina/AP | `module/collect_daily_free_power.py`, `module/collect_daily_task_power.py` | `to_tasks`/`implement` (task-power, ported); `to_purchase_pyroxenes_menu` (free-power, not ported) | `ba_auto/tasks/stamina.py` | `color.rgb_in_range` → `driver.color_at`; reference's per-tab claim loop → live UI's single "一括受取" bulk-claim button + Enter | Partially migrated: Mission-panel claim done (see `plan.md` Phase 8). Daily Free Power (real-money purchase menu) deliberately not automated |
|
||||
| Normal/Hard story AP sweep | `module/explore_tasks/sweep_task.py`, `module/explore_tasks/task_utils.py` | `to_region` (ported: OCR region-number readout + delta-click), a scoped-down `swipe_search_target_str` (ported: OCR stage-row label matching), `start_sweep`'s named-outcome contract (ported via `navigation.wait_for_state`, this project's scoped `co_detect` port) | `ba_auto/tasks/story_sweep.py` | OCR region/stage-name matching, ported for real (Phase 10) — replaces Phase 9's "next-region arrow stops advancing, then random stage" heuristic; MAX click verified via `SWEEP_MINUS_BUTTON_PROBE` color check (reused, still correct), modal closed via its own X button (Escape doesn't close it; X-button position re-calibrated per stage-layout variant, see Phase 10) | Done (see `plan.md` Phase 10, supersedes Phase 9). Config-driven exact `(region, stage, count)` targets (`config.STORY_SWEEP_TARGETS`), not latest-region/random-stage. Opt-in only, not in default flow |
|
||||
| Event sweep | `module/sweep_activity.py`, `module/activities/activity_utils.py` | `activity_sweep` (main flow), `to_activity` (nav to the event's Story/Mission/Challenge tabs), `check_sweep_availability`/`color.check_sweep_availability` (SSS gate), `start_sweep`'s named-outcome contract (shared with story_sweep's, ported the same way via `navigation.wait_for_state`) | `ba_auto/tasks/event_sweep.py`, `ba_auto/navigation.py` (`return_to_home`) | `to_activity`'s bottom-nav-icon entry -> this client's home-screen event badge (`config.EVENT_BADGE_ICON`, confirmed live to be a rotating carousel -- see Status); the reference's config-string sweep-list parsing (arbitrary stage lists, per-stage float/fraction counts via `preprocess_activity_region`/`preprocess_activity_sweep_times`) -> a single date-ordinal-modulo rotation target over a fixed 9-12 sub-range, mirroring `story_sweep.py`'s own rotation, per explicit user direction; stage-number OCR (`detector.read_int`) replaces the reference's `swipe_search_target_str` template-button search, since this client's stage list only ever needs its bottom scroll extreme for the 9-12 target range | Done, with a live-reported bug fixed (see `plan.md`'s Phase 14 follow-up). Live-calibrated against nik-gpu 2026-07-10 against the currently-running "鉄道爆走事件" event (12 stages) -- zero real AP spent during calibration (every confirm dialog reached was cancelled via Escape, verified by the AP counter). The stage-info modal is structurally identical to story_sweep's (MIN/-/+/MAX stepper, same AP-confirm dialog reusing `SWEEP_CONFIRM_*`/`SWEEP_RESULT_BUTTON_REGION`), but simpler: no region navigation, one fixed modal layout confirmed across two different stages (09 and 12), and the modal closes on Escape (story_sweep's doesn't). The reference's SSS-availability gate for a never-cleared stage was never exercised (every stage 9-12 on this account was already 3-starred) -- `event_sweep.py` handles that gate the same defensive way story_sweep handles an unavailable target: if the MAX-button count-raise can't be verified, it aborts without spending AP rather than guessing. **Bug found on a real run**: the home-screen event badge turned out to be a rotating carousel (cycles between the current event's countdown and other notices, e.g. a finished event's leftover reward-claim reminder), so a click could land on a stale event's page instead. Fixed via a new shared `navigation.return_to_home` primitive (bounded press-back-until-home loop, built generically so other tasks can reuse it, per explicit user request) plus wrong-page detection in `_find_stage_row` (zero stage-row numbers OCR'd at all -> retry via `return_to_home`, up to 3 attempts). Not yet live-tested against a real recurrence of the bug. |
|
||||
| Event sweep | `module/sweep_activity.py`, `module/activities/activity_utils.py` | `activity_sweep` (main flow), `to_activity` (nav to the event's Story/Mission/Challenge tabs), `check_sweep_availability`/`color.check_sweep_availability` (SSS gate), `start_sweep`'s named-outcome contract (shared with story_sweep's, ported the same way via `navigation.wait_for_state`) | `ba_auto/tasks/event_sweep.py`, `ba_auto/navigation.py` (`return_to_home`) | `to_activity`'s bottom-nav-icon entry -> this client's home-screen event badge (`config.EVENT_BADGE_ICON`, confirmed live to be a rotating carousel -- see Status); the reference's config-string sweep-list parsing (arbitrary stage lists, per-stage float/fraction counts via `preprocess_activity_region`/`preprocess_activity_sweep_times`) -> a single date-ordinal-modulo rotation target over a fixed 9-12 sub-range, mirroring `story_sweep.py`'s own rotation, per explicit user direction; stage-number OCR (`detector.read_int`) replaces the reference's `swipe_search_target_str` template-button search, since this client's stage list only ever needs its bottom scroll extreme for the 9-12 target range | Done, with a live-reported bug fixed (see `plan.md`'s Phase 14 follow-up). Live-calibrated against nik-gpu 2026-07-10 against the currently-running "鉄道爆走事件" event (12 stages) -- zero real AP spent during calibration (every confirm dialog reached was cancelled via Escape, verified by the AP counter). The stage-info modal is structurally identical to story_sweep's (MIN/-/+/MAX stepper, same AP-confirm dialog reusing `SWEEP_CONFIRM_*`/`SWEEP_RESULT_BUTTON_REGION`), but simpler: no region navigation, one fixed modal layout confirmed across two different stages (09 and 12), and the modal closes on Escape (story_sweep's doesn't). The reference's SSS-availability gate for a never-cleared stage was never exercised (every stage 9-12 on this account was already 3-starred) -- `event_sweep.py` handles that gate the same defensive way story_sweep handles an unavailable target: if the MAX-button count-raise can't be verified, it aborts without spending AP rather than guessing. **Bug found on a real run**: the home-screen event badge turned out to be a rotating carousel (cycles between the current event's countdown and other notices, e.g. a finished event's leftover reward-claim reminder), so a click could land on a stale event's page instead. Fixed via a new shared `navigation.return_to_home` primitive (bounded press-back-until-home loop, built generically so other tasks can reuse it, per explicit user request) plus wrong-page detection in `_find_stage_row` (zero stage-row numbers OCR'd at all -> retry via `return_to_home`, up to 3 attempts). **Second bug found on the next real run** (after the above fix correctly recovered and correctly OCR'd the target row): the row's own 入場 (enter) button click had no retry, unlike every other click-then-confirm step in this module -- missed once, aborted the whole run. Fixed via `_open_stage_modal`, the same click-then-verify-then-retry pattern already used everywhere else in the file. Neither fix has been live-tested against a real recurrence yet. |
|
||||
| Group/Club AP | `module/group.py` | Need to inspect | `ba_auto/tasks/group.py` | fixed click + state check via local driver | Not started |
|
||||
| Bounty | `module/rewarded_task.py` | Need to inspect | `ba_auto/tasks/bounty.py` | sweep/color/OCR adaptation | Not started |
|
||||
| Commissions | `module/clear_special_task_power.py` | Need to inspect | `ba_auto/tasks/commission.py` | sweep/color adaptation | Not started |
|
||||
|
||||
@ -39,6 +39,14 @@ not the target) -- the former retries via navigation.return_to_home + a
|
||||
re-click of the badge, hoping the carousel has moved on by the next attempt;
|
||||
the latter is reported and left alone, since retrying won't fix a genuinely
|
||||
different stage list.
|
||||
|
||||
A second live run (after the above fix) correctly recovered from a wrong
|
||||
first attempt and correctly OCR'd the target stage's row number, but then
|
||||
failed to open that stage's info modal: the row-enter click was a single
|
||||
click-then-check with no retry, unlike every other click-then-confirm step
|
||||
in this module. _open_stage_modal now retries it the same way, matching
|
||||
CLAUDE.md's own documented history of the mailbox/cafe icons missing their
|
||||
first click and working on retry.
|
||||
"""
|
||||
import datetime
|
||||
|
||||
@ -51,6 +59,7 @@ MAX_BUTTON_RETRIES = 3
|
||||
MODAL_CLOSE_RETRIES = 3
|
||||
WRONG_PAGE_RETRIES = 3
|
||||
WRONG_PAGE_RETRY_WAIT = 3
|
||||
STAGE_ENTER_RETRIES = 3
|
||||
|
||||
|
||||
def _is_stage_modal_open(driver, config):
|
||||
@ -195,6 +204,21 @@ def _watch_sweep_result(driver, config):
|
||||
return outcome or "unrecognized_state"
|
||||
|
||||
|
||||
def _open_stage_modal(driver, config, row_y):
|
||||
# A single click-then-check here was found live to intermittently miss
|
||||
# (CLAUDE.md already documents this exact failure mode for the
|
||||
# mailbox/cafe icons: "missed the first click and worked on retry") --
|
||||
# retry-with-verify like every other click-then-confirm step in this
|
||||
# module, rather than aborting on the first miss.
|
||||
for attempt in range(1, STAGE_ENTER_RETRIES + 1):
|
||||
driver.click(config.EVENT_STAGE_ENTER_X, row_y)
|
||||
driver.wait(2)
|
||||
if _is_stage_modal_open(driver, config):
|
||||
return True
|
||||
print(f"[event_sweep] stage info panel not detected after click (attempt {attempt}/{STAGE_ENTER_RETRIES})")
|
||||
return False
|
||||
|
||||
|
||||
def _sweep_target(driver, config, stage, count):
|
||||
print(f"[event_sweep] --- target stage {stage} x {count} ---")
|
||||
|
||||
@ -206,10 +230,7 @@ def _sweep_target(driver, config, stage, count):
|
||||
print(f"[event_sweep] stage {stage} not found in the visible stage list")
|
||||
return "stage_not_found"
|
||||
|
||||
driver.click(config.EVENT_STAGE_ENTER_X, row_y)
|
||||
driver.wait(2)
|
||||
|
||||
if not _is_stage_modal_open(driver, config):
|
||||
if not _open_stage_modal(driver, config, row_y):
|
||||
print("[event_sweep] stage info panel not detected, aborting")
|
||||
return "unrecognized_state"
|
||||
|
||||
|
||||
10
plan.md
10
plan.md
@ -493,6 +493,16 @@ This is the same class of finding CLAUDE.md already documents from mailbox/cafe'
|
||||
|
||||
**Not yet live-tested**: this fix has only been syntax/compile-checked and deployed, not run against a real recurrence of the wrong-page bug — the retry timing (3 attempts, 3s apart) is a reasonable-but-unconfirmed guess at how fast the badge carousel actually cycles.
|
||||
|
||||
#### Phase 14 follow-up #2: stage-row-enter click had no retry
|
||||
|
||||
**Reported by the user after re-running live.** The follow-up #1 fix worked as intended -- the outer retry loop correctly detected the first attempt's failed event-screen open, called `navigation.return_to_home`, and the second attempt landed on the right event, correctly OCR'd row 1055 as stage "12" (the day's rotation target). But the very next step -- clicking that row's own 入場 (enter) button to open the stage-info modal -- was a **single click with no retry**, unlike every other click-then-confirm step in this module (`_open_event_screen`'s badge click, `_click_max_and_verify`, `_click_plus_and_verify` all retry). That click missed once and the whole run aborted with `unrecognized_state` instead of trying again.
|
||||
|
||||
This is the same failure class `CLAUDE.md` already documents for the mailbox/cafe icons ("missed the first click and worked on retry"), just not yet covered here. The user's own hypothesis was that the click might actually be registering as a drag rather than a tap -- `driver.click`'s `xdotool mousemove` + separate `xdotool click 1` is a discrete warp-then-click, not a drag gesture, so that specific mechanism seems unlikely, but a plain missed click (or the modal needing a beat longer to render before the corner-darkness probe reads it) fits the existing pattern exactly.
|
||||
|
||||
**Fix**: new `_open_stage_modal(driver, config, row_y)` wraps the row-enter click in the same click-then-verify-then-retry pattern (`STAGE_ENTER_RETRIES = 3`) already used everywhere else in this file. `_sweep_target` calls it instead of the old bare single click.
|
||||
|
||||
**Not yet live-tested**: syntax/compile-checked and deployed only.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### OCR
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user