Commit graph

155 commits

Author SHA1 Message Date
justin
7ea18dd3d8 healthcare: optional surrogate-access intake question (expedited path)
- NpiIntakeStep: add positively-framed 'can you grant electronic I&A Surrogate
  access?' question for all filing slugs (reval/reactivation/nppes-update/
  enrollment/bundle). Optional, never required, never mentions paper; captured
  as intake_data.surrogate_access (yes/no/blank). Astro build green (58 pages).
- npi_provider.py: surface the surrogate answer in the admin todo so fulfillment
  knows EXPEDITED (online via surrogate) vs STANDARD (e-sign + daily mail batch).
2026-06-07 00:33:33 -05:00
justin
138fec17e9 healthcare: daily batched paper-filing fulfillment
Standard (no-login) CMS filings are mailed in one Priority Mail envelope per
destination agency, batched each postal working-day morning to save postage.

- migration 089: paper_filing_batches table + esign_records.paper_batch_id /
  filing_destination_key (idempotent: a filing is batched at most once).
- batch_cover_sheet.py: per-agency cover sheet (sender/dest/date/manifest) +
  merged print-job PDF (cover + all enclosed signed filings).
- daily_paper_batch.py worker: gather signed+unbatched cms855/cms10114 filings,
  group by destination (MAC by state via mac_routing; Fargo for CMS-10114),
  build cover+merged PDF per agency, persist batch, mark filings batched.
  Self-gates on postal working days (skips weekends + federal/USPS holidays).
  Phase 1 = human prints+mails; phase 2 = wire print-mail API.
- worker-crons: pw-paper-batch systemd timer (Mon-Fri 13:30 UTC, self-gated).
- test_paper_batch.py: 15/15 pass (working-day gating, routing, cover+merge).
2026-06-07 00:30:01 -05:00
justin
258d23bdc6 healthcare: two-tier (standard paper / expedited surrogate) filing model
- Verified Standard(no-login)/Expedited(surrogate) matrix from official CMS-855
  PDFs (docs/healthcare-filing-tiers-verified.md): reactivation+revalidation are
  855I paper-to-MAC reasons, original-signature, routed by state; sig may not be
  delegated; 855B needs PECOS app fee.
- Add scripts/workers/mac_routing.py: state->MAC routing (all 56 jurisdictions,
  12 destinations) for envelope addressing + daily batch grouping. Addresses
  marked VERIFY before live mail.
- npi_provider.py: fix access strings to two-tier framing; NPPES update/reactivation
  no longer 'online-only'; note 855B fee.
- checkout.ts + service pages: strip client-facing mechanics & the paper-vs-tier
  choice; surrogate is the only optional, positively-framed ask (faster, never
  required, never share password).
