Self-healing agent (supervisor, recovery, login fixes, operator mail) and Portal sage redesign

Snapshot of D:\Ecw Process a11c9ff (2026-10-30).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ManikandanandClaude Opus 5.5 committed 2026-10-30 10:58:35 +05:30
1 parent fafa5285eb
commit d500f750da
26 files changed
+1704 -357

No files matched your search

+91 -1
View File
@@ -40,11 +40,50 @@ Repo: `D:\Ecw Process`, git branch `master`, **no remote** (never pushed).
```bash
python run.py # the bot (logs into LIVE eCW)
python agent.py # the agent: the whole day's queue, self-healing (below)
python tests/test_<name>.py # one test file, prints "N/M passed"
run_web.bat # results portal, http://127.0.0.1:8000
start_ecw_chrome.bat # Chrome the bot attaches to (BROWSER_CDP_URL)
```
**The agent (2026-10-30, operator: "we are an agent, not a bot — observe,
fix, continue, and mail me what happened").** `python agent.py`
(`flows/supervisor.py`) runs the bot as a child process, pass after pass,
until every claim in the day's Medcor Ready queue has a result. Pass 1
downloads the live queue; a later pass takes only the claims still
pending — the queue file minus every `CV_ChargeSummary` row since the
agent started, a "Bot could not ..." Review not counting — through
`RESUME_CLAIM_IDS_FILE` (live mode, live Charge Summary, no TEST label,
no second export). After each pass, by exit code (`next_action`):
login failure (1, 6) → retry after 10 then 20 min, the 3rd in a row stops
the agent (the client's account must not be restricted); restricted (4),
screen changed (7), config (2) → stop at once; stuck page (9), stall (8),
claims error (5) → fresh Chrome + login in 1 min; guard wait (3) → wait;
a clean pass leaving the same claims pending → stop ("left for a
person"); 15 passes a day at most. VERIFY only: refuses to start with
`SAVE_CORRECTIONS=1`; forces `QUIET_BOXES=1`, `SHOW_EACH_CLAIM=0`,
`CLAIM_PROCESS_LIMIT=0`. Every failure and the end are mailed
(`core/notify.py`, from the 2FA mailbox's account over SMTP — `SMTP_*`
default to `IMAP_*`, port 465; **`NOTIFY_EMAIL_TO` blank = no mail, and it
is blank on this PC until the operator gives the address**). A mail
carries no claim number (6+ digit runs masked) and only login / error
page PNGs (`notify._EMAILABLE`) — never a claim screen or page HTML.
Inside a pass, `flows/recovery.py`: a claim the bot could not search or
read → login page on screen = session over (exit 9); else Escape, close
Patient Information, clear the screen, reopen the claim search if still
blocked, and try the claim once more; 2 claims in a row still failing
after recovery → exit 9 (live 2026-10-29: a Dental claim's sliding fee
check left Patient Information open and 18 claims went "Bot could not
search" one after another). Login: the security image page's Continue is
clicked by the bot (`login.continue_security_image`, a person only when no
Continue/OK is found; the page is saved as `security_image_page` the first
time); a 2FA link approved but not picked up in `OTP_PICKUP_SECONDS` (60)
→ Resend OTP + approve the new mail, `OTP_PICKUP_RESENDS` (2) times
(live 2026-10-29 night: three logins lost to mail 6 to 8.5 min late with
Resend still on screen). The "Chrome open" and security image boxes are
quiet with `QUIET_BOXES=1` (a `[BOX]` log line); CAPTCHA and manual 2FA
boxes still show — they need a person. **None of this has run live yet.**
**Watching a run live (2026-10-22, `8af5c14`):** `start_ecw_chrome.bat`
opens Google Chrome with its own profile `D:\EcwChrome` (`CDP_PROFILE_DIR`),
debug port 9222 on 127.0.0.1, the eCW proxy (claude.ai / anthropic.com go
@@ -305,6 +344,9 @@ that box and on the per-claim save box means Cancel.
| `core/page_utils.py` | `shot()` with a FAILURE tag (`is_failure_tag`: failed / missing / not_ / no_ / unconfirmed ...) also saves the page's DOM + frames beside the png as `<HHMMSS>_<tag>.html` (`SAVE_DOM_ON_FAILURE=1`, at most `SAVE_DOM_LIMIT=30` a run; 2026-10-21) — no more hand-copying DevTools into Dom.txt. `visible()` = instant check; `appears()` = real wait (Playwright 1.62's `is_visible(timeout)` never waits); `click_topmost()` clicks the copy on top when dialogs stack (never `.first`) |
| `core/fallback.py` + `selectors/fallbacks.py` | Fallback selectors (2026-10-21): the run-critical selectors are CHAINS — primary first, then alternatives taken from the live DOM (ng-click / ng-model / type+value, plain CSS) — joined as a CSS list, so every call site falls back with no change: password field, Log In, Claim No, Lookup, UB Cancel (`closeUBClaimScreen()`, never the X or OK), Dental Cancel (`closeDntl()`), Prog. Notes (`#UBClaimProgressNoteBtn`, `claimProgressNoteBtn<ts>`). `report()` logs `[SELECTOR] fallback: <name> — <primary> not found, matched <alt>` once a run (pre-flight and login call it) |
| `pages/preflight.py` | Pre-flight selector check (2026-10-21): the claims search page before the first claim, the claim form on the first claim of each screen type (UB / HCFA / Dental: Cancel, CPT tab, rows with code / fee / POS / M1). Missing → `[PREFLIGHT] FAIL <names>`, page shot + DOM saved, run stops with **exit 7** (`PREFLIGHT_STOP=0` only warns). No CPT row on a claim → its row checks wait for the next; 3 in a row = rows missing |
| `flows/supervisor.py` + root `agent.py` | The agent (2026-10-30, §2): passes of the bot as a child process until nothing is pending; `next_action(code, pending, state)` is the whole retry policy, `pending(queue, rows)` the resume list |
| `flows/recovery.py` | A stuck screen inside a pass (§2): `recover()` (logout check, Escape + Patient Information close + clear, reopen search) and `run_claim()` (one retry per claim, 2 in a row → `PageStuck`, exit 9) |
| `core/notify.py` | The agent's mail (§2): `incident()` / `summary()`; claim numbers masked, login / error PNGs only; never fatal |
| `core/watchdog.py` | Claim watchdog (2026-10-22): `watching(limit)` around the claim loop, `beat(where)` at each claim step and every message box, `check()` raises `Stalled` → exit 8; on a stall it closes this process's Playwright driver (`node.exe`) so the frozen call returns, and exits 8 itself if the main thread never comes back |
| `core/run_events.py` | A Run's timed events → `Logs\events_<run>.jsonl` (`start`, `emit`, `warning`, `claim_error`, `run_error` with traceback, `login_attempt_start/end`, `login_step`); never raises, never writes an OTP code / password / username |
| `core/browser.py` | `launch()`: Playwright Chromium with the eCW proxy, or (`BROWSER_CDP_URL`) attach to the operator's Chrome — `clear_ecw_data()` at attach and close, `cdp_ready()` checked before login |
@@ -583,6 +625,22 @@ the Portal (restarted 2026-10-26, [portal] terminal tab).
header `X-Run-Token` must match the token in the page; else 403),
`GET /api/bot/status`. Spec / plan:
`docs/superpowers/specs/2026-10-14-start-bot-from-web-page-design.md`.
- **Sage redesign (2026-10-30, the operator-approved mockup, live on
Dokploy that day from `portal-20261030-ui.zip`).** `base.html`: the navy
header became a sage side menu (`<aside class="side">`: brand, mode pill,
links with CSS-mask icons, the user's name over their role, Sign out);
breadcrumb and footer in the page column; Outfit + Inter from Google
Fonts; the old CSS variable names kept with sage / lime / Pass green /
Fail orange / Review blue values, so every page follows. Dashboard:
"Good morning|afternoon|evening, <first name>" + claim search, 4 tiles
with %, the lime **Agent run** card (the old Bot run card, same ids and
script) beside **Results by day**, **Top reasons today** beside **Runs
today** (`app._runs_today`: today's rows grouped by `run_at`, newest
first — no extra query). Sign in: sage panel + "Welcome back" card.
Not built: Queue / Pending tiles (the Portal cannot see the bot PC's
queue file — the agent would have to write the queue size to the DB).
The mockup canvas: claude.ai artifact "Charge Validation Portal Mockups"
(private to the operator).
---
@@ -647,6 +705,13 @@ the Portal (restarted 2026-10-26, [portal] terminal tab).
- 2026-10-14 #20 final (operator): ONLY the codes the operator gave — the 41 injection codes (IUD / implant, inhalation and oral J-codes included, as listed) and 96372 as the one admin code. No other code is added or counted; no client confirmation needed.
- 2026-10-08 Results go to SQL Server `MECARE331\MECARE331`, DB `Charge_Validation`, in the Charge Summary format (incl. PATIENT, Account Number, Guarantor Name); FastAPI page for now, the full portal later.
- 2026-10-30 The bot becomes an agent: technical failures (login, session, stuck screen, browser) are the agent's to recover from without a person; condition or process mistakes stay the operator's to point out. Mail the operator what happened, what the agent did and whether it worked.
- 2026-10-30 Agent mail: from the bot mailbox over SMTP; screenshots of login / error pages only, never a claim screen; no claim numbers.
- 2026-10-30 The security image page's Continue is clicked by the bot.
- 2026-10-30 Login failures: at most 3 in a row (10 / 20 min apart), then stop and mail — protect the client's eCW account.
- 2026-10-30 Bulk runs show no message boxes; the full day's queue (no `CLAIM_PROCESS_LIMIT`) must finish the same day.
- 2026-10-30 Portal look: the approved mockup — sage side menu, lime highlight, Outfit + Inter, Dashboard greeting / tiles / Agent run / Results by day / Top reasons / Runs today, two-panel Sign in (no Ecare/Portal toggle: the back end has none).
### Waiting on a ruling
1. An admin code / G2025 that the Fee Schedule prices at **0.00** after being added (pinned in tests).
2. `medicare_telehealth` with G2025 **unconfirmed** leaves other rows; `priced_line_check` may then check an unchecked priced row (pinned in tests).
@@ -688,7 +753,11 @@ the Portal (restarted 2026-10-26, [portal] terminal tab).
History: `git log --oneline`.
Last code commits: `0c2fca8` (2026-10-28, vaccine_z23_pointer #45 /
Last code commits: `78409f0` (2026-10-30, Portal sage redesign — §5;
deployed to Dokploy from the working tree before this commit, same
files), `24c9ce4` (2026-10-30, the self-healing agent: supervisor,
recovery, security image Continue, Resend pickup, operator mail — §2,
§10), `0c2fca8` (2026-10-28, vaccine_z23_pointer #45 /
t1017_non_billable #46 — §4), `d1aa6da` (2026-10-28, login waits for a late
security image page — §10), `97d4d74` (2026-10-28, Charge Summary notes a payer the
sheet and eCW disagree on — §5), `aaab1ff` (2026-10-28, lab_codes #3/#4,
@@ -825,6 +894,27 @@ test claim ids — new posts must not (§7).
## 10. Open items
- **Agent (2026-10-30), not yet run live.** To do: `NOTIFY_EMAIL_TO` in
`.env` (the operator's address) and one test mail; a first `python
agent.py` to see the security image Continue click on the real page
(its HTML is saved as `security_image_page` — tighten the selector from
it) and a late-mail Resend pickup. Not built: the agent's status in the
Portal (it runs on the bot PC; the Dokploy Portal sees only the
database — a status / queue-size row would carry it), Queue / Pending
tiles.
- **29/10 live queue (690 claims).** 208 got a result in two runs (17:18
126, 19:29 82); the rest were not reached: Ctrl-C would not stop a
run blocked in a Playwright call (the tab was closed instead), a stuck
Patient Information dialog cost 18 claims (now `recovery.py`), and three
logins that night failed on 2FA mail 6 to 8.5 min late (now the Resend
pickup). 482 claims of that day's queue were never checked.
- **Tests:** `test_cdp_browser.py` 2 of 11 fail on this PC since
2026-10-29 (the Chrome it opens has a command line PowerShell cannot
read — the same `[BROWSER] Could not read the command line` line live
runs print); environment, not code — look at it before trusting the
proxy check. `test_msgbox.py` now pins `QUIET_BOXES` off (this PC's
`.env` has it on).
- **Stability (review of 25 runs, 20–26/10, 2026-10-26).** After the eCW
login the run is steady: the last five full runs read 50/50 with no
bot error and the same results run to run. The weak part is getting