Ecare staff sign in with their office username and password, checked by the Ecare sign-in service from the backend (AUTH_TOKEN_API_URL); a good Ecare login still needs a Portal User. Service down = "try again shortly", never a wrong password. /api/auth/login, me, logout; sign-in tries limited per address. Users page: Ecare office login or Portal password per user. Context.md: 27/10 runs, the login retry bug. 71 test files, 1239 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1226 lines
96 KiB
Markdown
1226 lines
96 KiB
Markdown
# Project Context — eCW Charge Validation Bot (Salud)
|
||
|
||
_Last updated: 2026-10-23. A one-file briefing for anyone (person or AI session)
|
||
picking this project up. No credentials or patient data belong in this file._
|
||
|
||
---
|
||
|
||
## 1. What this is
|
||
|
||
A Python + Playwright bot that drives the **eClinicalWorks (eCW) web UI** for the
|
||
client **Salud** and does the "Charge Validation" step of the team's Medcor
|
||
Ready workflow:
|
||
|
||
1. Logs into eCW (username/password from `.env`, 2FA approval link read from
|
||
the mailbox automatically).
|
||
2. Filters claims to status **108ME "Medcor Ready"** and exports the Claim
|
||
Report to `Input\Claim Report_<date>.xlsx`.
|
||
3. Adds a payer **Group Name** → `Output\Medcor Ready_<date>.xlsx`, using the
|
||
team's own template (Claim Status Dashboard pairs, see §5).
|
||
4. For each claim: looks it up, opens the claim form (UB / HCFA / Dental
|
||
screen), waits for it to finish loading, reads it, opens the Progress Note,
|
||
runs the **conditions** (rules from the client's *Charge Condition
|
||
Document*), shows **one** review box per claim, and closes the claim with
|
||
**Cancel** (or Save, only if enabled and confirmed).
|
||
5. Writes `Output\Charge Summary_<date>.xlsx` in the team's 24-column format
|
||
(`Charge Summary_TEST_<date>.xlsx` for a `TEST_CLAIM_IDS_FILE` run),
|
||
**after every claim** (since 2026-10-12): the sheet grows claim by claim,
|
||
so a run that stops halfway keeps what it did. A same-day file is kept as
|
||
`__HHMMSS`; if it is open in Excel, the new sheet is saved under a
|
||
`__HHMMSS` name instead and the log and a box say which.
|
||
6. With `RESULTS_DB=1`, saves each claim's summary row to SQL Server
|
||
(`Charge_Validation.dbo.CV_ChargeSummary`) as soon as that claim is done;
|
||
`run_web.bat` shows them on a local page (§5).
|
||
|
||
Repo: `D:\Ecw Process`, git branch `master`, **no remote** (never pushed).
|
||
|
||
---
|
||
|
||
## 2. How to run
|
||
|
||
```bash
|
||
python run.py # the bot (logs into LIVE eCW)
|
||
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)
|
||
```
|
||
|
||
**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
|
||
direct, for the Claude extension) and the bot's usual Chrome flags. With
|
||
`BROWSER_CDP_URL=http://127.0.0.1:9222` in `.env` the bot attaches to that
|
||
Chrome instead of launching Playwright's Chromium: it opens its own tab,
|
||
clears **eCW's** cookies and site storage (`ecwcloud.com` only — claude.ai's
|
||
sign-in stays) when it attaches and again when it closes, closes only its own
|
||
tabs and leaves Chrome open. Only a loopback URL is accepted. The captcha
|
||
incognito retry still launches its own Chromium. Blank `BROWSER_CDP_URL` =
|
||
the old launch.
|
||
**Since `0f16560` (2026-10-22, operator: "Start on the Portal is all it
|
||
takes"):** no Chrome on the port at Start → the run **opens Google Chrome
|
||
itself** (same profile, proxy and flags, through a short-lived Python so
|
||
Chrome is outside the bot's process tree: a Portal Stop's `taskkill /T`
|
||
never takes it) and **closes it at the end** (`CDP_CLOSE_AFTER_RUN=1`); a
|
||
Chrome the run did not open is never closed. Before login the Chrome on the
|
||
port must carry the eCW proxy (its command line, read with PowerShell, has
|
||
`--proxy-server=<PROXY_IP:PORT>`), else exit 2 — added after a test opened
|
||
a Chrome with no proxy on 9222 at 19:41 that day, a Portal run attached to
|
||
it and eCW refused the login as "restricted" (exit 4, guard lockout 5 min;
|
||
the tests no longer read the real `.env`'s `BROWSER_CDP_URL`). **`.env` on
|
||
this PC has `BROWSER_CDP_URL=http://127.0.0.1:9222` since 2026-10-22**
|
||
(backup of the earlier `.env` in the session's temp folder). Live: 19:29
|
||
attached and reached the 2FA wait (stopped), **19:53 Portal run opened
|
||
Chrome, logged in, 50/50 claims (39 Pass / 11 Fail / 0 Review), cleared the
|
||
eCW cookies and closed Chrome** — exit 0. The landing page after Home took
|
||
~2 min there (15 s in Chromium runs); watch it.
|
||
**Since `8fea168` (2026-10-23, operator):** a run that opened Chrome itself
|
||
shows a person's box **"eCW Bot — Chrome open"** after the proxy check and
|
||
before the eCW login, so the Claude extension can be added; OK → login
|
||
(no OK → on after `MSGBOX_TIMEOUT_SECONDS`, 180 s; shown on a Portal run
|
||
too). Once the bot's tab is open, that Chrome's **empty first tab** (New Tab
|
||
/ about:blank) is closed, so the window shows eCW only. A Chrome already on
|
||
the port (`start_ecw_chrome.bat`): no box, every tab kept. Live 2026-10-23
|
||
terminal runs: 16:27 — 20 of 50 claims (all Pass), stopped with Ctrl-C
|
||
(KeyboardInterrupt in a claim box: no `run_end`, so the Runs page shows it
|
||
as ended without a record); 17:00 — stopped with Ctrl-C at the proxy check,
|
||
before any login; **17:07 — the box came up, OK, login in 279 s (box wait
|
||
included), 2FA mail in 42 s, Chrome showed only eCW tabs**, claims running.
|
||
A full test suite ran beside the 16:27 and 17:07 runs without touching
|
||
them (the tests' Chrome uses a free port, never 9222).
|
||
**2026-10-26 17:40 terminal run:** Chrome open box → OK, login 97 s (2FA
|
||
mail 42 s), **50/50 read, 38 Pass / 12 Fail / 0 Review, no bot error**,
|
||
~27 min. Against the 23/10 17:07 run, 49 claims gave the same comment;
|
||
the one change is eCW data, not code: a patient's sliding fee was renewed
|
||
(new period from 10/23/2026), so a claim with an earlier DOS is now Fail
|
||
"Sliding fee NOT STARTED" (§6 Waiting 6). The #25 and Portal changes
|
||
of 23–26/10 changed no other result.
|
||
**2026-10-27 terminal runs:** 16:40 — 2FA mail never came in 10 min (eCW
|
||
timed the approval out), then attempt 2 crashed: the run closed the Chrome
|
||
it opened and re-attached to 9222 while that Chrome was still closing
|
||
(`connect_over_cdp: Target page ... has been closed`; §10). 16:53 (after a
|
||
120 s guard cooldown) — login 220 s, 2FA mail 42 s, **50/50, 38 Pass / 12
|
||
Fail / 0 Review** (the 50-claim sheet; same as 26/10 17:40). 17:30 with
|
||
`Mecor Ready 2 - 10262026.xlsx` (all claims, QUIET_BOXES, ECW_KEEP_OPEN=0)
|
||
— login 98 s, **50/50, 30 Pass / 15 Fail / 5 Review**, the same as that
|
||
file on 26/10. No bot error in either.
|
||
|
||
**Runs page (2026-10-22, Stage 1 of EEWA-31..33, `751a177`):**
|
||
http://127.0.0.1:8000/runs (header link "Runs"). Every Run writes
|
||
`Logs\events_<run>.jsonl` beside `run_<run>.log` (`core/run_events.py`):
|
||
one JSON line per event, each with its time to the millisecond, flushed at
|
||
once, never fatal. The page lists every Run (Started By, Run Source, mode,
|
||
duration, result + exit code, eCW Login Attempts + total login time,
|
||
Pass / Fail / Review, Claim Errors, Warnings; filters date / result) and a
|
||
Run's detail: each eCW Login Attempt's steps with times and the 2FA mail
|
||
wait, Run Errors with the **traceback** (the log never had one), Claim
|
||
Errors, Not Reached claims, Warnings, failure screenshots (PNG only) and a
|
||
timeline; refreshes every 5 s while Running. Runs before 2026-10-22 are not
|
||
listed. Started By = the signed-in Portal User for a page run
|
||
(`ECW_STARTED_BY`, Stage 2), the Windows user for a terminal run. Seen live
|
||
2026-10-22 16:24 (a terminal run of the 50-claim sheet): every login step
|
||
and claim recorded; Log In → 2FA mail 46 s. Disk check before login: system drive under
|
||
`DISK_WARN_MB` (2048) = Warning, under `DISK_STOP_MB` (500) = exit 2.
|
||
Terms (Run Error / Claim Error / Warning / Fail / Not Reached / eCW Login
|
||
Attempt / Portal User / Sign-in / Started By): `GLOSSARY.md`. Why files
|
||
and not SQL: `docs/adr/0001-run-events-in-files.md`. Spec:
|
||
`docs/superpowers/specs/2026-10-22-run-tracking-and-portal-sign-in-design.md`.
|
||
|
||
**Portal Sign-in (2026-10-22, Stage 2 of EEWA-31..33, `c230a8e`):** every
|
||
page and `/api` needs a Sign-in. Setup, once: run `tools\sql\create_tables.sql`
|
||
in SSMS (adds `CV_PortalUser`, `CV_PortalSignIn` and, for Stage 3,
|
||
`CV_ChargeSummary.started_by`), then
|
||
`python tools\add_portal_user.py` for the first Super Admin (password typed
|
||
hidden), then restart `run_web.bat`. Roles since `f99dc17` (2026-10-27,
|
||
operator: "super admin means me"): **Super Admin** (everything; Users page
|
||
adds / changes Admins and Viewers — never deletes; nobody changes a Super
|
||
Admin from the Portal, only the PC tool makes one), **Admin** (everything:
|
||
Start / Stop, Sign-ins page; adds / changes Viewers only), **Viewer**
|
||
(Dashboard, Claim status, Runs; changes nothing, no Users or Sign-ins).
|
||
Operator is gone (folded into Admin). The live table was moved 2026-10-27
|
||
by the operator in SSMS (the bot's SQL login cannot ALTER the table): the
|
||
one user is Super Admin; the CHECK allows only the three roles.
|
||
**Ecare office sign-in (`1da9e7c`, 2026-10-27, operator: the office login
|
||
API, "only who we want"):** with `AUTH_TOKEN_API_URL` set
|
||
(`https://auth-wfm.ecareindia.com/api/login/token`; public TLS cert, empty
|
||
body = 401), a user added with sign-in "Ecare office login" (the Users
|
||
page default) keeps no Portal password (`pw_iterations` 0): the backend
|
||
posts their username + password there (15 s); 4xx = wrong password,
|
||
5xx / timeout / 404 / 405 = 503 "try again shortly", a good login with no
|
||
Portal User = 403 `no_access`. The Ecare token is not kept; same session
|
||
cookie. `/api/auth/login` / `me` / `logout`; 20 tries per address per 15
|
||
min = 429. "Portal password" users (the Super Admin's backup, clients)
|
||
never reach Ecare. NOT live yet: `AUTH_TOKEN_API_URL` is in neither the
|
||
PC's `.env` nor Dokploy, and no zip with it is deployed. Waiting: which
|
||
fields the Ecare reply carries (name, emp id?) so the Add form can be
|
||
username + role only and fill the name at first Sign-in —
|
||
`D:\ecw-portal-deploy\ecare-fields.ps1` (prints field names, never the
|
||
token or password) or AutoPilot's `backend/app/api/auth.py`.
|
||
Passwords PBKDF2-SHA256 (600k, own salt), 10+
|
||
characters; a user an Admin adds or resets must choose their own at the
|
||
next Sign-in. 5 wrong passwords = locked 15 minutes; 30 minutes idle =
|
||
signed out; sessions live in the server's memory (a restart signs everyone
|
||
out). The header shows the user, role and Sign out. Started By goes to the
|
||
bot as `ECW_STARTED_BY` (Runs page, `bot_run.json`, the Dashboard card
|
||
"started by …"); Stopped By into the Run's `stopped` event. Code:
|
||
`web/users.py`, `web/app.py`; tests `test_portal_users.py`,
|
||
`test_portal_signin.py` (the other web tests sign in first). **Set up on
|
||
this PC 2026-10-22:** the new part of `create_tables.sql` run in SSMS as an
|
||
admin at 18:19 (the bot's Windows login `MFILESERVER\EC10980` cannot create
|
||
or alter tables — checked; it can insert / update the new ones through its
|
||
database roles); first Admin `ec10980` added with `add_portal_user.py` at
|
||
18:22 (PBKDF2 600k, nothing plain stored); **first live Sign-in 18:24**
|
||
(recorded in `CV_PortalSignIn`). To look at the tables in SSMS:
|
||
`tools/sql/view_portal_tables.sql` (read-only; the user query leaves the
|
||
hash and salt out).
|
||
|
||
**Last-run banner and Started By in the results (2026-10-22, Stage 3,
|
||
`ecbbf3a`):** the Dashboard shows a red banner when the latest Run ended in
|
||
a Run Error or without a record (killed / crashed) — when, Started By, step,
|
||
exit code, message, claims Not Reached, a link to the Run; never for Done,
|
||
Stopped or Running, and the next good Run clears it.
|
||
`dbo.CV_ChargeSummary.started_by` holds the Run's Started By: the bot writes
|
||
it only once the column exists (checked before each insert; until then the
|
||
rows are saved without it), the Portal shows it as "Started By" in a claim's
|
||
side panel, and a Portal reading a table without the column says "Results
|
||
table needs an update — run create_tables.sql". The Excel Charge Summary is
|
||
unchanged. Tests: `test_run_alerts.py`.
|
||
|
||
**From the page (2026-10-14):** `.\run_web.bat` → http://127.0.0.1:8000
|
||
(the Dashboard) → **Start run** (Super Admin / Admin; Admin / Operator 2026-10-22 to 10-27). The page starts `python run.py` itself, always **VERIFY**
|
||
(`SAVE_CORRECTIONS=0` forced), information boxes go to the log
|
||
(`QUIET_BOXES=1`); CAPTCHA / 2FA boxes still come up on the PC. The card
|
||
shows phase, progress, Pass/Fail/Review and a live log; **Stop** kills the
|
||
bot and its Chrome. A terminal run is shown too but cannot be stopped
|
||
from the page. The child reads `.env` afresh (an edit made after the
|
||
page started is seen) but writes to the page's own results database.
|
||
|
||
All test files at once (there is no `tests/run_all.py` yet). Git Bash:
|
||
|
||
```bash
|
||
for t in tests/test_*.py; do echo "$t: $(python "$t" 2>&1 | grep -E '^[0-9]+/[0-9]+ passed' | tail -1)"; done
|
||
```
|
||
|
||
PowerShell:
|
||
|
||
```powershell
|
||
Get-ChildItem tests\test_*.py | ForEach-Object { "$($_.Name): $((python $_.FullName 2>&1 | Select-String '^\d+/\d+ passed' | Select-Object -Last 1).Line)" }
|
||
```
|
||
|
||
- Dependencies: `playwright`, `pyotp` only for the bot (locked-down machines;
|
||
no pytest, no openpyxl in the bot). The results DB and page use `pyodbc`,
|
||
`fastapi`, `uvicorn`, `jinja2` (+ `httpx` for tests), already installed on
|
||
this machine, with ODBC Driver 18 for SQL Server.
|
||
- Tests run offline against fake HTML pages in `tests/fixtures/` and fake DB
|
||
connections; they never touch eCW or SQL Server (`test_cdp_browser.py`
|
||
starts a local Chromium on a debug port). As of 2026-10-27: 71 test
|
||
files, **1239 tests** (full run 2026-10-27: 1239/1239) (earlier flaky, each
|
||
failed once and passed on rerun: test_verify_no_live_writes,
|
||
depression_dx_pointer, progress_note_wait — watch them) (about 5 minutes for all of them).
|
||
|
||
### Run modes
|
||
|
||
Read by `settings.py`. A setting missing from `.env` takes the code default,
|
||
so deleting a `.env` line does not switch anything off.
|
||
|
||
| Setting | Meaning | In `.env` now | Code default |
|
||
|---|---|---|---|
|
||
| `TEST_CLAIM_IDS_FILE` | If set: skip the bulk export, process the ids in this sheet; summary goes to `Charge Summary_TEST_<date>.xlsx`; missing path = exit 2 before login, unreadable sheet = exit 5 | `Input\Medcor Ready Claim Sheet.xlsx` (since 2026-10-06) | blank (bulk export) |
|
||
| `SAVE_CORRECTIONS` | 0 = apply fixes for review, close with Cancel. 1 = save after operator OK | **0 (deliberate)** | 0 (since 2026-10-05) |
|
||
| `APPLY_CORRECTIONS` | Run the conditions at all | not set | 1 |
|
||
| `CLAIM_PROCESS_LIMIT` | Max claims per run (0 = all) | 20 | 1 |
|
||
| `CHECK_SLIDING_FEE` / `CHECK_HIGH_RISK` | Dental checks | not set | 1 / 1 |
|
||
| `WRITE_CHARGE_SUMMARY` / `SUMMARY_ASSIGNED_TO` | End-of-run summary sheet | not set | 1 / `eCW Bot` |
|
||
| `SHOW_EACH_CLAIM` / `REVIEW_BEFORE_CLOSE` | Per-claim box after close / the one box shown with a corrected claim still open (replaces the per-claim box for that claim) | 1 / not set | 1 / 1 |
|
||
| `PROGRESS_NOTE_WAIT_MS` | How long to poll for the Progress Note after clicking Prog. Notes | not set | 20000 |
|
||
| `MSGBOX_TIMEOUT_SECONDS` | Message box timeout | 180 | 180 |
|
||
| `RESULTS_DB` / `RESULTS_DB_CONN` | Also save the summary rows to SQL Server / ODBC connection string | not set | 0 / Windows login to `MECARE331\MECARE331`, DB `Charge_Validation` |
|
||
| `DISK_WARN_MB` / `DISK_STOP_MB` | System drive free space before login: Warning / stop with exit 2 | not set | 2048 / 500 |
|
||
| `BROWSER_CDP_URL` / `CDP_PROFILE_DIR` / `CDP_CLOSE_AFTER_RUN` | Run in Google Chrome on this debug port (127.0.0.1 only; opened by the run when none is there) / its profile / close a Chrome the run opened | `http://127.0.0.1:9222` (since 2026-10-22) / not set / not set | blank (launch Chromium) / `D:\EcwChrome` / 1 |
|
||
| `WATCHDOG_MINUTES` | No progress on a claim this long = frozen page → driver closed, exit 8 (never under `MSGBOX_TIMEOUT_SECONDS` + 2 min; 0 = off) | not set | 10 |
|
||
|
||
⚠️ **There is no separate test environment.** Same code, same live eCW for
|
||
"test" and "live" runs; only the claim list and the save switch differ.
|
||
Since 2026-10-05 (review item P1) `SAVE_CORRECTIONS` **defaults to 0 in code
|
||
and in `.env.example`**, so a missing or blank line can never turn saving (the
|
||
OK click) on; `APPLY_CORRECTIONS` stays 1 (it only fills the form in, closed with
|
||
Cancel while saving is off, so it does not save, *assuming Cancel discards the
|
||
edits*: unverified until P16's check on a test claim, since adding a CPT row or
|
||
ticking a bill-to box may already save on the server). An unquoted `# comment`
|
||
after whitespace is stripped (`SAVE_CORRECTIONS=0 # note` reads as 0), and a
|
||
yes/no or number value the bot cannot read stops the run with exit 2 before
|
||
login. Every run prints `[MODE]` lines (VERIFY / LIVE-WRITE, claim limit, test
|
||
sheet), and with `SAVE_CORRECTIONS=1` it asks for an OK before login; Enter on
|
||
that box and on the per-claim save box means Cancel.
|
||
|
||
---
|
||
|
||
## 3. Code layout (`src/ecw_bot/`)
|
||
|
||
| Path | Role |
|
||
|---|---|
|
||
| `flows/export_medcor_ready.py` | Main flow: login → filters → export → categorize → per-claim loop → per-claim box → summary (~1,280 lines) |
|
||
| `pages/claim_form.py` | Claim form open/load-wait/read/close/save + all CPT-row edit helpers + telehealth detection; `clear_screen()` (before every claim) and `close_duplicate_forms()` (~2,420 lines) |
|
||
| `pages/progress_note.py` | Find and read the Progress Note (iframe/tab), keyed by `prisma-section` |
|
||
| `pages/sliding_fee.py` | Dental: Info → Patient Information → Sliding Fee Schedule date check |
|
||
| `pages/dental_notes.py` | Dental: "high risk" in Billing Notes |
|
||
| `pages/claim_filters.py`, `claim_results.py`, `claim_report.py`, `leftnav.py` | Claim lookup screen, search, export; `claim_results.search_blocker()` = Claim No + Lookup on screen, enabled, nothing over them, eCW spinner gone; `wait_search_ready()` gives it up to 20 s before the claim is a failed search |
|
||
| `selectors/*.py` | All CSS selectors (mapped from live DOM; comments say where from) |
|
||
| `conditions/*.py` | One module per rule; registry in `conditions/__init__.py` |
|
||
| `conditions/base.py` | `run_all`: **every `detect()` on the untouched claim first, then `apply()` in registry order**; `ConditionResult.review` / `declined()`, optional `needs_review()` and `summary_comment()` hooks, and a `report` dict (fired / changed / failed / review / comments) |
|
||
| `core/charge_summary.py` | Charge Summary sheet (24 columns); `build()` (the rows) and `write()` (Excel); `review_reasons()` and `claim_outcome()` → Pass / Fail / Review |
|
||
| `core/results_db.py` | The same rows → SQL Server `dbo.CV_ChargeSummary` (typed, parameterized insert, never fatal) |
|
||
| `core/run_lock.py` | The run record `Logs\bot_run.json`: one live run at a time, exit code, source (web / terminal) |
|
||
| `core/payer_groups.py` + `payer_groups.json` | PAYER → Group Name (dashboard names, `classify()`); `rule_group()` translates them to the names the conditions decide on |
|
||
| `core/spreadsheet.py` | Minimal xlsx read/write (no openpyxl); `write_xlsx_keep_old()` keeps a same-day file and survives one open in Excel |
|
||
| `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 |
|
||
| `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 |
|
||
| `core/msgbox.py` | Timed Windows message boxes; `default_cancel=True` makes Enter mean Cancel |
|
||
| `core/text.py` | `first_line()` shared by core / conditions / flows |
|
||
| `auth/*` | Login, 2FA mailbox, captcha, cooldowns; `login.wait_for_home()` answers eCW's "password expires in N days" box with Cancel; `password_entry.type_password()` TYPES the password key by key (never `fill()`, 2026-10-20) |
|
||
| `settings.py` | `.env` parsing and every setting |
|
||
|
||
Outside `src/`: `web/` (FastAPI results page: `app.py`, `repo.py`,
|
||
`templates/index.html`; `bot_runner.py` starts / stops the bot as a
|
||
child process; `run_status.py` reads the run log into the page's live
|
||
status and per-claim list), `tools/sql/create_tables.sql` (results
|
||
table), `tools/import_payer_groups.py`, `run_web.bat`,
|
||
`start_ecw_chrome.bat` → `tools/start_ecw_chrome.py` (§2), `web/users.py`
|
||
+ `tools/add_portal_user.py` + `templates/signin.html`, `password.html`,
|
||
`users.html`, `signins.html` (Portal Sign-in, §2), `web/runs.py`
|
||
+ `templates/runs.html` / `run.html` (the Runs page, §2).
|
||
|
||
Claim screens: **UB** (`#UBClaimDiv`, has a CPT/HCPCS tab), **HCFA**
|
||
("Claim" screen, `data-fieldname` fields, payer in `#billingClaimTbl8`),
|
||
**Dental** (`dentalClaim<ts>`, `#cancelDentalClaimButton`).
|
||
|
||
---
|
||
|
||
## 4. Conditions (UB/HCFA claims)
|
||
|
||
Registry order (`conditions/__init__.py`) — order matters:
|
||
|
||
| # | Module | Doc row | Rule |
|
||
|---|---|---|---|
|
||
| 1 | `zero_fee_uncheck` | common rule | **FIRST.** Checked CPT line with Billed Fee 0 → uncheck |
|
||
| 2 | `telehealth` | #21 | Telehealth note → POS 2 + modifier on first CPT row: Medicaid/RAE = GT, Commercial = 95, audio-only (Commercial) = 93. Medicare excluded. Any other payer group (unmapped, blank, Program, Dentaquest) → declined → Review. A row already at POS 2 / right modifier is not a change |
|
||
| 3 | `medicare_telehealth` | #22 | Medicare telehealth → POS 2 + 95/93, add **G2025**; once G2025 confirmed, uncheck + zero other rows. G2025 not added (or its modifier did not take) → Review, other rows left alone |
|
||
| 4 | `depression_dx_pointer` | #6 | Z13.31 pointer only on G8510/G8431; removed elsewhere; duplicate G8510/G8431 → keep first priced one. **Not on Medicare claims** (#14 instead) |
|
||
| 5 | `j2930_j2001_update` | #5 | J2930 and J2001 → uncheck + fee 0 (no replacement code) |
|
||
| 6 | `dummy_codes` | #8 | DCONS / Dscprit (any case) → uncheck + fee 0. BHT3/BHT4 are NOT dummy codes (operator, 2026-10-06) |
|
||
| 7 | `dental_dummy_codes` | #48 | DPIMP or **DFIMP** (any case; DFIMP "FINAL IMPRESSION" is the code the live Dental grid carries, 2026-10-20). **Dental payer** (dashboard "Dental - Commercial" or "Dental - CHP+/DentaQuest") → uncheck + fee 0. **Any other payer**, unmapped or none read → nothing changed, claim **Review** with a Comments note naming the payer group ("DPIMP on a Medicaid payer claim — not changed ...") (operator, 2026-10-13). Separate from #8 |
|
||
| 8 | `dental_payer_check` | #13 | A **billed Dental code** ("D" + 4 digits; DCONS / Dscprit / DPIMP / DFIMP are not; billed = `lines.ends_billed`) and **no insurance row** (primary, secondary ...) is DentaQuest, CHP+ DentaQuest or "Delta Dental …" (by name, case and spaces ignored; Aetna Dental PPO, Cigna Dental, MetLife are NOT allowed) → **Claim Status set to Patient** (`PAT`, `claim_form.set_claim_status`, `selectors.claim_form.SEL_CLAIM_STATUS` — never the `#status` claimRP field), no program check (operator, 2026-10-21, EEWA-27). Only where the run may write (`writes_allowed`: LIVE-WRITE, never the Dental screen) — otherwise Review "set it to Patient by hand". No payer read → Review, nothing changed. Comment names codes and payer(s) |
|
||
| 9 | `medicaid_uncheck_cpts` | #43, #44 | **Medicaid** payer (rule_group "Medicaid" only — not RAE, CHP+, unmapped or blank; RAE ruled out by the operator 2026-10-14) → 15851, 15852, 73521 uncheck + fee 0; no other line touched (operator, 2026-10-13) |
|
||
| 10 | `medicare_billing_screening` | #14 | Medicare payer (rule_group; Medicare Advantage plans are Commercial since 2026-10-21) → G8510/G8431 uncheck + fee 0; no other line touched. No other billed line (screening only) → also Review "provider clarification needed" (operator, 2026-10-07) |
|
||
| 11 | `medicare_vaccine_a6` | #19 | **Medicare** payer (rule_group) with a billed **G0008 or G0009** (`lines.ends_billed`) → condition code **A6**: UB form ON SCREEN only — A6 already in FL 18-28 → nothing; else typed into the first empty box (`uBClaimCondtnCode1..11`) and read back from the box and its Angular model; all 11 full → Review "add A6 by hand"; typed but not taken → changed + Review. HCFA / Dental → Review "check A6 by hand" — **not to be mapped**: Medicare is billed on UB (#2), so such a claim is on the wrong form (operator, 2026-10-21). **#19 complete**; live test still needs a Medicare UB claim with G0008 / G0009. Only those two codes trigger it — not #18's vaccine list; the bot does not add G0008/G0009 (operator, 2026-10-16) |
|
||
| 12 | `medicare_wellness_history` | #27, #28, #29 | **Check only, never changes the claim** (operator, 2026-10-20). **Medicare** payer with a billed **G0402** (IPPE, once per lifetime), **G0438** (first AWV, once in 3 years) or **G0439** (subsequent AWV, once a year): reads the patient's earlier claims in **Account Inquiry** (claim form side menu → Patient Inquiry Detail → Show CPT Payments, every page; this claim left out; closed with the X, the tick undone). Billed inside the window, counted either way from this claim's service date (exactly 3 years / 12 months = outside) → **Review**, Comments "G0438 already billed on MM/DD/YYYY (claim N) — once in 3 years". Account Inquiry not read → Review "check … history by hand". `pages/account_inquiry.py`, `selectors/account_inquiry.py`. Since `ac25f83` (2026-10-23) it also leaves its verdict on the claim's data (`_wellness_history`: read or not, the repeats found) for #25 — still nothing changed in eCW |
|
||
| 13 | `medicaid_pregnancy_th` | #23 | **Medicaid** payer (rule_group "Medicaid" only, NOT RAE) with a **pregnancy-related ICD** (chapter O, Z33, Z34, Z36, Z3A — **Z32.00/.01/.02 pregnancy test never counts**) → **TH** on the first billed E&M line (99202-99499; none → first billed line), **in M1, overwriting what M1 held** (comment says "replacing 25"; never M2-M4); TH already in any box → nothing; no M1 box → Review "add TH by hand". A Medicaid telehealth pregnancy visit: **#21 writes TH instead of GT** (RAE keeps GT). Dental → Review. `9b87b4d`, not run live yet (2026-10-20, EEWA-30) |
|
||
| 14 | `bh_h_code_modifier` | #12 | H code (H + 4 digits). **BH payer** (name starts with "BH") with any modifier → M1–M4 cleared on that line only; telehealth note → not fired (GT stays); note not read → Review, nothing changed (operator, 2026-10-07). **Any other payer** → billed H code line unchecked + fee 0.00, telehealth or not, modifier or not; blank payer → not fired (operator, 2026-10-12). Comment worded from what was done |
|
||
| 15 | `vaccine_admin_codes` | #18 | Vaccine without admin code → add 90460(+90461) age ≤18, 90471(+90472) >18; units 1; all payers except Medicare. **Vaccine = the client's 19-code list only** (90633, 90648, 90651, 90656, 90657, 90660, 90680, 90687, 90698, 90707, 90710, 90713, 90716, 90732, 90734, 90744, 90746, 91321, 91322), by code; **admin = 90460/90461/90471/90472 only** (operator, 2026-10-13; no hover text, no 90476-90759 range). Unclassified payer / no payer / no age → Review; an admin code that could not be added → Review ("NOT added — add by hand") |
|
||
| 16 | `injection_admin_code` | #20 | Injection code from the client's **41-code list** (J0561 ... J8499, as given — IUD, inhalation and oral J-codes included) billed without **96372** → add 96372 ×1, Units 1, all payers except Medicare. Only 96372 counts as admin billed — also when it is unchecked with a fee (priced_line_check checks it at the end, so no second 96372; review 2026-10-14). J2001 / J2930 never count (#5 unchecks them). Unclassified / no payer → Review; not added → Review; Dental → Review (operator, 2026-10-13) |
|
||
| 17 | `medicare_pps_code` | #25 | **PPS Payer** (exact eCW names: Medicare, Medicare-Novitas, HCFA Medicare, HCFA Medicare Novitas, Humana Medicare, Devoted MCR UB, Cigna Healthspring, Cigna Health Spring/ Bravo — the two Cigna plans count although Commercial elsewhere) on a **UB** claim: billed (`lines.ends_billed`) **Qualifying Codes** → the **PPS Code** on the client's Medicare G code crosswalk (`CROSSWALK`): G0466 new office, G0466H new home, G0467 established office, G0467NH nursing home, G0467H established home, G0468 IPPE / AWV (G0402 / G0438 / G0439 win over an E&M). Right PPS Code billed → Pass. Missing → **added** via the Fee Schedule picker, Units 1 (LIVE-WRITE; Fail "CPT Code Missing (PPS code G0467 added (99213))"); VERIFY → Review "G0467 not added in VERIFY — add it by hand (99213)". **Review, nothing changed:** codes only on both the G0466 and G0467 lists ("new or established?"); conflicting visit codes; a different PPS Code billed (also beside the right one); mental health 90791 … 90845 ("G0469 or G0470?" until the client rules, EEWA-37; either one billed → Pass); a variant the picker cannot find; HCFA with a Qualifying Code; G0468 when #27–#29 found a repeat, could not read the history, or never checks it (the Cigna plans, Commercial there — operator, 2026-10-23). Dental → skipped (operator). `ac25f83` |
|
||
| 18 | `priced_line_check` | common rule | **LAST.** Unchecked line with Billed Fee > 0 → check |
|
||
|
||
**Every claim runs every condition above** — each one's `detect()` decides
|
||
from the claim's codes and payer, so a claim gets every "All payers"
|
||
condition plus its own payer's (operator's number-one rule, 2026-10-07 /
|
||
2026-10-12). **Dental included since 2026-10-12** (spec
|
||
`docs/superpowers/specs/2026-10-07-dental-claims-run-all-conditions-design.md`,
|
||
"as built" section): a Dental claim runs its high risk / sliding fee checks,
|
||
then the same read, Prog. Notes and `run_all`. The Dental grid has no POS and
|
||
no modifier columns, so a condition whose fix needs them (telehealth,
|
||
medicare_telehealth) or a new line (vaccine_admin_codes) declares
|
||
`DENTAL_UNSUPPORTED` and goes to **Review** on a Dental claim instead of being
|
||
applied. Dental claims are **never saved**, whatever `SAVE_CORRECTIONS` says
|
||
(operator, until a live verify run). Any condition whose CPT/HCPCS tab could
|
||
not be opened is **declined** (Review), not silently skipped; a condition that
|
||
raises is reported as failed (Review). Neither kind of claim is ever offered
|
||
for Save.
|
||
|
||
### Telehealth detection (`pages/claim_form.py`)
|
||
- Telehealth = "telehealth" or "telephone" in the note, or a `TH` token —
|
||
**except** a `TH` inside a `Modifiers:` list. That is the obstetric modifier
|
||
on in-person OB visits ("OB office visit … Modifiers: TH"), not telehealth
|
||
(found 2026-10-05 in saved notes; before this fix those visits got POS 2 + GT).
|
||
- Audio vs video, first match wins: explicit audio-only ("Mode of Treatment:
|
||
Telephone", "audio only", "no video", "(audio)", "phone call only") → audio;
|
||
explicit video ("audio and video", "video visit", "via video", "Mode of
|
||
Treatment: Video") → video; any other "telephone" → audio; otherwise video
|
||
(the document's default). "Audiology" or the consent line "phone and/or video"
|
||
decide nothing. The box shows the phrase that matched.
|
||
|
||
### Loading and reading waits
|
||
- Claim click times out after 5 s (was 30 s), then waits up to 5 s for the form.
|
||
- A claim form left open by the previous claim is cancelled before the next click;
|
||
if it will not close, that claim goes to Review instead of being read on the
|
||
wrong form.
|
||
- HCFA and Dental: wait (20 s) for the claim number to match and the CPT rows to
|
||
be filled and stable; not loaded → Review, conditions skipped. Wrong claim
|
||
number → error.
|
||
- Progress Note: polled up to `PROGRESS_NOTE_WAIT_MS` until its sections stop
|
||
changing; one re-click if nothing appears; a note that never settles is read
|
||
but marked partial (Review).
|
||
|
||
### What the operator sees (one box per claim)
|
||
- Corrected claim (SAVE off, `REVIEW_BEFORE_CLOSE=1`): **one** box, shown with
|
||
the form still open behind it: search result, what changed, any "Needs review"
|
||
lines, "nothing saved". The old before-apply box is gone (log line only).
|
||
- Every other claim: the per-claim box after close (`SHOW_EACH_CLAIM`), with
|
||
"Changed", "Needs review" and "Summary: <Category> — <Current Status>".
|
||
- Dental: the per-claim box carries the high-risk and sliding-fee results (no
|
||
separate box, no "?" fields; nothing when `SHOW_EACH_CLAIM=0`).
|
||
- Test runs prefix every box title with `[TEST] `. All box text is English.
|
||
|
||
### Dental checks (read-only)
|
||
Run first on a Dental claim, before the conditions (above). Results show in
|
||
the per-claim box and go into the same Charge Summary row as the condition
|
||
comments (§5): HIGH
|
||
RISK or a sliding fee EXPIRED / NOT_STARTED → **Fail**; sliding fee NONE is
|
||
noted in Comments (Pass); Billing Notes unreadable or sliding fee UNKNOWN →
|
||
**Review**.
|
||
- **#15 High risk** — "high risk" (any case/spacing) in Billing Notes
|
||
(`#dentalClaimNotes`) **and** at least 79 characters **from where "HIGH
|
||
RISK" starts to the end of the notes** (`dental_notes.high_risk_length`,
|
||
`HIGH_RISK_MIN_CHARS`; spaces and line breaks count, trailing blanks do
|
||
not; text before HIGH RISK does not count — 2026-10-21, was the whole
|
||
notes) → YES/NO. "High risk" in shorter notes → NO (not Review); the log gives
|
||
the length. Verified live on 4 Dental test claims: 1 YES, 3 NO; the
|
||
79 rule verified live 2026-10-13 (the YES claim's notes: 307 characters).
|
||
- **#8 Sliding fee** — only **Date from/to** vs claim DOS → ACTIVE / EXPIRED /
|
||
NOT_STARTED / NONE / UNKNOWN. Closes with Close + Cancel, never Save.
|
||
Verified live on the same 4 claims (all ACTIVE).
|
||
|
||
### Removed / not built
|
||
- **#9 Medicaid "D" codes** — built, then reverted on operator's instruction
|
||
(commit `6c44165`).
|
||
- **#1 Claim assigned to the patient → Claim Status Patient** — built
|
||
(`1c6b0f9`), reverted the same day (`5354c84`) on the operator's instruction.
|
||
- **#16 secondary Medicaid** — not needed (operator, 2026-10-21).
|
||
|
||
---
|
||
|
||
## 5. Charge Summary sheet (`core/charge_summary.py`)
|
||
|
||
24 columns, exactly the team's format (incl. their spellings
|
||
`Recevied date`, `Assigned To `):
|
||
|
||
- **A–R** from the downloaded report: 15 columns by the same header, plus
|
||
`S.No`, `Group Name` (payer_groups), `Pmts/Refunds = PMTS/ADJS − Adjustment`.
|
||
Report column `CLAIMTYPE` (U = UB, D = Dental, blank = HCFA) is dropped.
|
||
- **S–X** only for claims this run read: dates = run date, Assigned To =
|
||
setting, Comments per condition, Current Status, Category. Three categories
|
||
since 2026-10-05:
|
||
- **Review**, "Needs manual review": the bot could not check everything.
|
||
Triggers: search raised; form unreadable or never loaded; conditions not run
|
||
(`APPLY_CORRECTIONS=0`); progress note not read or partial; zero CPT or zero
|
||
ICD rows read; a condition failed or declined; an approved save eCW did not
|
||
confirm; Dental notes unreadable or sliding fee UNKNOWN. Comments = findings
|
||
then reasons; "Corrections SAVED to eCW" leads when the claim was saved.
|
||
Review wins over Fail.
|
||
- **Fail**: a condition changed something (its comment comes from what
|
||
actually happened, e.g. "G2025 NOT added — add by hand"), or Dental HIGH RISK
|
||
/ sliding EXPIRED / NOT_STARTED. "Ready to Submit" if saved, else
|
||
"Corrections pending (not saved)".
|
||
- **Pass**: "Clean claims (…)", Ready to Submit.
|
||
- A claim the run never reached or did not find stays blank in S–X.
|
||
- Same-day re-run keeps the old file as `…__HHMMSS.xlsx`. If the old file is
|
||
open in Excel (rename refused), the old file is left alone and the NEW sheet
|
||
is saved as `…__HHMMSS.xlsx` instead (D10, 2026-10-05); the `[SUMMARY]` log
|
||
line names the file actually written, and a box tells the operator the new
|
||
name. Same for `Medcor Ready_<date>.xlsx` (its box only with
|
||
`NOTIFY_UNMAPPED_PAYERS=1`).
|
||
- A `TEST_CLAIM_IDS_FILE` run writes `Charge Summary_TEST_<date>.xlsx` (D1).
|
||
- Written after **every claim** (operator, 2026-10-12), not once at the end.
|
||
The first claim's write keeps an earlier same-day file aside as above; every
|
||
later claim rewrites this run's own file (`charge_summary.write(into=...)`,
|
||
no `__HHMMSS` copy per claim). Rows of claims not done yet stay blank in
|
||
S–X until their claim is done. If the operator opens the in-progress file in
|
||
Excel, the rest of the run goes to a new `__HHMMSS` name. Each box (new
|
||
name, write error) shows once per run, not per claim.
|
||
Test: `tests/test_summary_per_claim.py`.
|
||
|
||
### Payer groups (since 2026-10-06)
|
||
- `src\ecw_bot\payer_groups.json` holds the team's template: every distinct
|
||
`Payer Name` → `Payer Group Name` pair from the Claim Status Dashboard
|
||
(`\\mecare2\Production\Salud-eCW RPA\Claim Status Dashboard Details 1001.xlsx`),
|
||
267 payers, plus 5 RAE spellings the dashboard does not list. Payer and group
|
||
names only, no patient data.
|
||
- Refresh it when the dashboard changes, from PowerShell (Git Bash cannot
|
||
reach the share): `python tools\import_payer_groups.py` (`--dry-run` lists
|
||
what would change without writing).
|
||
- Group Name columns (`Medcor Ready_<date>.xlsx`, Charge Summary) show the
|
||
dashboard's names: Commercial Ins, Medicaid, RAE (Behavioral Health),
|
||
Dental - CHP+/DentaQuest, Dental - Commercial, Medicare, CHP+, Self Pay,
|
||
Patient...
|
||
- Conditions decide on `payer_groups.rule_group()`: Commercial Ins → Commercial,
|
||
RAE (…) → RAE payer, Dental - CHP+/DentaQuest → Dentaquest, Self Pay →
|
||
Program, Payer Not In Group → unmapped; others pass through. CHP+, Patient
|
||
and the Dental groups have no telehealth rule (Review); the vaccine rule now
|
||
applies to them (document #18: all payers except Medicare).
|
||
- One-off sheet from the 21-Sep report: `Output\Charge Summary_09-21-2026.xlsx`
|
||
(1,145 claims; 301 with blank Group Name — unmapped payers). With the
|
||
dashboard table the same report has **0** blank Group Names.
|
||
|
||
### Results database + web page (since 2026-10-08)
|
||
The same Charge Summary rows also go to SQL Server, and a local page shows
|
||
them. Spec: `docs/superpowers/specs/2026-10-08-results-db-and-web-page-design.md`.
|
||
**Portal look since `a436489` (2026-10-26, operator: "konjam attractive,
|
||
content add, grid correct, header footer align"):** header, breadcrumb,
|
||
page and footer share one 1180px column; header has a "CV" mark,
|
||
tab-style links with the current page marked (`aria-current`), a user
|
||
avatar; a footer at the bottom of every page (brand, PHI note, mode).
|
||
Dashboard adds the pass rate on Checked today, a **Last 7 days** Pass /
|
||
Fail / Review chart (a day links to its page), **Today's Fail and Review
|
||
reasons** (comments grouped with counts; the extra `day_results` query
|
||
runs only when today has claims) and three equal cards (Claim status,
|
||
Runs, Find a claim). Motion is movement only, never opacity (a tab the
|
||
browser holds in the background must not stay faded), and off under
|
||
`prefers-reduced-motion`; still no inline `style=` attributes (a test).
|
||
Checked in the Browser pane against a made-up-data preview (port 8765,
|
||
a scratchpad script; no SQL, no real users), desktop and phone width.
|
||
The running Portal shows it after `run_web.bat` is restarted.
|
||
**Sign-in page since `61338b4` (2026-10-26, operator):** a card on a
|
||
slow navy-to-blue moving gradient — navy brand panel ("Charge
|
||
Validation" and three feature titles only, no description lines;
|
||
floating circles, pulsing CV mark) and the form (icon fields, Show /
|
||
Hide password, Caps Lock warning, idle / lockout rules read from
|
||
`web/users.py`). Every page has an inline SVG tab icon (no
|
||
`/favicon.ico` 404). Motion off under `prefers-reduced-motion`. Live on
|
||
the Portal (restarted 2026-10-26, [portal] terminal tab).
|
||
- Server `MECARE331\MECARE331`, database `Charge_Validation`, table
|
||
`dbo.CV_ChargeSummary` (every table of this project starts with **`CV_`**,
|
||
operator 2026-10-12): the format's 24 columns + `run_at`, `run_mode`
|
||
(VERIFY / LIVE-WRITE, or TEST VERIFY / TEST LIVE-WRITE since 2026-10-12),
|
||
`details_json` (conditions fired, before/after
|
||
changes, review reasons, Dental results). Amounts → DECIMAL, dates → DATE,
|
||
unconvertible → NULL.
|
||
- `core/results_db.py`; the flow calls `_save_results_db` after each
|
||
claim's Excel write, with that claim's row only (since 2026-10-12). Every
|
||
row of one run shares one `run_at` (kept on the run's `info` dict), so the
|
||
page shows it as one run. Only rows the run filled in (a Category) are
|
||
saved; a claim not found saves nothing. `RESULTS_DB=1`
|
||
turns it on (default off); `RESULTS_DB_CONN` (default: Windows login, no
|
||
password). A DB error is one `[RESULTS-DB]` log line; the run and the Excel
|
||
are unaffected.
|
||
- Setup (once): run `tools/sql/create_tables.sql` in SSMS; give the Windows
|
||
login db_datareader + db_datawriter (commented lines at the end of that file).
|
||
**Done 2026-10-12** on MECARE331: table created, grants run for
|
||
`MFILESERVER\EC10980`, renamed to `CV_ChargeSummary` with
|
||
`tools/sql/rename_to_cv_prefix.sql`. Checked read-only from the bot's own
|
||
connection: 28 columns, types / widths / indexes match, 0 rows, the web
|
||
repo reads it.
|
||
- **Portal (2026-10-19, `2da2bdd`)**: `run_web.bat` → http://127.0.0.1:8000
|
||
(local only, PHI). Operator: a second run hid the first (the old page
|
||
opened on the newest run only), and the front page was three quarters
|
||
claims table. Spec / plan:
|
||
`docs/superpowers/specs/2026-10-19-results-portal-redesign-design.md`.
|
||
- **Dashboard `/`**: Bot run card (below) with a **Live claims** panel
|
||
(every claim of the running / last run: result, Checking, Waiting; Not
|
||
finished / Not checked once over; a saved result opens its day page),
|
||
today's tiles, **Find a claim**, a big **Claim status** button. No
|
||
claims table.
|
||
- **Claim status `/days`**: one card per day a run STARTED (all runs of
|
||
a day together, last 90 days), newest first: claims checked, runs,
|
||
Pass / Fail / Review, colour bar.
|
||
- **One day `/day/YYYY-MM-DD`**: every row of that day's runs; tiles
|
||
filter by Category; claim # search; short table (#, Claim #, Time,
|
||
Payer, Result, Comments), 10 a page, no sideways scroll; a row opens
|
||
every field + conditions JSON in a side panel (empty fields hidden);
|
||
`?claim=<no>` opens that claim.
|
||
- **Find a claim `/claim?claim_no=`**: every saved check (newest first,
|
||
200 max), each linked to its day.
|
||
- Navy & blue theme, breadcrumb, VERIFY / LIVE-WRITE pill
|
||
(`templates/base.html`). Old `/?run_at=` links redirect to the run's
|
||
day. JSON: `/api/runs`, `/api/results`. The same claim checked twice
|
||
in a day shows twice; "Not found" claims have no saved row.
|
||
- Fed by the bot's `[CLAIMS] Queue: ...`, `[CLAIMS] Results run_at:
|
||
<ISO>` (the saved run's time, RESULTS_DB=1 only) and one
|
||
`[RESULT] claim n/N <id>: <Category>` line per claim.
|
||
- Bot run card (2026-10-14): **Start run** / **Stop** / live status —
|
||
`POST /api/bot/start`, `POST /api/bot/stop` (Origin must be this page and
|
||
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`.
|
||
|
||
---
|
||
|
||
## 6. Rulings and decisions (operator, keep these)
|
||
|
||
- 2026-09-29 Vaccine admin units: always 1 (document says count-based; client to confirm).
|
||
- 2026-09-30 Common rule: zero fee checked → uncheck; priced unchecked → check (both halves, first/last).
|
||
- 2026-09-30 A checked row billed at 0 does not count as billed (vaccine, depression agree with it).
|
||
- 2026-09-30 Depression duplicates: keep the first **priced** G8510/G8431.
|
||
- 2026-09-30 Sliding fee: Dental only; compare only the date range with DOS.
|
||
- 2026-09-30 High risk / sliding fee: show in a message box for now; Excel later (now in the summary).
|
||
- 2026-10-01 Condition #9 not needed — reverted.
|
||
- Client: J2930 gets no replacement code (just uncheck + zero).
|
||
- Client: Z13.31 is the only depression pointer (no Z13.32).
|
||
- 2026-10-05 Review steps 1–6 approved and done (commit `f5bf3f9`). Rulings taken in that work:
|
||
- `SAVE_CORRECTIONS` code default 0; `APPLY_CORRECTIONS` stays 1 (it only fills the form, closed with Cancel).
|
||
- PHI/client files in the repo root are gitignored in place, not moved.
|
||
- A `TH` in a `Modifiers:` list is the OB modifier, not telehealth.
|
||
- Audio-only (93) needs explicit evidence; "telephone" still means audio; default is video.
|
||
- A telehealth note on a payer with no modifier rule is Review, never a guessed modifier.
|
||
- A claim with a failed / declined condition or an unread note is never offered Save.
|
||
- 2026-10-06 Payer groups come from the team's **Claim Status Dashboard** (`Payer Name` → `Payer Group Name`), not a hand-made list.
|
||
- 2026-10-06 Clean-claim comment: "Clean claims (claims that have no errors, placed in Pending with Errors)"; other claims keep their condition comments.
|
||
- 2026-10-06 Dummy codes are **DCONS and Dscprit only** (uncheck + fee 0, any payer). BHT3/BHT4 are **not** dummy codes.
|
||
- 2026-10-14 Results page can start the bot: **VERIFY only** (LIVE-WRITE stays terminal-only); information boxes off for a page run (log `[BOX]` lines), boxes needing a person stay on; one live run at a time (a second refuses before login, exit 2).
|
||
- 2026-10-16 #19 Medicare: only G0008 / G0009 trigger condition code A6 (not #18's vaccine list; G0008/G0009 are not added); UB first empty Condition Code box; HCFA → Review by hand.
|
||
- 2026-10-21 #19 (`0f090e8`): the HCFA condition codes need no mapping — Medicare claims are billed on UB (#2); a Medicare G0008/G0009 claim on HCFA stays Review (wrong form). #19 is complete.
|
||
- 2026-10-16 VERIFY writes nothing to eCW (option C): a CPT line's Assign To Patient box is set on screen only (no click, so eCW's save does not run) and no CPT row is added — a code that would be added is Review "add by hand". LIVE-WRITE still clicks and adds.
|
||
- 2026-10-16 #13 Dental codes only to DentaQuest / Delta Dental / CHP+ Dental (the document's three; not other Dental payers); otherwise Review + comment (check program, then Submit to Patient) — the bot does not change the Claim Status yet.
|
||
- 2026-10-21 #1 (Claim Status → Patient for a claim whose every priced line is unticked) — built in `1c6b0f9`, then **reverted the same day (`5354c84`) on the operator's instruction: #1 is not needed**. priced_line_check ticks every unticked priced line again, as before.
|
||
- 2026-10-21 #16 (secondary Medicaid) — **not needed** (operator).
|
||
- 2026-10-21 #13 (`df74331`): a secondary (any insurance row) DentaQuest / Delta Dental / CHP+ Dental counts; with none, the bot sets the **Claim Status to Patient (PAT)** itself, no program check (option A). Written only where the run may write (LIVE-WRITE, not the Dental screen); elsewhere Review "set it by hand". Claim Status DOM (HCFA / Dental `ClaimData.ClaimStatus`, UB `selectedClaimStatusCodes`) from the claim form DOMs the operator pasted.
|
||
- 2026-10-19 Results portal: Dashboard (Bot run card, today's tiles, Find a claim, Claim status button — no claims table on the front page) → Claim status = date cards (all runs of a day together) → one day = short table + side panel; navy & blue theme; live claims in the Bot run card; search one claim over all dates.
|
||
- 2026-10-15 Results page table: claim number and Category always on screen; a Claim status button lists every claim of the run; one vertical scroll only, 10 claims a page, column slider above the table.
|
||
- 2026-10-07 Every claim runs every "All payers" condition **plus** the conditions for its own payer (Medicare, Medicaid, Dental...) — the same for all payers, Dental included.
|
||
- 2026-10-07 #14 Medicare: G8510/G8431 unchecked + fee 0, no other code touched; the depression-pointer process (#6) does not run on Medicare; screening only → also Review (provider clarification).
|
||
- 2026-10-12 #14 on a UB form with no Assign-To-Patient checkbox: zeroing the fee is enough (no Review); the comment says "fee zeroed", not "unchecked".
|
||
- 2026-10-07 #12 H codes: payers whose name starts with "BH" only; every modifier on the H code line is removed; telehealth visits excluded (GT stays).
|
||
- 2026-10-12 #12 H codes are billed to BH payers only: on any other payer (Medicare, Medicaid, Commercial ...) the H code line is unchecked and its fee zeroed — telehealth included.
|
||
- 2026-10-12 #14 stays G8510 / G8431 only (not G8511 / G8432).
|
||
- 2026-10-12 **Every database table of this project starts with `CV_`** (results table: `dbo.CV_ChargeSummary`).
|
||
- 2026-10-12 Number-one rule, restated: every claim = every "All payers" condition + its own payer's conditions; Dental claims too (now built). A fix the Dental screen cannot hold → Review; Dental claims are never saved for now.
|
||
- 2026-10-12 Charge Summary (Excel + DB) is written after every claim, not at the end of the run; Comments keep the per-condition wording (Clean / depression pointer / invalid codes ...).
|
||
- 2026-10-12 **One claim's problem never costs the next claim.** Before every claim the screen is cleared (extra tabs, note viewers, a leftover form by Cancel, stray panel) and Claim No + Lookup are verified on screen and uncovered. A claim that hits a problem or a page that will not clear → that claim is Review in the summary (with the reason) and the run moves to the next claim.
|
||
- 2026-10-12 #15 High risk = "HIGH RISK" in the Billing Notes **and** the whole notes 79 characters or more (spaces included). "HIGH RISK" in shorter notes → not high risk (not Review).
|
||
- 2026-10-21 #15 (`da8acdb`): the 79 characters are counted **from "HIGH RISK" to the end of the Billing Notes** (later lines included), not the whole notes; text before it (an earlier line) does not count.
|
||
- 2026-10-13 #43 / #44: on a Medicaid claim 15851, 15852 and 73521 are unchecked and their fee set to 0 — one condition, `medicaid_uncheck_cpts`.
|
||
- 2026-10-21 Selector breaks: no live "AI healer" (sending a page's DOM to an AI service mid-run sends PHI) until a HIPAA approval (BAA / zero data retention) exists; even then read / navigation steps only, never a write step. First: fallback selectors + automatic DOM/screenshot capture on a break + a pre-flight selector check, fixed offline in Claude Code.
|
||
- 2026-10-21 UHC Community ("UHC Community Plan" / "... Do not use") is **Medicare** — `payer_groups.json` stays as it is: #20 adds no 96372 there, #14 unchecks G8510. The client's #20 example on that payer does not apply.
|
||
- 2026-10-21 #22 (`21038b1`) Medicare Advantage plans (`AARP Medicare Advantage`, `Aetna Medicare ADV`) follow the dashboard: **Commercial** for every condition (`payer_groups.RULE_OVERRIDES` now empty). So telehealth gets #21 93/95, not #22 G2025; no #14 G8510/G8431, #19 A6 or #27-#29 history check; #18 vaccine admin and #20 96372 are added.
|
||
- 2026-10-20 #23: Medicaid only (not RAE); Z32.00/Z32.01/Z32.02 (pregnancy test) never get TH; TH goes in M1; a Medicaid telehealth pregnancy visit gets TH, not GT.
|
||
- 2026-10-21 #23 confirmed (`8f5a4a4`): pregnancy ICD = O*, Z33, Z34, Z36, Z3A (no progress-note keywords); TH in M1 of the first billed E&M line (none → first billed line); M1 in use → **overwritten** with TH, not moved to M2-M4. TH already in M2-M4 → left alone (bot's choice: no second TH).
|
||
- 2026-10-23 #25 (operator, spec EEWA-34): PPS Payers by exact eCW name (list in §4 row 17); the two Cigna plans are an exception to the 2026-10-21 #22 Medicare Advantage = Commercial ruling, other "Medicare" group payers are not in #25. UB only; HCFA → Review; Dental → skipped. Billed lines only. New vs established decided by a code on one list only; both-lists codes alone → Review. Conflicting codes → Review. G0468 wins over an E&M. One PPS Code for the medical visit, one for mental health. Different PPS Code billed → Review, never overwritten. LIVE-WRITE adds Units 1 through the picker; VERIFY adds nothing. G0468 not added when #27–#29 found a repeat — nor when the history is not checked (the Cigna plans: #27–#29 never runs on them) → Review (operator, later the same day).
|
||
- 2026-10-20 #27/#28/#29 (client, via the operator): G0402 / G0438 / G0439 come on Medicare claims only. The bot only CHECKS the history in Account Inquiry and writes the error in the Charge Summary Comments — never adds, changes or unchecks. G0402 billed before at any time; G0438 inside 3 years; G0439 inside 12 months → error (Review). Window from this claim's service date.
|
||
- 2026-10-20 #48: DFIMP (the live Dental grid's "FINAL IMPRESSION" code) is the same dummy code as the document's DPIMP — both handled; notes and the box name the code found.
|
||
- 2026-10-13 #48: DPIMP is a dummy code on **Dental payers** → uncheck + fee 0, in its own file `dental_dummy_codes` (#8 unchanged). DPIMP on any other payer → not changed, noted in the Comments (the claim is Review).
|
||
- 2026-10-14 An HCFA claim form Cancel will not close before the next claim (or one still taking no clicks after the 10 s wait): Escape as the last resort (HCFA window only; UB / Dental left as they are), logged loudly — so one stuck form cannot send the rest of the run to Review.
|
||
- 2026-10-13 #18 vaccines are the client's 19-code list only, admin codes 90460/90461/90471/90472 only; a vaccine code off the list is left alone (no note).
|
||
- 2026-10-14 #43/#44 (`medicaid_uncheck_cpts`): Medicaid payers only — NOT the RAE (BH) payers, CHP+ or any other group. As built; no code change.
|
||
- 2026-10-21 #44: only **73521** is unchecked on Medicaid, not every imaging code (the client examples' 76801 / 76805 are not added). As built; no code change.
|
||
- 2026-10-13 #20 injection codes: the client's 41-code list as given; 96372 only; always Units 1; Medicare excluded.
|
||
- 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.
|
||
|
||
### 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).
|
||
3. Dental HIGH RISK / sliding EXPIRED rows say "Corrections pending (not saved)" although nothing was corrected. Needs the operator's wording (e.g. "Submit via DentaQuest portal").
|
||
4. Demo `.env`: which sheet `TEST_CLAIM_IDS_FILE` points at (now the Dental sheet), and a shorter `MSGBOX_TIMEOUT_SECONDS`?
|
||
5. #25 mental health: G0469 or G0470 (new / established) — the client's ruling (EEWA-37). Until then Review.
|
||
6. Sliding fee renewed: a claim whose DOS falls in the patient's **previous** sliding fee period, checked after a renewal — Pass on the old period, or Fail "NOT STARTED" against the current one (as built)? Seen 2026-10-26. Billing team / client to rule.
|
||
|
||
---
|
||
|
||
## 7. Working agreements
|
||
|
||
- **Never commit or push unless the user asks at that moment.** No remote exists.
|
||
- Never commit `run.py` (has an unrelated local change), `Dom.txt`,
|
||
`Charge Condition Document.xlsx`, `Charge Summary File Format.xlsx`, `~$*` lock files.
|
||
- Jira posts (comments, tickets) carry no claim ids and no patient data.
|
||
- `Context.md` is tracked in git, so keep it free of claim ids, patient data
|
||
and credentials.
|
||
- **Every tracked file, not only `Context.md`: no real claim id, no
|
||
patient or provider identifier.** Code comments, selectors, fixtures and
|
||
tests use synthetic values (claim ids `91xx000`, `9000001`, `8000014`;
|
||
the patient "Sample, Pat Q"). A scan on 2026-10-23 found 33 real claim ids
|
||
in 52 tracked files and one captured patient's identifiers (name, DOB,
|
||
account number, address, phone, subscriber number) plus two provider
|
||
names in the UB / progress-note fixtures and their tests — all replaced
|
||
(synthetic, not reversible; the tests unchanged otherwise). **The local
|
||
git history still holds them: never push this history.** A remote (Gitea)
|
||
gets a fresh single-commit snapshot of a scanned tree only (§10).
|
||
- `Input\`, `Output\`, `Logs\`, `Screenshots\` contain PHI — gitignored; never paste their contents.
|
||
- Never print or type credentials from `.env`.
|
||
- Keep `SAVE_CORRECTIONS=0` until the client approves writes.
|
||
- Live eCW inspection is read-only; close claims with Cancel, never OK.
|
||
- User writes in Tanglish; answer in Tanglish.
|
||
- New eCW screens are mapped from a DOM dump the user pastes into `Dom.txt`.
|
||
|
||
---
|
||
|
||
## 8. Git state
|
||
|
||
History: `git log --oneline`.
|
||
|
||
Last code commits: `1da9e7c` (2026-10-27, Portal sign-in with the Ecare
|
||
office login — §2), `cc3a01a` (2026-10-27, Portal container on India time —
|
||
§10), `69c51d8` (2026-10-27, Dockerfile: libgssapi-krb5-2 for ODBC Driver
|
||
18 — §10), `70ba709` (2026-10-27, Portal server mode for Dokploy — §10), `f99dc17` (2026-10-27, Portal roles Super Admin /
|
||
Admin / Viewer — §2), `61338b4` (2026-10-26, Sign-in page redesign + tab
|
||
icon — §5), `a436489` (2026-10-26, Portal look: aligned header /
|
||
footer, 7-day chart, today's reasons, pass rate — §5), `2d4d4fc`
|
||
(2026-10-23, tracked files de-identified — §7), `8fea168` (2026-10-23, "Chrome open" box before the
|
||
login and the empty first tab closed, in a Chrome the run opened — §2),
|
||
`1c7eb55` (2026-10-23, #25: G0468 → Review when the preventive visit
|
||
history is not checked — the Cigna plans), `ac25f83` (2026-10-23, #25 `medicare_pps_code` — PPS
|
||
Code for PPS Payers; `medicare_wellness_history` leaves its verdict for it;
|
||
EEWA-35/36, §4 row 17), `0f16560` (2026-10-22, claim watchdog — exit 8; Start
|
||
opens Google Chrome itself and closes it after; proxy check on an attached
|
||
Chrome; tests isolated from the real `BROWSER_CDP_URL`, §2), `37c214d`
|
||
(2026-10-22, the Dashboard says "the Portal
|
||
is not reachable — is run_web.bat still running?" when a Start or a status
|
||
poll cannot reach the server; a Start used to do nothing), `57d22fb`
|
||
(2026-10-22, `auto_dialog` answers every
|
||
dialog — Cancel a confirm in VERIFY, §10), `ecbbf3a` (2026-10-22, last-run
|
||
banner and
|
||
`CV_ChargeSummary.started_by` — EEWA-33 Stage 3, §2), `c230a8e` (2026-10-22,
|
||
Portal Sign-in, roles, Started
|
||
By / Stopped By — EEWA-32 Stage 2, §2), `9a07f74` (2026-10-22, events file
|
||
closed at the end of a Run; tests keep events files out of the repo),
|
||
`751a177` (2026-10-22, Run events + Runs page — EEWA-31
|
||
Stage 1, §2), `8af5c14` (2026-10-22, attach to the operator's Chrome
|
||
over CDP — `BROWSER_CDP_URL`, eCW cookies cleared at start and end, §2),
|
||
`5354c84` (2026-10-21, #1 reverted — not needed), `df74331` (2026-10-21, #13 Claim Status set to Patient,
|
||
secondary insurance counts, EEWA-27), `da8acdb` (2026-10-21, #15 high risk 79 characters from
|
||
"HIGH RISK" to the end), `a6c01d6` (2026-10-21, #27-#29 Account Inquiry launcher
|
||
from the real DOM), `8f5a4a4` (2026-10-21, #23 TH always in M1, overwriting),
|
||
`21038b1` (2026-10-21, #22 Medicare Advantage = Commercial for every
|
||
condition, RULE_OVERRIDES empty), `c0e1a75` (2026-10-21, selector resilience — fallback
|
||
selector chains, pre-flight check (exit 7), DOM saved on every failure shot), `9b87b4d` (2026-10-20, #23 medicaid_pregnancy_th — TH on a
|
||
Medicaid pregnancy visit, TH not GT on telehealth, EEWA-30), `686ed0c` (2026-10-20, #27-#29 medicare_wellness_history —
|
||
G0402/G0438/G0439 history check in Account Inquiry, EEWA-29), `1dffa10` (2026-10-20, eCW password typed key by key —
|
||
fill() left Log In dead after the password change; #48 DFIMP), `2da2bdd` (2026-10-19, results portal — Dashboard, Claim
|
||
status by day, day page, Find a claim), `6005b7d` (2026-10-19, code
|
||
review fixes: lines.ends_billed for #13/#19, A6 read-back and visible UB
|
||
form only, VERIFY add-by-hand wording, Dental never written, P16 docs),
|
||
`6457100` (2026-10-16, #19 medicare_vaccine_a6 — A6
|
||
for G0008/G0009 on Medicare, UB), `57e2f5a` (2026-10-16, VERIFY writes nothing to eCW —
|
||
boxes set on screen only, no CPT row added), `d74b7b3` (2026-10-16, #13
|
||
dental_payer_check — Review only), `11b1032` (2026-10-15, results page: one vertical
|
||
scroll, 10 claims a page, column slider), `5e8c76a` (2026-10-15, results page: claim columns
|
||
pinned, claim side panel, Claim status button, `[CLAIMS] Queue:` line),
|
||
`1d79ac3` (2026-10-15, page run reads `.env` afresh, page's results DB),
|
||
`f2d29e5`..`665ca7e` (2026-10-14, start / stop the bot from the results
|
||
page: run record, `[RESULT]` line, `QUIET_BOXES`, run status, bot runner,
|
||
Start/Stop card; branch feature/start-bot-from-page fast-forwarded),
|
||
`a28ffac` (2026-10-14, HCFA form Cancel will not
|
||
close → Escape as the last resort in clear_screen), `ec150bf` (2026-10-14, review fixes: search page waits
|
||
up to 20 s for eCW to finish loading; a claim window is never Escaped by
|
||
the stray-panel check; no duplicate 96372), `719d185` (2026-10-13, #18 vaccine code list + #20
|
||
injection admin 96372), `f8216a7` (2026-10-13, claim windows that take no
|
||
clicks: Chrome flags + settle wait), `c138c48` (2026-10-13, #48 DPIMP on Dental
|
||
payers), `b7d078b` (2026-10-13, #43/#44 Medicaid
|
||
15851/15852/73521 unchecked + fee 0), `3d12518` (2026-10-12, #15 High risk needs
|
||
79-character Billing Notes), `77fdffd` (2026-10-12, note viewers counted by
|
||
clicks + leftover-form diagnostic), `a9dcc63` (2026-10-12, stacked dialogs + clean
|
||
search page before every claim), `ebc4382` (2026-10-12, results table renamed
|
||
`CV_ChargeSummary`), `cef7666` (2026-10-12, Dental claims run every
|
||
condition), `48a6940` (2026-10-12, low-severity review hardening),
|
||
`449813e` (2026-10-12, #12 H code off a BH payer unchecked + zeroed), `a979eb8` (2026-10-12, review fixes for #12/#14),
|
||
`27b8f39` (2026-10-12, summary written claim by claim),
|
||
`8fbd20d` (2026-10-08, results DB + web page) and `394d01e` (2026-10-08,
|
||
DCONS/Dscprit, #14 Medicare screening, #12 BH H codes). Uncommitted
|
||
(check `git status`):
|
||
- `run.py`: local change, never commit (§7).
|
||
- `tests/test_guard.py`: the operator's local change, not ours.
|
||
- `src/ecw_bot/claim_file.py` (one-line change) and untracked
|
||
`tools/sql/Create_CV_Table.rpt`: present before 2026-10-22's CDP work,
|
||
not part of it; ask the operator before committing.
|
||
- Root `Dom.txt`, client `.xlsx` files and `~$` lock files no longer show in
|
||
`git status`: they are gitignored, still in place.
|
||
|
||
## 9. Jira (project EEWA, parent EEWA-5 "Charge Validation")
|
||
|
||
Status lives in Jira; the open work is in §6 and §10.
|
||
|
||
| Key | Task | Status (2026-10-16) |
|
||
|---|---|---|
|
||
| EEWA-8 | Medicare Telehealth | Done |
|
||
| EEWA-10 | j2930_j2001_update | Done |
|
||
| EEWA-11 | dummy_codes | Done (2026-10-12 comment corrects the old BHT3/BHT4 one: DCONS/Dscprit only) |
|
||
| EEWA-12 | vaccine_admin_codes (2026-10-13 comment: client's 19 vaccine codes, 4 admin codes) | In Progress |
|
||
| EEWA-13 | sliding_fee | Done |
|
||
| EEWA-14 | dental_high_risk (incl. the 79-character rule) | Done |
|
||
| EEWA-15 | bh_h_code_modifier (#12 H codes) | Done (closed 2026-10-20) |
|
||
| EEWA-16 | medicare_billing_screening (#14; rulings pending) | Done (closed 2026-10-20) |
|
||
| EEWA-17 | Results database and web page | Done (closed 2026-10-20) |
|
||
| EEWA-18 | Claim-by-claim run stability (clean page, stacked dialogs, ghost form) | Done (closed 2026-10-26: 50/50 runs 22/10 and 23/10) |
|
||
| EEWA-19 | medicaid_uncheck_cpts (#43 15851/15852, #44 73521; Medicaid only, not RAE — ruled 2026-10-14) | Done (closed 2026-10-20) |
|
||
| EEWA-20 | dental_dummy_codes (#48 DPIMP on Dental payers) | In Progress |
|
||
| EEWA-21 | injection_admin_code (#20 add 96372; only the operator's 41 codes — ruled 2026-10-14) | In Progress |
|
||
| EEWA-22 | Log messages use first_line (review cleanup) | To Do |
|
||
| EEWA-23 | Run record: one place for its start/end time logic (review cleanup) | To Do |
|
||
| EEWA-24 | Run record: one Windows "running process" query (review cleanup) | To Do |
|
||
| EEWA-25 | Results page: live check of Stop on a VERIFY run | To Do |
|
||
| EEWA-26 | RunStatus type from runner to status to page (blocked by EEWA-23) | To Do |
|
||
| EEWA-27 | dental_payer_check (#13 Dental codes only to DentaQuest / Delta Dental / CHP+ Dental; Claim Status → Patient since 2026-10-21) | To Do |
|
||
| EEWA-28 | medicare_vaccine_a6 (#19 G0008/G0009 on Medicare → condition code A6; UB done, HCFA Review — no mapping needed, Medicare = UB) | To Do |
|
||
| EEWA-30 | medicaid_pregnancy_th (#23: TH modifier on a pregnancy visit on Medicaid) | To Do |
|
||
| EEWA-29 | medicare_wellness_history (#27/#28/#29: G0402 / G0438 / G0439 billed too often → Review) | To Do |
|
||
| EEWA-31 | run_events + Runs page (Stage 1) | Done (closed 2026-10-26; built `751a177`, seen on every live run since 2026-10-22) |
|
||
| EEWA-32 | portal sign-in (Stage 2: Portal Users, Roles, Started By / Stopped By); blocked by EEWA-31 | In Progress (built 2026-10-22, `c230a8e`; live Sign-in and a Portal Start with Started By 2026-10-22; roles Super Admin / Admin / Viewer `f99dc17` 2026-10-27; Viewer not yet tried) |
|
||
| EEWA-33 | portal run alerts (Stage 3: Run Error banner, `started_by` column); blocked by EEWA-32 | Done (closed 2026-10-26; built `ecbbf3a`; banner seen after the 19:42 run, `started_by` on the Portal runs' rows) |
|
||
| EEWA-34 | medicare_pps_code (#25) — spec | To Do (parent of the work below) |
|
||
| EEWA-35 | #25 PPS Code check — Review when missing | In Progress (built 2026-10-23, `ac25f83`; not run live) |
|
||
| EEWA-36 | #25 add the PPS Code in LIVE-WRITE; blocked by EEWA-35 | In Progress (built 2026-10-23, `ac25f83`; not run live) |
|
||
| EEWA-37 | #25 mental health G0469 / G0470 — after the client's ruling | To Do (waiting on the client) |
|
||
|
||
EEWA-15 to EEWA-18 were created on 2026-10-12 for work already built and
|
||
moved to In Progress on 2026-10-13; EEWA-19, EEWA-20 and EEWA-21 were
|
||
created and moved to In Progress on 2026-10-13. EEWA-22..26 were created
|
||
on 2026-10-15 from the code review's leftovers (`/to-tickets`), as
|
||
**Subtasks** of EEWA-5 (EEWA-5 is a Task, not an epic: a Task under it
|
||
is refused), label `ready-for-agent`. How skills use Jira:
|
||
`docs/agents/issue-tracker.md`. EEWA-21 has the 2026-10-14 #20 ruling as
|
||
a comment. New tickets are assigned to Manikandan. The older descriptions of EEWA-13/14 (2026-10-01) name
|
||
test claim ids — new posts must not (§7).
|
||
|
||
---
|
||
|
||
## 10. Open items
|
||
|
||
- **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
|
||
in: about half the runs never passed the login — "No second factor
|
||
prompted / Login not confirmed" (four on 20/10, not since), a late
|
||
2FA mail (372 s once, one 9-minute timeout), the 19:42 restriction
|
||
(fixed by the proxy check), guard waits, and stops by hand. Two runs
|
||
stopped at "Opening landing page" (Chrome takes ~2 min there); the
|
||
watchdog came after them and has not fired live yet. Suggested, not
|
||
ticketed: a landing-page timeout with one retry, a 2FA retry, a week of
|
||
daily runs to measure login times on the Runs page.
|
||
- **Login retry bug (seen 2026-10-27 16:50, not fixed).** After a 2FA
|
||
timeout the run closes the Chrome it opened and attempt 2 attaches to
|
||
9222 at once, while that Chrome is still closing → `connect_over_cdp:
|
||
Target page, context or browser has been closed`, run over. Fix: wait
|
||
until 9222 stops answering, then open a fresh Chrome (test-first).
|
||
- **Gitea:** one overall snapshot update at the end of a day, when the
|
||
operator asks (from the clone `D:\ecw-charge-validation`; operator
|
||
pushes; 2026-10-26: `ba15a57` → `120fd68`).
|
||
- **Portal for the client — Gitea + Dokploy (asked 2026-10-23; LIVE on
|
||
Dokploy 2026-10-27, `cc3a01a`, Ecare network only).** URL
|
||
`http://10.200.0.15:8085` (Dokploy project Ecw_ChargeValidation →
|
||
production → app **Portal**; port 8085 → 8000). The operator signed in
|
||
there as Super Admin; Dashboard, Users, Sign-ins work. How it got there:
|
||
- **Source = Drop (zip upload), not Git:** Dokploy cannot clone from
|
||
Gitea (`server certificate verification failed`, Traefik's self-signed
|
||
cert); Gitea has no internal http port and no SSH; the operator cannot
|
||
open Dokploy Settings → Git Providers; the OTS-Team Gitea account does
|
||
not see `automation-tools`. So: `git archive --format=zip HEAD
|
||
Dockerfile requirements-portal.txt src web` → `D:\ecw-portal-deploy\
|
||
portal-<hash>.zip` → app → General → Provider → **Drop** → select →
|
||
the **Deploy under the drop box** (the top Deploy / Reload rebuilds the
|
||
last upload). Long term: ask the Dokploy admin for a Gitea provider.
|
||
- **DB:** the SQL Server is on the operator's PC (`MECARE331` =
|
||
10.50.2.131), instance TCP port 58468 (dynamic — a SQL restart may move
|
||
it; set a static port later). SQL login **`cv_portal`** (made in SSMS
|
||
2026-10-27: db_datareader + INSERT/UPDATE `CV_PortalUser`, INSERT
|
||
`CV_PortalSignIn`); its password only in Dokploy's env
|
||
(`RESULTS_DB_CONN`), never here or in chat. The Portal works only while
|
||
that PC is on.
|
||
- **Fixes found live:** `69c51d8` the image needs `libgssapi-krb5-2` by
|
||
name (autoremove took it; ODBC Driver 18 "Can't open lib ... file not
|
||
found"); `cc3a01a` `TZ=Asia/Kolkata` + `tzdata` (the server is UTC: a
|
||
15:45 IST Sign-in read 10:15; three such rows stay in CV_PortalSignIn
|
||
from 2026-10-27 ~15:45). Errors seen: `SQLSTATE 01000` = driver not
|
||
loading; `28000` = login missing / wrong password.
|
||
- Sessions are in memory: every Deploy / Reload signs everyone out.
|
||
- For the client: needs a public HTTPS URL (or VPN) from IT, and the DB
|
||
off a desktop PC. `Dockerfile`
|
||
(python 3.13-slim + ODBC Driver 18, copies only `src/` + `web/`,
|
||
`requirements-portal.txt`); env in Dokploy, never in Git:
|
||
`RESULTS_DB_CONN` (a SQL login; a Linux container cannot use
|
||
Trusted_Connection or, likely, the named instance — host,port),
|
||
`PORTAL_URLS` (hosts / origins; all-https = Secure cookie),
|
||
`PORTAL_SERVER=1` (set in the image: NoRunner, no Start / Stop). The
|
||
Runs page there is empty (events files stay on the bot PC). Earlier
|
||
notes: Gitea `giteanew.ecareindia.com` (10.200.0.15, internal; its
|
||
HTTPS certificate is not in Git Bash's curl bundle) and Dokploy
|
||
(`http://10.200.0.15:3000`, same server). The operator joins Dokploy and
|
||
creates a **Private** Gitea repo themselves; nothing is pushed until they
|
||
say so, and only a single-commit snapshot of a scanned tree (§7). The
|
||
Portal cannot simply run in a Linux container as it is: Start / Stop
|
||
starts `run.py` (Windows, Chrome, eCW proxy, message boxes; never
|
||
committed), the Runs page reads `Logs\events_*.jsonl` (PHI, on the bot's
|
||
PC), the DB connection is `Trusted_Connection` (needs a SQL login from a
|
||
Linux container, and a route to `MECARE331`), and host / allowed origins
|
||
are `127.0.0.1` with no `Secure` cookie. Plan: the container is the
|
||
results Portal (Viewer for the client); the bot stays on the Windows PC;
|
||
Runs data to SQL later. Needs Ecare IT + Salud approval (PHI on the
|
||
page), a SQL login, the repo URL. Also open: the "Chrome open" box waits
|
||
up to 3 min on an unattended Portal run — off-by-default setting
|
||
suggested, not decided.
|
||
- **#25 `medicare_pps_code` (2026-10-23, `ac25f83`, EEWA-35/36) — not run
|
||
live.** Built test-first on the fixture UB claim (the fixture's Fee
|
||
Schedule prices for G0466 / G0467 / G0467NH / G0468 are made up; G0467H
|
||
left out to test "the picker cannot find it"). Needs test claims from the
|
||
client: Medicare UB 99213 without G0467; Devoted MCR UB 99203 without
|
||
G0466; a home visit 99347; a nursing-home visit 99309; an AWV G0439; a
|
||
mental-health 90834; a Cigna Healthspring claim; one already carrying the
|
||
right PPS Code. Watch on the first live VERIFY run: how many claims go to
|
||
Review for it (every HCFA claim of a PPS Payer with a Qualifying Code —
|
||
"HCFA Medicare" is one of the names), whether the live picker finds the
|
||
H / NH variants, and the fees eCW puts on them. Open: §6 "Waiting" 5.
|
||
The code review (2026-10-23) left two judgement calls as they are: the
|
||
hand-over through `data["_wellness_history"]` (documented in
|
||
`conditions/base.py`) and the use of `vaccine_admin_codes`' private
|
||
`_facts` / `_add_admin_code` (as `injection_admin_code` does).
|
||
- **Run tracking (EEWA-31..33, 2026-10-22).** All three stages built and
|
||
tested offline; the database part set up the same day (tables, column,
|
||
first Admin `ec10980` — §2). Runs that day, as the events files show
|
||
them:
|
||
- 16:24 (terminal, 50-claim test sheet): login in one attempt (Log In →
|
||
2FA mail 46 s), 17 claims checked, then **Ended without a record** —
|
||
the process was gone while the per-claim box after claim 17 was up
|
||
(window closed, most likely); the record has no end and no exit code.
|
||
- 17:56 (terminal, VERIFY with information boxes off): the 2FA mail
|
||
never came; **Stopped** after 8.5 minutes still waiting (no claims).
|
||
Same as attempt 1 on 2026-10-21 — a mail that does not come is eCW /
|
||
mailbox side, not the bot.
|
||
- 18:11 (terminal): login OK in 117 s (mail 53 s), then claim 1 **hung
|
||
28 minutes** after its UB form opened and the pre-flight passed — the
|
||
next step is reading the form (`claim_form.extract`); no error, no
|
||
dialog, and the open click had already timed out (5 s) though the
|
||
form was there. Stopped from here at 18:43. The same claim took 18 s
|
||
in the next run, so it looks like a frozen page, not the claim — see
|
||
the item below.
|
||
- **18:47 (Portal, started by `ec10980`) — Done, exit 0:** login in one
|
||
attempt (107 s, mail 43 s), **50/50 claims: 39 Pass / 11 Fail / 0
|
||
Review**, no Claim Error, Warning or Run Error, ~18 s a claim
|
||
(information boxes off), 17 minutes in all. Same split as the
|
||
2026-10-21 live run. Started By was `ec10980` in the events, in
|
||
`bot_run.json` and on all 50 `CV_ChargeSummary` rows.
|
||
Sign-in worked live at 18:24 (a first Start clicked after `run_web.bat`
|
||
had been closed did nothing — fixed in `37c214d`). Still to check live:
|
||
a Viewer without Start, and the Dashboard banner after a failed run. All
|
||
decisions are in the spec.
|
||
- **A page that freezes mid-claim — watchdog added 2026-10-22
|
||
(`0f16560`), not yet seen firing live.** The 18:11 run sat 28 minutes
|
||
after claim 1's form opened (Playwright's `evaluate` has no timeout). Now
|
||
no progress for `WATCHDOG_MINUTES` (10) closes the driver; the claim
|
||
becomes a Claim Error (Review), the run ends with **exit 8** at step
|
||
`stalled`, the rest Not Reached, the Dashboard banner shows it. The
|
||
cause of the freeze itself is still unknown (not seen again in the
|
||
18:47 or 19:53 runs).
|
||
- **Found while mapping the login (2026-10-22), not changed:**
|
||
`auth/mailbox.py` prints a mailed 2FA **code** into the run log
|
||
(`[OTP] Code ...`; the page tail hides it, the file does not); a login
|
||
page with no username box raises `Restricted` (exit 4) though it is more
|
||
likely the proxy; OK on the final captcha box counts as logged in
|
||
without checking the home page.
|
||
|
||
- **`auto_dialog` — fixed 2026-10-22 (`57d22fb`).** It called
|
||
`dialog.message()`, a property in Playwright Python, so the TypeError was
|
||
swallowed and no dialog was ever answered (a real Chromium tab crashed on
|
||
an unanswered confirm). Now: alert → OK; confirm / prompt → **Cancel in
|
||
VERIFY** (live eCW is read-only; a confirm's OK may be a save), OK only in
|
||
LIVE-WRITE; beforeunload dismissed. Each is logged
|
||
(`[DIALOG] confirm: '...' -> Cancel`) and is a Warning on the Runs page.
|
||
No dialog has appeared in any live log yet; if a VERIFY confirm ever
|
||
needs OK (say, a form that will not close without it), the Warning shows
|
||
which one — revisit the rule then. Tests: `test_auto_dialog.py`.
|
||
- **CDP Chrome — live 2026-10-22, still to check:** the Claude
|
||
extension in that Chrome (added and signed in with `start_ecw_chrome.bat`;
|
||
claude.ai bypasses the proxy, not yet tried), and why the landing page
|
||
after Home took ~2 min in Chrome (15 s in Chromium). eCW loaded through
|
||
the proxy with no certificate error; cookies were cleared and Chrome
|
||
closed at the end (§2).
|
||
|
||
- **LIVE-WRITE ISSUE — fixed in code 2026-10-16 (`57e2f5a`), live check
|
||
pending.** Found 2026-10-15, confirmed by the operator in the eCW audit
|
||
log 2026-10-16: clicking a CPT line's "Assign To Patient" box runs eCW's
|
||
`updateBillToIns()`, which saves on the server at once — Cancel does NOT
|
||
undo it (settings.py P16). Adding a CPT row (picker OK,
|
||
`addClaimFSCPTCode('save')`) saves too. Typed edits (POS, modifiers,
|
||
fees) are not kept. Earlier runs changed boxes on 9 claims
|
||
(2026-09-17..10-15, mostly G8510 checked by the old depression
|
||
re-include step); the list is in the logs (no claim ids here) and the
|
||
operator restores them by hand from the audit log.
|
||
Fix (option C): in VERIFY `claim_form._set_bill_box` sets the box by JS
|
||
(no click event) and `add_cpt_row` adds nothing; vaccine admin (every
|
||
code it would add) / 96372 / G2025 become Review "add by hand"; #22
|
||
still unchecks / zeroes the other rows on screen (review 2026-10-19).
|
||
`writes_allowed(page)` is False on a Dental claim in both modes. LIVE-WRITE unchanged — its box clicks
|
||
still save at once even if the operator then declines the save box (out
|
||
of scope for now). Tests: `tests/test_verify_no_live_writes.py`; the
|
||
click/add mechanics tests run as LIVE-WRITE. Page VERIFY run 2026-10-16
|
||
19:08 on the 50-claim sheet (after `57e2f5a` + `6457100`): 50/50, Pass 39
|
||
/ Fail 11 / Review 0, exit 0 — claim for claim the same as 2026-10-15's
|
||
run; Fails are 9 telehealth (typed POS/M1), 1 depression pointer (typed),
|
||
1 Dental HIGH RISK (read only). No box change, no row add, no A6 in the
|
||
log, so nothing was written — and the fix was not exercised. **Left:**
|
||
the operator adds claims to the sheet that exercise it (a checked line at
|
||
0.00, a non-Medicare vaccine without admin code, a Medicare UB claim with
|
||
G0008/G0009), one VERIFY run, then their audit logs show no new change.
|
||
- **#19 Medicare A6 (2026-10-16, `6457100`, EEWA-28).** UB done: A6 typed
|
||
into the first empty Condition Code box. Left: the HCFA screen's
|
||
condition codes (screenshot + DOM from the operator) — until then an HCFA
|
||
Medicare claim with G0008/G0009 is Review "check A6 by hand"; and one live
|
||
check that typing A6 is not saved by itself (audit log), as the DOM
|
||
suggests (no blur / change hook).
|
||
- **Code review of 2026-10-19** (`1d79ac3..HEAD`, Standards + Spec, 66
|
||
findings, 59 confirmed / plausible): fixed in `6005b7d` and `2da2bdd`
|
||
(the portal replaced the page findings). **Left:** (1) #13 secondary insurance — done 2026-10-21 (any row
|
||
counts); (2) #13 tests on
|
||
the UB / HCFA fixtures and a run_all-level VERIFY "zero clicks" test;
|
||
(3) live checks: a box set on screen in VERIFY survives the next tab
|
||
re-click (Angular redraw), a Dental fee blur is not kept on Cancel,
|
||
typing A6 is not saved by itself; (4) payer lookup still copied across
|
||
conditions (only #13 / #19 use `lines.primary_payer`); (5) Jira
|
||
EEWA-27 / EEWA-28 still To Do and their summaries do not follow
|
||
`docs/agents/issue-tracker.md`'s format.
|
||
- **Results page, live** (2026-10-14/15): three page-started VERIFY runs
|
||
of the 7-claim sheet, each 7/7 read, Pass 4 / Fail 3 / Review 0, exit 0;
|
||
card, chips and table matched. A 50-claim run (operator, terminal) gave
|
||
Pass 38 / Fail 12; page runs of the 50 (2026-10-15 and 10-16) gave Pass
|
||
39 / Fail 11. The 0.5 s "search page not ready" wait saved a claim
|
||
live. Left: Stop on a real run (EEWA-25). A terminal run waits at
|
||
"Press Enter to close", and the page shows it as running until then.
|
||
- **Test claim sheet** (`Input\Medcor Ready Claim Sheet.xlsx`, 2026-10-15):
|
||
50 claims (43 added by the operator's list; extra notes in column R).
|
||
`.env` `CLAIM_PROCESS_LIMIT=50` (operator) takes all 50.
|
||
- **Code review of 2026-10-14** (session changes 2026-10-12..13, 14
|
||
findings). Fixed in `ec150bf`: (1) the search page check waited
|
||
0 s — a claim of run 6 went to Review under eCW's loading
|
||
spinner ("covered by html.jelly-page"); now `wait_search_ready()`
|
||
polls up to 20 s, the route spinner counts as not ready, and a click
|
||
falling through to the page is logged "not taking clicks yet (eCW
|
||
still loading)"; (2)+(3) `clear_screen()` and `open_claim()` called
|
||
`dismiss_stray_modal()` without sparing the claim window, so Escape
|
||
could drop an HCFA form (left over, or auto-opened by eCW after the
|
||
Lookup) without closeClaimScreen() — now spared, Cancel only; (4) #20:
|
||
an unchecked 96372 with a fee + bot-added 96372 = two billed — now
|
||
counted as billed. Left open (not fixed): `close_duplicate_forms`
|
||
counts a Cancel it could not click; a ghost (inert) window next to a
|
||
live one fails the claim after 2×10 s; #18 adds 90471 on a COVID
|
||
vaccine already billed with 90480 (ask the client); DPIMP already
|
||
unchecked at 0.00 on a non-Dental payer still goes to Review;
|
||
progress_note.close() says "closed" when it clicked nothing; short
|
||
"HIGH RISK" notes show a bare "NO" in the Dental box; payer lookup
|
||
copied in 4 conditions; uncheck-and-zero apply/describe copied in 5.
|
||
From the fix's own review: a form `clear_screen` could not Cancel
|
||
stayed on screen and every later claim went to Review — operator
|
||
ruled (2026-10-14) Escape as the LAST RESORT, HCFA window only, logged
|
||
loudly ("LAST RESORT … closeClaimScreen() does NOT run"; check that
|
||
claim in eCW by hand) — `a28ffac`. Not fixed:
|
||
open_claim's retry after a failed click still Escapes (older path);
|
||
a stray panel UNDER an HCFA window would make Escape close the form
|
||
(never seen). #20: an UNCHECKED priced injection line that
|
||
priced_line_check checks at the end gets no 96372 — not ruled.
|
||
- **Code review of 2026-10-08/09**: no high-severity bug. The 4 medium
|
||
findings are **fixed in `a979eb8`** (2026-10-12): #14 "screening only" is
|
||
judged on the live grid after the zeroing; #12/#14 decline with
|
||
`CPT_GRID_NOT_READ` on an empty live grid; #14's comment says "unchecked" /
|
||
"fee zeroed" from what was done (UB with no checkbox: fee 0 is enough,
|
||
operator); #12 records a fee eCW re-prices after the modifier change (waits
|
||
up to 2 s per H-code row, like Units). The low findings are **fixed in
|
||
`48a6940`**: PHI kept out of `details_json`, text cut to column width,
|
||
SQLSTATE-only DB errors, 30 s query timeout, run_mode TEST VERIFY / TEST
|
||
LIVE-WRITE, web Host check + no-store headers + clear error kinds, #12
|
||
partial note = unread, J-code detect ignores case, test gaps. Not done
|
||
(need a live look): payer from the insurance row with sequence "P" rather
|
||
than the first row; whether eCW's M1–M4 inputs react to key events.
|
||
Rulings needed: Z13.31 retag on Medicare; WELLCARE MEDICARE / PERENNIAL
|
||
ADVANTAGE CO (and unmapped payers with G8510) as Medicare for #14.
|
||
- **Results DB live setup**: done (2026-10-12). `RESULTS_DB=1` in `.env`;
|
||
the SAVE-off test run wrote one row per claim to `dbo.CV_ChargeSummary`
|
||
(7 claims, Dental included). Left: open the page with `.\run_web.bat`
|
||
(PowerShell needs the `.\`) and check the rows there. `.env.example`
|
||
does not document `RESULTS_DB` / `RESULTS_DB_CONN` yet.
|
||
- **Dental claims — live verify** (built 2026-10-12, `cef7666`): the Dental
|
||
grid selectors and `extract()` come from one Dom.txt capture. First live
|
||
run SAVE-off on a few Dental claims (one with DCONS or Dscprit); watch how
|
||
many go to Review for "Progress note not read" (Prog. Notes disabled on
|
||
Dental?). Dental save stays off until that run is clean.
|
||
- Not built: "Secondary insurance removal" (expected on a test claim), the
|
||
"Special programs" half of #8, and the rest of the document rows.
|
||
- **Stacked dialogs — verified live** (`a9dcc63`). Second SAVE-off test
|
||
run 2026-10-12 (`run_20261012_171538.log`): 7 found, **7 read** (first
|
||
run: 3), 0 search failed, 0 Review; 4 Pass, 3 Fail. Every claim logged
|
||
`Search page ready`; eCW opened claim 6 twice again and the top copy was
|
||
Cancelled. The "2 progress note viewers" line on every claim was a
|
||
miscount (the viewer has two Close buttons) — fixed in `77fdffd`.
|
||
- **Ghost claim form after HCFA / Dental Cancel** — root cause found and
|
||
fixed in `f8216a7` (2026-10-13), not yet seen live. Test run 2026-10-13
|
||
(`run_20261013_163322.log`: 7 found, 7 read, 0 Review; 4 Pass, 3 Fail)
|
||
with the `77fdffd` diagnostic showed two cases, both only after an HCFA
|
||
or Dental claim:
|
||
1. The HCFA window closed while the bot's **review box** was in front of
|
||
Chrome stayed at opacity 0.222, `pointer-events: none`: Chrome had
|
||
paused the fade-out of a window it thought hidden. The next claim
|
||
clicked its Cancel four times (~45 s). Fix: Chrome is launched with
|
||
`--disable-backgrounding-occluded-windows`,
|
||
`--disable-renderer-backgrounding`,
|
||
`--disable-background-timer-throttling` (`core/browser.py`).
|
||
2. **eCW opens the next claim's own form by itself after the Lookup**
|
||
(window ids are creation timestamps: made during that claim's search,
|
||
before the bot's click). The bot Cancels it and clicks again — correct,
|
||
7/7 read; the log message now says so. Likely the cause of the
|
||
"claim opened twice" seen before.
|
||
Safety net: a window with `pointer-events: none` is never clicked; it
|
||
gets up to 10 s to settle (gone / clickable → Cancel / still not →
|
||
refused as before). `click_topmost` and `count_shown` skip such elements.
|
||
Next run: there should be no 4×Cancel timeouts; look for `finished on its
|
||
own` / `takes no clicks` lines.
|
||
- Three test files used to write screenshots into the operator's
|
||
`Screenshots\` folder (`*_search_not_ready_<claim id>.png`, some
|
||
`*_progress_note.png` of 2026-10-12/13 outside run times); fixed in
|
||
`f8216a7`. The files already written are fake test pages, not deleted.
|
||
- **eCW password changed 2026-10-20** by the operator (the bot never
|
||
changes a password). Afterwards every login hung: Log In went grey, no 2FA
|
||
page. A network log showed the click sent NO request when the password
|
||
was set with `fill()`; typed key by key it was sent at once and eCW went
|
||
to its 2FA page. Fixed in `password_entry.type_password()` (login and
|
||
captcha). A person's own logged-in eCW session is not the cause (ruled
|
||
out by logging out). 2FA mail can still be slow: attempt 1 that day got
|
||
no mail in 9 min, attempt 2 got it in 42 s.
|
||
- Ideas offered, not started: expected-vs-bot accuracy report from the test
|
||
sheet's Comments column.
|
||
- **Selector breaks (2026-10-21, `c0e1a75`):** pre-flight check, DOM saved on every
|
||
failure shot and fallback selector chains — **verified live 2026-10-21**
|
||
(run below): `[PREFLIGHT] Search page OK (5 selectors)`, `ub` / `hcfa` /
|
||
`dental claim form OK`, no `[SELECTOR] fallback:` line (every primary
|
||
still matches), and the 2FA timeout saved its page as
|
||
`<HHMMSS>_2fa_timed_out.html`. A `[SELECTOR] fallback:` line later means a
|
||
primary needs updating. AI healer (Gemini on Vertex AI, which the operator's
|
||
org already uses under its agreement) waits for the auth details (API key
|
||
or service account + project/region) and the model name; read /
|
||
navigation steps only, DOM masked before it is sent.
|
||
- **#23 `medicaid_pregnancy_th` (2026-10-20, `9b87b4d`, EEWA-30) — not
|
||
run live yet.** Files:
|
||
`conditions/medicaid_pregnancy_th.py`, `claim_form.put_row_m1()`,
|
||
the TH-for-GT switch in `telehealth.apply`, registry after
|
||
medicare_wellness_history, `tests/test_conditions_medicaid_pregnancy_th.py`
|
||
(14). Rulings confirmed 2026-10-21 (§6). Needs Medicaid pregnancy test claims
|
||
(in person and telehealth, one with only Z32.0x).
|
||
- **#27-#29 `medicare_wellness_history` (2026-10-20, EEWA-29) — not run
|
||
live yet.** Built from Dom.txt's Patient Inquiry Detail capture; the
|
||
side-panel launcher is `a[title="Account Inquiry"][ng-click^=
|
||
"vm.loadSmartPanel("]`, the visible copy clicked (from the claim form DOM
|
||
the operator pasted; 2026-10-21, was a text match before). Flow confirmed
|
||
by the operator 2026-10-21: G0402 / G0438 / G0439 on the claim → Account
|
||
Inquiry → Show CPT Payments → an earlier one inside lifetime / 3 years /
|
||
1 year → comment only, nothing changed.
|
||
Account Inquiry lists CPT lines only under a posted payment / adjustment:
|
||
a line billed but not yet adjudicated is not seen. Needs a live VERIFY
|
||
run on Medicare claims carrying G0402 / G0438 / G0439, one with an
|
||
earlier repeat and one without; watch that the modal closes and the next
|
||
claim is not covered by it.
|
||
- **Live VERIFY run 2026-10-21** (`Input\Medcor Ready Claim Sheet.xlsx`,
|
||
50 claims, code at `c8958cc`): 50 found, 50 forms read, **39 Pass / 11
|
||
Fail / 0 Review**, no error, every form closed with Cancel; Charge Summary
|
||
TEST sheet + `dbo.CV_ChargeSummary` written. Conditions that fired:
|
||
depression_dx_pointer, telehealth, vaccine_admin_codes. #15 new rule seen
|
||
live: the HIGH RISK Dental claim counts 173 characters from "HIGH RISK"
|
||
(307 for the whole notes before) → YES; the other Dental claims NO.
|
||
#13 / #23 / #27-#29 did not fire — the sheet has no such claim; they
|
||
still need claims of their own (D code on another payer, Medicaid
|
||
pregnancy, Medicare G0402 / G0438 / G0439).
|
||
- Login took 735 s: attempt 1's 2FA mail never came (9 min timeout),
|
||
attempt 2's came in 42 s. Two earlier tries that day died with
|
||
"Target page, context or browser has been closed" (after the 2FA
|
||
confirm, and before the password box) while **C: had 0-14 MB free**;
|
||
with ~2 GB freed the run went through. Keep C: with free space (the
|
||
runs set `TEMP` / `TMP` to `D:\claude_tmp`; Chromium still writes some
|
||
files under the user profile on C:).
|
||
- **Client test claims run 2026-10-20** (14 claims, VERIFY, one box per
|
||
claim; sheet `Input\Condition Test Claims.xlsx`, run with
|
||
`TEST_CLAIM_IDS_FILE` set for that run only — `.env` unchanged):
|
||
12 Pass, 1 Fail, 1 Review. Every claim was handled correctly for the
|
||
rules as they stand, but **almost none exercises a fix** — the examples
|
||
are already corrected:
|
||
- #12: BH payer, H0002 with no modifier → nothing to do.
|
||
- #18 (4): every vaccine already has its admin code billed (one claim's
|
||
vaccines 90677 / 90715 are not on the 19-code list anyway).
|
||
- #20 (3): one has 96372 billed; one is UHC Community = Medicare (ruled
|
||
2026-10-21: correct, #20 does not apply) — #14 unchecked G8510 instead
|
||
(Fail); one was not read (below).
|
||
- #43/#44 (3): 15851 already unchecked; two carry only 76801 / 76805
|
||
(ruling 0b).
|
||
- #48 (3): DFIMP (now handled) and DCONS, all already unchecked at 0.00.
|
||
Still to ask the client for: a BH-payer H code WITH a modifier and an H
|
||
code on a non-BH payer (#12); a listed vaccine with NO admin code, one
|
||
patient ≤18 and one >18 (#18); a listed injection with no 96372 on a
|
||
non-Medicare payer (#20); a Medicaid claim with 15851/15852/73521 still
|
||
checked (#43/#44); DPIMP/DFIMP still checked with a fee (#48).
|
||
- The claim after a held-open review box was **not read**: a claim modal
|
||
still covered the search page ("Claim No is covered by
|
||
div#claimModal…"), 20 s wait, Review. The next claim started clean.
|
||
Watch for it; re-run that claim alone.
|
||
|
||
- **eCW password expiry box (history).** On 2026-10-05 eCW started showing
|
||
"This password expires in 14 days. Do you want to change the password now?"
|
||
after the 2FA approval. Both runs that day failed with "Login not confirmed":
|
||
the box hid the home screen, and the bot did not know its Cancel button.
|
||
Fixed in `b787e2d`: the box is found by its text and only its own Cancel is
|
||
clicked (never OK). An already-expired password stops the run with that reason.
|
||
The fix has not run live yet, because the real box's DOM was never captured.
|
||
**A person must change the password in eCW before it expires and update
|
||
`ECW_PASSWORD` in `.env`.** The bot never sets a password.
|
||
|
||
- Two payers seen in downloaded reports are not on the dashboard yet:
|
||
`Banner Aetna Insurance`, `HealthSpring Claims Dept` (blank Group Name).
|
||
- Team's Comments dropdown list (the format's dropdown points at a broken network file).
|
||
- "Click reported failure, but the claim form is already open": now ~5–10 s per
|
||
hit (was ~30 s); the log line records the error and elapsed time, so the
|
||
root cause can be studied.
|
||
- Vaccine add-admin-code step: not yet tested live.
|
||
- Sliding fee: no live EXPIRED or NONE case seen yet (all verified claims were ACTIVE).
|
||
- None of the 2026-10-05 fixes has run against live eCW yet. Do a short live
|
||
test run (SAVE off) before the demo.
|
||
- Live checks waiting for the operator's OK: **P16** (add a row / tick a box on
|
||
a test claim, Cancel, reopen: did anything persist?) and **L3** (UB checkbox
|
||
selector in DevTools).
|
||
- A form that stays stuck on screen can cost up to ~2 min per claim while the
|
||
bot tries to cancel it (that claim then goes to Review).
|
||
|
||
## 11. Demo / production review (2026-10-01, 56 verified findings → 38 items)
|
||
|
||
Full report: `docs/2026-10-01-demo-production-review.md` (file:line references
|
||
are as of 2026-10-01).
|
||
|
||
**Status (2026-10-06):** recommended steps 1–6 are done in commit `f5bf3f9`:
|
||
P12, P1, D1, D10, D9, D3, D4, D5, D6, D7, D8, P8, D11, D2, with St1 and P2 in a
|
||
lighter form (the outcome report plus "tainted claims are never offered Save").
|
||
Each step was implemented, reviewed and fixed, then a final review of the whole
|
||
change ran with 5 lenses (19 of its 21 surviving findings fixed). Still open
|
||
from steps 1–6: **P16** and **L3** (live checks) and the demo `.env` choice (§6).
|
||
Steps 7–10 below are not started.
|
||
|
||
Top items as found (the ones marked ✅ are done):
|
||
|
||
**Before the demo**
|
||
- ✅ D1 Test mode on → summary mostly blank; name test summaries `_TEST`.
|
||
- ✅ D2 Summary says Clean/Pass even when a check crashed, declined, or the note was not read → add a "Review" category.
|
||
- ✅ D3 Telehealth reports POS 2→2 on already-correct claims → correct claims marked Fail.
|
||
- ✅ D4 Audio-only detection matches "audio and video", "Audiology" → 93 instead of 95.
|
||
- ✅ D5 Telehealth writes 93 for unmapped/blank payers.
|
||
- ✅ D6 Failed G2025 add still reported as success.
|
||
- ✅ D7 HCFA/Dental read before Angular fills the form (0 CPT → Pass).
|
||
- ✅ D8 Progress note fixed 3 s sleep misses slow notes.
|
||
- ✅ D9 Dental per-claim box shows "?" fields and Tamil words.
|
||
- ✅ D10 Same-day summary lost if the earlier file is open in Excel.
|
||
- ✅ D11 30 s click timeout (known).
|
||
|
||
**Before live production**
|
||
- ✅ P1 Save/Apply default True; `.env` inline comments parse wrong; no mode banner; Enter = Save.
|
||
- ◐ P2 Partial apply() failure leaves edits that a later Save writes.
|
||
- P3/P4 No recovery after a stuck form/expired session; summary lost on crash; no resume.
|
||
- P5 Save/close success checked by a UB-only element (wrong on HCFA/Dental).
|
||
- P7 Cancel / Don't Save clicked by numbered ids.
|
||
- ✅ P12 PHI files in repo root not gitignored.
|
||
- P16 Prove `SAVE_CORRECTIONS=0` is truly read-only (does adding a row/checkbox save server-side?).
|
||
- Also: TLS checks off, PHI retention, auto-accepted confirm(), stale README.
|
||
|
||
**Logic**: L1 depression can strip a line's only pointer; L2 telehealth first row may be an excluded code;
|
||
L3 UB checkbox selector unconfirmed; L4 slow sliding modal reads as NONE; L5 high-risk negations.
|
||
|
||
**Structure**: ◐ one outcome model per claim (changed/declined/failed — light version done); run journal + resume;
|
||
`RUN_MODE` attended/unattended; production approval model (with client); split `claim_form.py`;
|
||
`tests/run_all.py`.
|
||
|
||
**Performance**: ~43–45 s/claim before the fixes (click fallback) → ~13–15 s expected after them (click timeout fix done; not yet measured live);
|
||
~540-claim run 4.5–14 h today → 2.3–4.8 h. No run beyond 17 claims yet (soak test needed).
|
||
|
||
**Recommended order**: PHI gitignore + save-off default + Cancel-discards check → demo setup →
|
||
on-screen polish → telehealth correctness → read completeness → truthful summary →
|
||
production write safety → run resilience → operating model → hardening/docs.
|