2026-06-07 00:24:56 -05:00
justin
3da7794a85 hc email: add 'verify every detail for accuracy' (core strength) so the filing isn't rejected 2026-06-06 16:49:36 -05:00
justin
4233c90a4f hc email: reframe value-add to 'No 2FA. No government portals.' (we have a portal; the pain is CMS 2FA/identity-proofing); cron creates fresh dated campaign when prior is finished; add hc bounce watcher (Postfix->listmonk-hc webhook, hard/complaint->blocklist) 2026-06-06 16:47:12 -05:00
justin
2aa9e770c9 healthcare email: add color variety - amber warning box (urgency) + red overdue date + green price/CTA, breaking up the all-teal look into a problem->relief->action color story 2026-06-06 04:58:44 -05:00
justin
8eea9a694f healthcare email: add 'About Performance West' explainer (regulatory compliance consulting firm) after the no-logins relief copy 2026-06-06 04:57:42 -05:00
justin
0d212787ef healthcare email: add 'No logins. No portals. No headaches.' value-add (sells the relief, hides the mechanics); research doc on verified no-login third-party submission paths 2026-06-06 04:53:26 -05:00
justin
53ec011198 email trust signals: add data-safety + guarantee + social-proof strip to HC, telecom (campaign_template), and trucking (6 source + active campaigns via injector). Vertical accents: teal/blue/orange 2026-06-06 04:13:16 -05:00
justin
95698852ce healthcare warmup: gate Google/Workspace domains out of week 1 (they hard-reject cold IPs 550-5.7.1); send 501 non-Google practice domains first, defer 222 Google to week 2-3; cron uses hc_warmup_nongoogle.csv 2026-06-06 04:02:00 -05:00
justin
2bc86268f7 healthcare: HC warmup campaign cron (Mon-Fri 7AM Central) - imports overdue-first verified slice into listmonk-hc + runs Medicare-revalidation campaign via hc HOT stream; rate-throttled by pw-hc-rampcap 2026-06-06 03:57:08 -05:00
justin
2d3bccd31e healthcare email: white logo for teal header (was dark navy, invisible); drop NPPES-source footer line 2026-06-06 03:48:04 -05:00
justin
29c7a421e9 healthcare email: teal gradient header (matches site hero) + standalone CSV MX/SMTP verifier (binds .72 non-sending IP); gitignore PII warmup lists 2026-06-06 03:39:19 -05:00
justin
5129ebec5c healthcare email: add List-Unsubscribe/List-Id/Date/Precedence bulk headers to improve inbox placement on the cold hc IPs 2026-06-06 03:31:22 -05:00
justin
3859557506 healthcare: +$200 across all 6 provider services; add segmented marketing email builder (5 compliance-problem campaigns) + rendered HTML 2026-06-06 02:33:46 -05:00
justin
9bcd27db80 feat(site): vertical-specific order-page headers (trucking/telecom/healthcare/corporate) via unified VerticalOrderHeader; apply to all 49 order pages; retire TruckingOrderHeader 2026-06-06 01:52:22 -05:00
justin
68333148e6 fix(npi): two-tier Direct/HISP classifier so real Direct-Primary-Care/counseling practices stay institutional (was wrongly parked); add classifier unit tests 2026-06-06 00:09:42 -05:00
justin
90d8b94f3f feat(email): wire listmonk-hc into deploy + dev override + hc ramp-cap
- deploy.sh/deploy-dev.sh: bring up listmonk-hc (upstream image, excluded from
  build); document the one-time listmonk_hc DB create + --install.
- docker-compose.dev.override.yml: dev-only override (committed) that drops the
  prod host-port bindings and pins dev's own postgres volume (dev-pgdata) via
  compose !override tags. deploy-dev ships it as docker-compose.override.yml so
  syncing the canonical compose to the shared host no longer breaks dev's
  api-postgres (port :5432 clash + volume switch). Discovered + fixed while
  validating listmonk-hc on dev.
- pw-hc-rampcap.sh: healthcare analogue of pw-listmonk-rampcap, ramps the
  listmonk_hc cap 100->1000/h off /etc/postfix/hc-warmup-start, fully
  independent of the trucking ramp/cap.
2026-06-05 19:19:45 -05:00
justin
289c3b91be feat(healthcare): split outreach list into 3 outbound streams
Add scripts/healthcare_email_streams.py as the single source of truth for
classifying NPPES-endpoint emails into institutional (HOT stream) / consumer
(trucking-discipline stream) / direct (DirectTrust, parked), plus an exclude set
for non-prospect giants (va.gov, *.mil, cvshealth, walgreens, walmart).

Rework build_npi_outreach_lists.py to emit one CSV per stream
(npi_healthcare_institutional/consumer + npi_direct_secure), overdue-first
sorted, with companion files (revalidation/leie/optout) now optional.

Verified on May 2026 NPPES endpoint_pfile: 89,557 institutional / 19,366 consumer
/ 242,441 direct rows.
2026-06-05 18:59:44 -05:00
justin
4f49fad7f9 deploy: bring up the healthcare proxy-relay sidecar on prod and dev
deploy.sh: include proxy-relay in the default service set; it's an upstream
image (ginuerzh/gost) with no build context, so exclude it from 'compose build'
while keeping it in 'compose up'.

