FightThis
Tested prototype · not launched · AI, payment and email providers never exercised live · private repository
Summary
FightThis is a tested prototype that turns a confusing medical bill or insurance denial into a plain-language explanation, a dispute-strength estimate, a short plan and draft letters. What it demonstrates is guardrail design around a model: validate what it returns, check what it says, fail into a saved case marked for review, and never let a payment or a document depend on trusting the browser. Those safeguards are tested with a mock model and mocked providers; they have not been proven against a live model, real payments or real email, so this is not a safe-to-ship claim.
The problem and who it serves
Medical bills and insurance denials are hard to read, and people often pay or give up without checking them. FightThis explains a bill or denial in plain language, estimates how strong a dispute is and drafts the letters, while refusing to act like a lawyer or promise an outcome.
People facing a medical bill or a health-insurance denial they do not understand. The product scope is deliberately narrow (medical bills and insurance denials) although the data model allows other dispute types. It has no hosted instance and no users.
My role
Solo. I set the product scope, the safety rules (no legal or medical advice, no promised outcomes, no invented laws or deadlines, a human-review path), the architecture and the milestone plan. Implementation was AI-assisted under my direction over 2026-06-11 to 2026-06-13 and committed as one initial commit, so git does not show the individual steps. In September 2026 I added CI after finding that a clean clone no longer installed.
Architecture
The diagram is drawn from the code. Each external dependency sits behind an interface with a real implementation and a local one, and a health endpoint reports which mode each subsystem is in. In production and staging the app refuses to start with any mock subsystem or a weak secret.


Decisions and trade-offs
- Check meaning, not just shape. Schema validation guarantees the model returned the right fields. A second pass checks what those fields say: it rejects outcome promises and lawyer language, requires a real statement of uncertainty, and refuses a high dispute-strength rating that cites no source. Anything that fails goes to human review instead of the user. Phrase lists are blunt and will miss paraphrases, so they are a floor with a review path behind them, not a guarantee.
- Fail into a saved case, never a partial answer. A timeout, invalid output or provider error marks the analysis failed, keeps the case and its documents, sets it to “action needed” and writes a timeline entry. Five database-integration tests force each failure mode and check the stored state.
- Let only the webhook grant a purchase. The browser’s return from checkout is never trusted. Entitlements are created by the Stripe webhook alone, a repeated event ID is skipped, and refunds revoke access. The cost is a short delay between paying and unlocking. Database tests cover this with a stubbed Stripe client; it has not run against Stripe itself.
- Make every dependency optional in development and mandatory in production. Without keys the app uses a deterministic AI mock, local text extraction, inline jobs, disk storage and a file outbox, so it can be built and tested offline. Production configuration refuses to boot in any of those modes. The trade-off is two code paths per subsystem, and the real paths are the less exercised ones.
Verified results
| Measure | Result | Evidence |
|---|---|---|
| Tests in CI | 124 passed in 29 files | CI run 34171715157, main 5920540, 2026-09-07, with a PostgreSQL 16 service |
| What those tests are | 72 unit + 52 database-integration tests (12 files) | Rerun on a fresh clone against PostgreSQL 17, 2026-10-24: the same 124 pass. The AI provider, Stripe client and email are mocked; the database is real |
| Clean install | npm ci failed on a clean clone → passes, and is now the first CI step | Commit a76be80 (2026-09-07): a lockfile drift in optional native packages |
| Local walk-through | sign-up → email verification → consent → case → upload → text extraction → analysis → documents panel | 2026-10-24, synthetic bill, every subsystem in local or mock mode as reported by the health endpoint; screenshots on this page |
| Live integrations | 0 of 7 credentialed checks executed | The project’s launch-readiness document lists AI, image OCR, Stripe, email, S3, Redis and Sentry as not yet run |
Limitations and remaining work
- Not launched and not deployed; no users. A staging package (container, Railway config, runbook) is prepared but has never been run.
- No real AI model has analysed a document. The analysis on this page and in the tests comes from the deterministic mock provider.
- Payments, email delivery, S3 storage, the Redis queue and image OCR have only been exercised through mocks or local modes. Client-side error monitoring is not wired.
- The guardrails are rule- and phrase-based. They reduce risk; they do not make the output correct, and the product is not legal or medical advice. It is privacy-aware but not HIPAA-compliant.
- Losing the data-encryption key makes stored document text unrecoverable; key rotation needs a re-encryption plan that does not exist yet.
- Development history is a single initial commit, so the milestone-by-milestone work is recorded in project notes and documents, not in git.