From 988db5d5c4d69fd569911edc8a18f0ad0d7304a9 Mon Sep 17 00:00:00 2001 From: Barely Removable Date: Mon, 31 Aug 2026 20:44:05 -0700 Subject: [PATCH] Fix run_rule(): clicking the menu item only opens a confirmation modal, never runs anything MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The user reported runs logged as "success" locally weren't showing up in MassPrice's own Activity Log. Live-debugged directly against production: clicking the dropdown's "Run Rule" item opens a confirmation modal ("Are you sure you want to continue?") rather than running the rule immediately. Confirmed by network capture — without clicking the modal's #Confirmation_Run button, zero requests fire, no error is thrown, and the rule silently never runs. Every prior "success" in run_history.json for real production traffic was therefore a false positive. Verified the fix live: clicking through #ConfirmationModal's #Confirmation_Run now fires a real POST /Rule/Run request. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01H6TgffxTvYhqxykAbVQg2v --- .claude/skills/deploy-massprice/SKILL.md | 7 +++++++ CLAUDE.md | 23 ++++++++++++++++++----- app.py | 2 +- automation.py | 16 +++++++++++++++- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/.claude/skills/deploy-massprice/SKILL.md b/.claude/skills/deploy-massprice/SKILL.md index 926ca1b..fb407c0 100644 --- a/.claude/skills/deploy-massprice/SKILL.md +++ b/.claude/skills/deploy-massprice/SKILL.md @@ -76,6 +76,13 @@ deploy rsync (which runs `--delete`) and from git. ## If scheduled runs stop working Check, in order: +0. **Local Activity says success but nothing shows up in MassPrice's own + Activity Log** — this exact symptom happened in production once + already: `run_rule()` was only opening MassPrice's "Are you sure?" + confirmation modal, never clicking its `#Confirmation_Run` button, so + zero requests ever fired despite no error being thrown. Fixed, but if + it recurs (e.g. a MassPrice redesign changes the modal/selectors), + this is the first thing to suspect — see `CLAUDE.md`. 1. **Dashboard banner / Activity page** — a "session expired or missing" message means go log in again via the Session page. "rule id not found" means a rule was renamed/deleted on MassPrice — click "Refresh diff --git a/CLAUDE.md b/CLAUDE.md index 55061dc..2e138a5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,12 +44,25 @@ tool is confirmed working, so there's exactly one thing triggering runs. id/name pairs (cached to `rules_cache.json`, refreshed via the Dashboard's "Refresh Rules" button or on first boot). `run_rule(id)` finds that rule's `li.rule.widget-rule` row, clicks its - `.dropdown-ellipsis` three-dot toggle, then clicks the row's + `.dropdown-ellipsis` three-dot toggle, clicks the row's `a.run-rule[data-rule-id=...]` item — mirroring exactly what a human - does in the UI. **No public API was found or used** — the "Run Rule" - click handler is JS/AJAX-bound (no plain href) and its request is - almost certainly anti-forgery-token protected, so automation drives - the real page rather than replaying a raw HTTP POST. + does in the UI — **then must click through a confirmation modal** + (`#ConfirmationModal` → `#Confirmation_Run`; the same generic modal + is reused for Delete Rule etc., so the confirm click is scoped to + that specific modal instance). That confirmation step is not + optional or cosmetic: confirmed by live testing that skipping it + fires **zero** network requests and the rule silently never runs, + even though the initial dropdown click "succeeds" with no error — + an earlier version of this code did exactly that and every reported + "success" was a false positive for a real stretch of production use. + If a future site redesign changes this modal, watch for the same + symptom: local history says success, MassPrice's own Activity Log + never gets a matching entry. **No public API was found or used** — + the "Run Rule" click handler is JS/AJAX-bound (no plain href, a + delegated jQuery handler on `document.body`, not the element itself) + and its request (`POST /Rule/Run`, confirmed by live network capture) + is almost certainly anti-forgery-token protected, so automation + drives the real page rather than replaying a raw HTTP POST. - **Session persistence** — a Playwright `storage_state` (cookies + localStorage) is saved to `/data/auth_state.json` and reused across runs: scheduled runs never touch the login form at all, they just load diff --git a/app.py b/app.py index 89bf0d7..dc65fdc 100644 --- a/app.py +++ b/app.py @@ -8,7 +8,7 @@ import automation import scheduler import storage -APP_VERSION = "0.5.0" +APP_VERSION = "0.6.0" APP_VERSION_DATE = "2026-09-01" app = Flask(__name__) diff --git a/automation.py b/automation.py index 19aaa91..07882b6 100644 --- a/automation.py +++ b/automation.py @@ -190,7 +190,21 @@ def run_rule(rule_id, rule_name=None): run_link = row.first.locator(run_selector) run_link.wait_for(state="visible", timeout=5000) run_link.click() - page.wait_for_timeout(1500) # let the click's AJAX call fire + + # Clicking the dropdown item only opens a confirmation modal + # ("You are about to run the rule ... Are you sure?") — it + # does not trigger the run itself. Confirmed by live testing: + # without this step, zero network requests ever fire and the + # rule silently never runs, despite the initial click + # "succeeding" with no error. #ConfirmationModal / its + # #Confirmation_Run button are used generically across the + # site (also for Delete Rule etc.), so scope the confirm + # click narrowly to this specific modal instance. + modal = page.locator("#ConfirmationModal") + modal.wait_for(state="visible", timeout=5000) + modal.locator("#Confirmation_Run").click() + modal.wait_for(state="hidden", timeout=10000) + page.wait_for_timeout(1000) # let the confirmed run's request land status = "success" message = "Run Rule clicked"