Fix run_rule(): clicking the menu item only opens a confirmation modal, never runs anything
All checks were successful
Deploy / deploy (push) Successful in 25s

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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6TgffxTvYhqxykAbVQg2v
This commit is contained in:
Barely Removable 2026-08-31 20:44:05 -07:00
parent 63e2296d1c
commit 988db5d5c4
4 changed files with 41 additions and 7 deletions

View file

@ -76,6 +76,13 @@ deploy rsync (which runs `--delete`) and from git.
## If scheduled runs stop working ## If scheduled runs stop working
Check, in order: 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" 1. **Dashboard banner / Activity page** — a "session expired or missing"
message means go log in again via the Session page. "rule id not message means go log in again via the Session page. "rule id not
found" means a rule was renamed/deleted on MassPrice — click "Refresh found" means a rule was renamed/deleted on MassPrice — click "Refresh

View file

@ -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 id/name pairs (cached to `rules_cache.json`, refreshed via the
Dashboard's "Refresh Rules" button or on first boot). `run_rule(id)` Dashboard's "Refresh Rules" button or on first boot). `run_rule(id)`
finds that rule's `li.rule.widget-rule` row, clicks its 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 `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" does in the UI — **then must click through a confirmation modal**
click handler is JS/AJAX-bound (no plain href) and its request is (`#ConfirmationModal` → `#Confirmation_Run`; the same generic modal
almost certainly anti-forgery-token protected, so automation drives is reused for Delete Rule etc., so the confirm click is scoped to
the real page rather than replaying a raw HTTP POST. 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 + - **Session persistence** — a Playwright `storage_state` (cookies +
localStorage) is saved to `/data/auth_state.json` and reused across localStorage) is saved to `/data/auth_state.json` and reused across
runs: scheduled runs never touch the login form at all, they just load runs: scheduled runs never touch the login form at all, they just load

2
app.py
View file

@ -8,7 +8,7 @@ import automation
import scheduler import scheduler
import storage import storage
APP_VERSION = "0.5.0" APP_VERSION = "0.6.0"
APP_VERSION_DATE = "2026-09-01" APP_VERSION_DATE = "2026-09-01"
app = Flask(__name__) app = Flask(__name__)

View file

@ -190,7 +190,21 @@ def run_rule(rule_id, rule_name=None):
run_link = row.first.locator(run_selector) run_link = row.first.locator(run_selector)
run_link.wait_for(state="visible", timeout=5000) run_link.wait_for(state="visible", timeout=5000)
run_link.click() 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" status = "success"
message = "Run Rule clicked" message = "Run Rule clicked"