deploy-dev.sh: rsync docker-compose.yml to the dev server (it was never synced,
so new services like proxy-relay never reached dev) and add proxy-relay to the
'compose up --build' set.
2026-06-05 18:41:09 -05:00
justin
4060fd7562 fix(proxy): parse proxy creds with URL-reserved chars (e.g. '#') correctly
The residential proxy password contains a '#', which urlparse() misreads as a
URL fragment and corrupts the port (ValueError: Port could not be cast...).
Parse scheme://creds@host:port manually and percent-decode user/pass so both
raw ('#') and encoded ('%23') passwords work. Verified against the live
credential.
2026-06-05 18:34:19 -05:00
justin
17318f6e7d feat(healthcare): route NPPES/PECOS Playwright flows through residential SOCKS proxy
CMS healthcare portals (NPPES, PECOS, I&A) block datacenter IPs, so the
healthcare browser automation needs to egress via the residential proxy on
hg409y7ez04.sn.mynetname.net (username 'performancewest').

- undetected_browser: use_proxy now accepts an env-var name, so callers can
  select a domain-specific proxy. _proxy_config(proxy_env) reads it and falls
  back to UNDETECTED_PROXY_URL. Healthcare uses 'HEALTHCARE_PROXY_URL'.
- probe_npi_undetected: launches with use_proxy='HEALTHCARE_PROXY_URL' when set.
- npi_provider: documents that the (future) automated NPPES/PECOS flows must
  use the healthcare proxy.
- Plumb HEALTHCARE_PROXY_URL (+ UNDETECTED_PROXY_URL fallback) through the
  ansible env template and docker-compose workers env.

The credential itself is NOT in the repo. Set the full URL in the ansible
vault as vault_healthcare_proxy_url:
  socks5://performancewest:<password>@hg409y7ez04.sn.mynetname.net:<port>
Verified parsing + Playwright proxy-dict wiring with a unit test.
2026-06-05 14:36:01 -05:00
justin
bd9a70607f fix: maintain Services dropdown header from one canonical source
The site header / Services mega-dropdown was duplicated across two render
systems (Astro pages via Base.astro->nav.html, and ~80 pre-rendered static
public/**/index.html pages each embedding their own copy). They had drifted
into 5 different variants (missing 'New Carrier Setup', misplaced Healthcare
column, NEW vs FREE badges, em-dash encoding differences), so
dev.performancewest.net, the order pages, and the rest of the site disagreed.

- Make site/src/partials/nav.html the single source of truth (adopts the most
  complete variant).
- Add scripts/sync_nav.py to rewrite every static page's <nav> block from
  nav.html (idempotent; --check guards against drift in CI/deploy).
- Run the sync automatically in deploy.sh and scripts/deploy-dev.sh.
- Deprecate scripts/inject_healthcare_nav.py (now delegates to sync_nav.py).
- Neutralize the broken no-op SiteNav.astro component.

All 80 headers + the Astro-built order pages now render the identical dropdown.
2026-06-05 14:27:24 -05:00
justin
695ace207c Reframe healthcare filing as standard vs expedited; e2e test + bug fixes
Copy: drop paper/electronic/fax framing across the revalidation + enrollment
marketing pages and the order-confirmation email; present two service tiers:
- Standard filing  (no CMS account; we prepare CMS-855, you sign, we submit to MAC)
- Expedited filing (CMS I&A surrogate access; same-day PECOS filing + tracking)
Internal worker todos + the _STANDARD_FILING_SLUGS identifier updated to match.

New scripts/test_healthcare_e2e.py validates the whole order line (slug
consistency x6 places, price agreement, intake field collection+enforcement,
worker dispatch, handler execution producing CMS-855 PDF+anchor, free-tool
action_urls). 45 checks.

Bugs found + fixed by the test:
- medicare-enrollment requires practice_state server-side but the wizard never
  enforced it -> orders could be paid then stall. Wizard now requires it.
- determine_form_type defaulted org NPIs to the individual 855I because
  enumeration_type is never collected -> wrong form, CMS rejection. Now does a
  live NPPES lookup (safe 855I fallback).
2026-06-05 03:58:46 -05:00
justin
5cfe9702e2 Add Healthcare/NPI section to nav dropdown across all static pages
The site's pre-rendered public/**/index.html pages each embed their own copy
of the Services mega-dropdown and do not read src/partials/nav.html, so the
earlier nav.html-only edit never appeared. inject_healthcare_nav.py adds the
canonical Healthcare block (Medicare Revalidation, Medicare Enrollment, NPI/
NPPES Services, free NPI Compliance Check) to the desktop Column 3 + mobile
menu of all 80 static pages. Idempotent.
2026-06-05 03:05:19 -05:00
justin
e212f20a34 Add CMS-855 PDF filler + e-sign fulfillment for Medicare revalidation/enrollment
- cms855_pdf_filler.py: fills official CMS-855I/B/O/A AcroForms from intake
  (name, NPI, DOB, cert-page printed name) and records the signature anchor at
  the form's official /Sig box so the e-sign stamper lands on the cert line.
