The MCS-150 intake-completion email linked customers to /order/dot-compliance,
which is the sales/checkout page -- it ignores ?order= and asks the customer to
re-pick services and pay again, so they 'cannot enter any data' (Paul Wilson's
report). Link to the per-service intake wizard /order/<slug>?order=... instead,
which loads the paid order, pre-fills from the FMCSA census, and drops payment.
Also add a Trailers field to the DOT intake fleet section and wire it through to
the MCS-150 PDF Q26 trailer row, so carriers can update trucks AND trailers.
The sales we got came at $79 + a 24hr coupon; cutting MCS-150 to $39 flat
removed urgency and conversions did NOT improve (a permanent low price sets a
new anchor and lets people defer). Restore the higher anchor and let an
expiring discount create the now-or-lose-it decision.
- Restore MCS-150 anchor $39 -> $79 (catalog single source + regenerated).
- build_trucking_campaigns.py: mint ONE random 5-letter coupon per send-day
(40% off, valid through 23:59:59 ET that day) into the existing discount_codes
table; inject coupon_code/pct/expires + a ?code= LP link into every email.
Idempotent per day; service-fee-only scope (gov/pass-through fees never cut).
- Listmonk MCS-150 (186) + Inactive USDOT (188) templates: lead with the
struck-through anchor + sale price + code + 'expires tonight', and point the
primary CTA at the order page (with code) instead of the 'free check' tool.
- OrderPriceBanner: validates ?code= via /api/v1/discount and shows
was/now + expiry; Wizard forwards the code to order creation.
- Verified: code gen, expiry math, scope enforcement, discount API
(40% off $79 = $47.40), site+api builds clean.
Closes the data gap for orders that bypass the full intake (e.g. the DOT
compliance-remediation pipeline) and for all MCS-150 variants:
- Worker intake-completeness gate (mcs150_update): before filling, check the
customer-required operational fields the FMCSA census cannot supply
(operation classification, cargo, CURRENT annual mileage, email; plus
signer/address for new-registration/reactivation, and states-of-operation
for 150B hazmat). If missing, email the customer a census-pre-filled intake
link and hold the order at fulfillment_status='awaiting_intake' with an admin
todo, instead of fabricating a blank filing. The existing intake PUT endpoint
already re-dispatches the worker on submit, so filing auto-resumes.
- Intake wizard (Wizard.astro): when resuming ?order=CO-xxx for a DOT/MCS order,
seed still-empty fields from the FMCSA census (name/address/fleet/interstate)
so the customer only confirms the operational details.
- /api/v1/dot/census now also returns total_drivers + a normalized
carrier_operation_code for the prefill.
- MCS150Step.astro extended to collect every field the filler needs across all
variants: mailing address, cdl_drivers, primary_vehicle_type,
reason_for_filing, usdot_revoked, cell/fax, hazmat-safety-permit block
(needs_hmsp, operating states, security plan), and intermodal-equipment
provider counts; all prefill from intake_data.
verify_mcs150_variants.py covers 150/150B/150C end-to-end (ALL PASS).
High-friction conversion points (payment step, review step, order intro) had
almost no trust reinforcement at the moment of payment. Adds a shared,
regulator-agnostic CheckoutTrustBand component used across all four verticals:
- Payment step: 'full' variant -- money-back-if-we-fail-to-file guarantee +
256-bit TLS / Stripe / SOC 2 / PCI / fixed-price security badges + the right
'not affiliated with <agency>' disclaimer for the vertical.
- Review step: 'compact' variant -- guarantee + disclaimer (no badges).
- Order intro (VerticalOrderHeader, shared by all 49 order pages): thin green
'Secure checkout / Fixed price / Money-back guarantee' bar.
Guarantee copy is a real promise (full refund if we cannot file), worded so it
never overpromises a regulatory outcome (agency approval is not ours to give).
Vertical is inferred from the intake-step list via slugVertical() (single
source of truth, no hand-maintained slug table), with an explicit corporate
slug set since corporate services share the generic 'entity' step. Note: the
'dc_agent' step is the telecom D.C. process-agent designation, not corporate.
Also fixes two pre-existing mislabeled order-page headers surfaced by an
exhaustive header-vs-disclaimer audit: rmd-filing (Robocall Mitigation DB) and
new-carrier-bundle (VoIP carrier onboarding) are telecom, not healthcare/
trucking.
1. Email: add a 'Problem with your order? We're here to help' support band to
the shared htmlEmail() footer, so EVERY transactional email (confirmation,
portal link, receipts) has a prominent 'Get help with your order' button
linking to /contact. Less silent frustration -> fewer chargebacks.
2. NPI order form: entering a 10-digit NPI now auto-fills provider name, practice
state, and specialty from the live NPPES lookup (same API as the free
compliance-check tool), with a 'Found: <name>' confirmation. Only fills empty
fields so it never clobbers edits.
3. NPI order form: read ?npi= from the URL so the email 'Start my revalidation'
click lands with the NPI prefilled and the rest auto-filled (was being
ignored entirely before).
4. Support FAB: add the floating help button + panel to 27 static public pages
that were missing it (order, portal, trucking, survey, upload pages), so help
is one click away everywhere.
- 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).
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.
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).
- 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.
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
Astro hoists multiple step scripts from Wizard even when the step is not
rendered on the current order page. Several steps bound DOM elements at load
time with no presence check, causing null addEventListener errors on unrelated
order pages.
Add step-presence guards to the obvious offenders used by the shared wizard:
IccImportStep, Block6CertStep, WirelessStep, BundledServiceStep, CPNIStep,
and LNPARegionStep.
- Guard PaymentStep DOM bindings so its hoisted script no longer throws on
pages without a payment step.
- Remove the correct wizard nav element on successful intake submit
(pw-wizard-nav, not the nonexistent pw-wizard-footer).
The cold-visitor Finish flow created the order then called Stripe Checkout
directly, which is gated on intake_data_validated=true and returned 422
INTAKE_NOT_VALIDATED. Now call POST /compliance-orders/:n/validate between
create and checkout (matching the token-order flow), and surface any missing
required fields to the user instead of a raw HTTP error.
Add TruckingValueNotice on the 14 state trucking order pages. Each page now
shows, near the price:
- 'You already owe this' reframe: the tax/fee is a liability the moment the
carrier operates in the state; we do not create it.
- Per-program 'how you protect and recover money' bullets (IFTA fuel-tax
credits/refunds, IRP apportionment vs trip permits, avoided penalties,
citations, out-of-service, and protected operating revenue).
- 'Is this a tax write-off?' section: BOTH our service fee AND the state
tax/fee paid are deductible business expenses (IRC 162), with the exact
return line per entity type (Schedule C / 1120 / 1120-S / 1065), mirroring
the telecom TaxDeductibilityNotice pattern. Fuel taxes noted as part of
fuel cost.
Not tax advice; disclaimer included.
- Wizard Finish button: for visitors with no token/order (e.g. arriving via a
campaign ?dot= link), create the compliance order from collected intake data
and redirect to Stripe Checkout, instead of silently doing nothing.
- StateTrucking: Operating States no longer required; single-state/intrastate
carriers can finish (relabeled 'Other Operating States (if any)').
- build_trucking_campaigns: per-state programs (weight_tax/emissions) now derive
the CTA landing page from the deficiency flag's state suffix (e.g.
state_weight_tax_OR -> OR), not the carrier base state, so a GA-based carrier
flagged for OR weight-mile tax links to the OR page (not a mismatched one).
Campaign CTA buttons link to /order/<slug>?dot=1234567. Add a fast local-only
GET /api/v1/dot/census endpoint (vs the heavy 12s live /dot/lookup) and a ?dot=
branch in the Wizard that seeds intake_data from the carrier's cached FMCSA
census record (name, email, base state, city/street/zip, power units). The
existing StateTrucking step already prefills its inputs from intake_data, so the
form now shows up pre-populated. Best-effort: only fills empty fields, never
blocks the form, never overwrites visitor input.
Gap: dot-registration (new USDOT=MCS-150) routed through intake but never asked
for photo ID; usdot-reactivation, emergency-temporary-authority, carrier-closeout
(final MCS-150 + authority revocation), entity-dissolution, and entity-upgrade-
bundle weren't wired to collect it at all.
- intake_manifest: route usdot-reactivation, ETA, carrier-closeout,
entity-dissolution through the dot-intake step
- DOTIntakeStep DOT_SECTIONS: add dot-sec-photo-id for dot-registration and all
the above (operations + photo ID)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Client-side: Laplacian variance blur detection in photo quality check
(very blurry / somewhat blurry / acceptable / good)
- Server-side: async Ollama vision model validates uploaded image is a
real government ID (minicpm-v:8b), flags non-ID uploads
- Corporate check: sole proprietors now get yellow 'form an LLC' upsell,
formal entities get annual report/RA reminder
Previous approach relied on the main is:inline script block which
could be blocked by FCC step crashes. New approach: tiny self-contained
script right next to the QR img element, runs immediately, fetches
upload token and generates QR. Falls back to page URL on failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
QR code hidden via CSS on screens < 640px (phones). On mobile,
file input gets capture=environment so tapping the button opens
the camera directly. Tablets still show QR code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- API: POST /api/v1/id-upload/token generates upload token
- API: POST /api/v1/id-upload/:token receives base64 image, stores in MinIO
- API: GET /api/v1/id-upload/:token/status returns upload status + thumbnail
- Mobile page: sends image as base64 with upload_token
- Desktop intake: requests token, generates QR with upload URL, polls
every 3s for phone upload, auto-shows thumbnail when detected
- MinIO storage with presigned URLs for thumbnails
- Compliance order intake_data updated with photo_id_uploaded flag
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
QR code displayed inline below the upload button so truckers can
immediately scan with their phone to take a photo of their ID.
Clear instructions: 'Scan with your phone camera to take a photo'
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Yellow instruction box explains 3 methods in plain English
(phone photo, computer upload, scanner)
- One big orange "Add Photo of Your ID" button
- Webcam + QR code kept in code but simplified UI
- Accepted formats note
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Desktop users can now use their webcam to photograph their ID:
- Click "Use Camera" → browser requests webcam permission
- Live video preview with orange guide rectangle for ID placement
- Capture button takes high-res JPEG (1280x720)
- Cancel button stops webcam and returns to upload options
- Captured image goes through same quality check flow
- Works on Chrome, Firefox, Edge, Safari (desktop + mobile)
- No libraries needed — native WebRTC API
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Intake data now persists to DB after each step completion (non-blocking).
If browser crashes, data is recoverable from compliance_orders.intake_data.
Partial saves (_partial: true) only update intake_data without changing
payment_status or marking intake_data_validated. Final submit still
triggers the full validation + worker dispatch flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows uploaded image at larger size with automated quality checks:
- File size (too small = low quality warning)
- File type validation (JPEG, PNG, PDF, HEIC)
- Resolution check (minimum 400x250 for readable text)
- Aspect ratio check (should look like an ID card)
Green checkmark for passing checks, red X for issues.
Yellow warning box for quality problems with specific guidance.
Accept & Continue button to confirm, Retake to re-upload.
After accept, collapses to small preview with "Change ID" option.
Front of ID only (sufficient for FMCSA MCS-150 filing).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three upload methods:
- Upload File: standard file picker
- Camera / Scanner: uses capture attribute for camera on mobile
or TWAIN/WIA scanner devices on desktop
- QR Code: generates QR with current page URL so user can scan
with phone and take a photo of their ID on mobile
QR generated via api.qrserver.com (no library dependency).
Remove button restores all upload options.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FCC step scripts crash on DOT pages due to missing elements. By
using is:inline, DOT intake script runs independently, not in the
hoisted bundle. Stripped TypeScript annotations for plain JS compat.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wraps entire script in element existence check to prevent running
on FCC pages where other step scripts crash from missing elements.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>