- npi_provider handlers (revalidation/reactivation/enrollment) now generate the
  paper CMS-855, upload it to MinIO, request_esign with anchors, and email the
  signing link. Human completes/verifies + USPS Priority Mails to the MAC.
- scripts/Dockerfile: copy the official CMS-855I/B/O/A forms into the image.
2026-06-05 02:27:11 -05:00
justin
31a53f89a6 feat(npi): offer paper CMS-855 path (e-sign + we mail to MAC) alongside I&A surrogacy
- order-confirmation email presents both filing methods: paper CMS-855 (no
  account needed, client e-signs one page, we print+mail to their MAC) and
  I&A surrogacy (faster, needs CMS account). NPPES-only services note that
  surrogacy is required (web-only).
- npi_provider handlers record the access model per service in admin todos.
- marketing copy leads with the lowest-friction paper option.
2026-06-05 01:53:44 -05:00
justin
e32193352b fix(npi): lenient CSV decoding in companion loader (CMS exports have stray latin-1 bytes) 2026-06-05 01:38:02 -05:00
justin
157c7a2571 test(npi): add slug consistency check across all wiring places 2026-06-05 01:35:04 -05:00
justin
4b0155542e feat(npi): healthcare marketing pages, nav dropdown, NPI lookup API + free tool + companion data migration/loader 2026-06-05 01:33:36 -05:00
justin
e67db156e8 feat(npi): wire 6 healthcare services into catalog, intake, items, handlers, portal 2026-06-05 01:25:05 -05:00
justin
73e09b12a0 feat: NPI outreach list pipeline (120k cold-emailable + 236k DirectTrust-later) + doc 2026-06-05 01:08:26 -05:00
justin
8400e27d12 Add DOT check CTA to trucking deficiency emails 2026-06-04 18:29:01 -05:00
justin
40d5643116 Fill trucking campaign quotas with sendable subscribers 2026-06-04 12:36:22 -05:00
justin
e5e70b744b Guard bounce watcher against empty queue IDs 2026-06-04 12:33:53 -05:00
justin
c027d49f43 Fix trucking campaign cron send date 2026-06-04 03:19:35 -05:00
justin
f42833cc9d Set trucking campaign Reply-To header 2026-06-03 23:35:28 -05:00
justin
ef79e85b41 Align trucking source campaign placeholder list 2026-06-03 23:32:53 -05:00
justin
5c35140a22 Configure trucking deficiency campaign cron env 2026-06-03 23:04:41 -05:00
justin
d7de818f39 fix: stagger trucking campaign catchups and subscriber reattach 2026-06-03 13:21:16 -05:00
justin
6d4c323ab6 feat: daily intake-reminder worker for paid orders with incomplete intake
Adds a systemd-timed worker that nudges customers who paid but never completed
their intake form (which stalls fulfillment).

- migration 087: intake_reminder_count + intake_reminder_last_at on
  compliance_orders (makes the daily run idempotent and bounded), plus a
  partial index for the paid-order eligibility scan.
- scripts/workers/intake_reminder.py: each run emails any paid order with
  intake_data_validated != TRUE, capped at 10 reminders/order, at most one
  consolidated email per customer per day (groups a customer's incomplete
  services into one email). Reuses the post-payment intake URL format
  (/order/{slug}?order={n}) and the API's email validation, skipping
  placeholder/invalid addresses (synthetic@, pipeline.com, etc.). Sends via
  smtplib with SMTP_PASS (verified working in the worker container).
- worker-crons: pw-intake-reminder timer, daily ~noon ET (16:00 UTC).
2026-06-03 00:20:37 -05:00
justin
f6419759e6 portal: converge all compliance orders on the single ERPNext portal
Root cause of customers being unable to log in: ERPNext (portal.performancewest.net)
is the intended single portal and already surfaces compliance/trucking orders
(performancewest_erpnext/www/orders.py reads compliance_orders by email). But
only the Stripe checkout path provisioned the ERPNext Website User up-front
(findOrCreateCustomer). PayPal / crypto / remediation-pipeline orders go straight
to handlePaymentComplete, which created NO portal user and never set
portal_user_created -> no login + no set-password invite (exactly what happened
to the Paul Wilson / Compound Technologies PayPal order).

- handlePaymentComplete: add ensureCompliancePortalUser() in the shared
  post-payment path so EVERY paid compliance order (any payment method) gets an
  ERPNext portal account + the set-password invite. Idempotent.
- Guard against placeholder emails (synthetic@/pipeline.com etc): skip portal
  provisioning and the set-password invite for non-deliverable addresses.
- compliance-orders API: validate email format AND reject placeholder addresses
  at order creation (was: presence-only, so synthetic@pipeline.com passed).
- delivery_worker: never email a set-password invite to a placeholder address.

Note: the legacy PG-customers login (api/routes/portal-auth.ts, /account/*) is
CRTC/formation-era and only backfills canada_crtc_orders/orders, never
compliance_orders. ERPNext is now the consistent portal for compliance.
2026-06-02 22:44:34 -05:00
justin
f21f3d41d9 DOT D&A binder: remove Performance West and National Drug Screening from vendor directory
Drop the two self-listings (Performance West C-TPA and supervisor training) and
the National Drug Screening / NDS entries from the Suggested Vendors panels.
The non-endorsement disclaimer (which still names Performance West) is unchanged.
2026-06-02 22:02:07 -05:00
justin
843a5bfacb DOT D&A binder: add DER Quick-Start checklist, two-column vendor directory, flow sections
- Add a one-page 'DER Quick-Start Checklist' tear-off as the first content page
  (set-up-once / every-hire / ongoing checkboxes, each pointing to the relevant
  section or form).
- Add a two-column 'Suggested Vendors & Resources' directory page: C-TPA/
  consortium, collection sites & labs, MRO, SAP, supervisor training, and (mode-
  aware) FMCSA Clearinghouse or DOT resources, plus employee help lines. Marked
  as examples not endorsements; mode-aware.
- Remove forced page breaks between consecutive content sections (now a light
  section rule) so they flow continuously; page breaks kept only for the cover,
  quick-start, TOC, each form, the vendor page, regulations, and the addendum.
- New builder helpers: section_rule(), checkbox(), two_col_panels().
2026-06-02 21:54:06 -05:00
justin
501e417584 DOT D&A binder: add Section 11 — Practical Guidance for the Administrator
Adds the real-world know-how a first-time DER needs beyond the bare regs:
- Owner-operators / one-driver companies (must use a consortium; cannot self-test
  or self-select) — the most misunderstood case.
- Audits & penalties: what the new-entrant safety audit asks for and the
  consequences of no program (civil penalties, failed audit, out-of-service).
- Problem test results: dilute, shy bladder, cancelled test, split-specimen.
- Prescriptions / marijuana / CBD (marijuana prohibited regardless of state law;
  CBD trap; route medical questions to the MRO).
- What counts as a refusal (treated as a positive).
- Costs & timeline expectations.
- DER do's and don'ts (act same-day, keep records separate/confidential, never
  tip off a random selection, don't interpret results yourself).
2026-06-02 21:42:55 -05:00
justin
b569a32f3a DOT D&A binder: add driver enrollment guidance + Form G roster
Customers (the DER) had no concrete how-to for onboarding/enrolling drivers or
what information to collect. Add:
- Section 1 'Enrolling a driver (new-hire onboarding)' subsection: exact info to
  collect, the onboarding sequence (collect info, sign Forms A/B, Clearinghouse
  query, prior-employer inquiry, add to C-TPA pool, pre-employment test, wait for
  MRO negative), and a driver-removal note.
- Form G — Driver Enrollment & Covered-Employee Roster: per-driver enrollment
  block (name, DOB, SSN last4, CDL #/state, contact, hire date, test result,
  Clearinghouse/prior-employer status) plus a roster table for the covered pool.
- TOC, email, and handler text updated A-F -> A-G.
2026-06-02 21:39:50 -05:00
justin
7c79cc9a08 DOT D&A delivery email: point CTA + body to the customer portal
The instant-delivery email told customers to 'just reply to this email' with no
way to view/manage their order. Add a portal line in the body and change the CTA
to 'View in Portal' pointing at PORTAL_URL (portal.performancewest.net), matching
delivery_worker/renewal_worker conventions. Add _site_url()/_portal_url() helpers.
2026-06-02 21:37:01 -05:00
justin
9718ab9ffa DOT D&A binder: editable DOCX output, all 6 forms each full-page, service-aware delivery email
- Rewrite dot_da_binder_generator.py to emit an editable .docx (was reportlab PDF)
  so carriers/counsel can review and adapt the program. ~4000 words, 10 sections.
- Render all six required forms (A-F); previously only A, D, E existed. Each form
  starts on its own page (page break) and fills a page.
- Mode-aware policy text for FMCSA/FRA/PHMSA/FTA/FAA/USCG with correct CFR parts
  and random-testing rates; optional single-state Drug-Free Workplace addendum
  (federal DOT program is nationwide; only the optional DFWP addendum is state-keyed).
- Handler now outputs .docx instead of .pdf.
- job_server instant-delivery: attach DOCX (correct MIME) as well as PDF, and use
  DOT-specific email copy + CTA instead of the FCC/telecom boilerplate.
2026-06-02 21:27:44 -05:00
justin
06e59965cc DOT D&A: instant PDF compliance-program binder (49)
Turn the DOT Drug & Alcohol Compliance Program into an automated
instant-delivery deliverable: when a carrier orders, we generate a
complete, print-ready PDF binder and email it (no admin step).

The binder (dot_da_binder_generator.py) bundles everything a small
carrier needs under 49 CFR Part 382 + Part 40:
  - How to manage the program (DER setup + annual operations)
  - Written drug & alcohol testing policy for employees
  - The six DOT test scenarios + triggers
  - Random testing / consortium (C-TPA) instructions
  - Supervisor reasonable-suspicion training + live/online access
  - Violations, SAP access, return-to-duty / follow-up
  - EAP / rehab / treatment resources (SAMHSA, 988, locator, ODAPC)
  - Recordkeeping retention schedule
  - Ready-to-use forms (acknowledgment, reasonable-suspicion,
    post-accident decision worksheet)
  - Regulation citations
  - Optional state Drug-Free Workplace addendum

Policy-variant selection: FMCSA (Part 382) is the trucking default;
honors an explicit dot_da_mode override for FRA/PHMSA/FTA/FAA/USCG.

New DrugAlcoholProgramHandler returns the binder PDF; slug added to
INSTANT_DELIVERY_SLUGS so job_server emails it automatically. Slug
rerouted from MCS150UpdateHandler (was admin-assisted enrollment) and
re-priced as a discountable own-deliverable (no passthrough cost).

Tests: scripts/tests/test_dot_da_binder.py (FMCSA sections, PHMSA+state
addendum, all-modes render) — passing.
2026-06-02 19:28:58 -05:00
justin
29ad0908ee trucking: pass-through fee disclosure + state fulfillment status machine
Item 2 of the trucking state-authorization plan.

- compliance-orders.ts: populate gov_fee_label for every state-trucking
  service so the variable, billed-at-cost government charges (apportioned
  IRP, IFTA decals, NY HUT, CT HUF, weight-distance, CA MCP+CARB, OS/OW
  permits, bundle) are disclosed at checkout. price_cents stays the flat
  service fee; gov fees pass through at cost.
- migration 086: compliance_orders.fulfillment_status state machine
  (authorization_required -> authorization_signed -> awaiting_customer_
  delegation -> awaiting_secure_credentials -> awaiting_government_fee_
  approval -> awaiting_insurance_filing -> ready_to_file ->
  filed_waiting_state -> completed) + fulfillment_status_at
- state_trucking.py: FULFILLMENT_* constants + _set_fulfillment_status();
  gate sets authorization_required on pause, authorization_signed on
  resume, ready_to_file once the filing todo is queued
- TruckingValueNotice.astro: 'What's included & what's billed at cost'
  disclosure with the authorization/delegation explanation
2026-06-02 16:49:31 -05:00