Every dollar
shows its work.
Carelyze prices a dental visit from the carrier’s own portal before the patient sits down. I’m its founding design engineer — carelyze.com, the CareClinic workspace, and its patient links — and every decision below is shown with the artifact that forced it.
Founding design engineerProduct design · UX · React/TypeScript
Two-person buildThe Today redesigns and the confidence removal are mine; the rules engine is my co-founder’s lead.
In productionBay Area dental practices · Delta Dental + MetLife live


The number is the easy part. Defending it at the front desk is the product.
What a patient owes depends on the treatment plan, the clinic’s contracted fees, and benefits that live behind a carrier portal login with MFA. Front desks reconcile it by hand every morning — or trust the EHR’s stale snapshot and absorb the write-off. The design docs answer with owners of truth: the portal owns benefits, Dentrix owns the calendar, and ambiguity always has somewhere to go.
The accuracy doctrine, verbatim: “The carrier portal is the ground truth. Our job is to interpret it correctly and completely, not to rebuild it.”

The input arrives with someone else’s guess already on it.
Four procedures on tooth 14, a fee column, and the EHR’s own estimate: patient portion $580.50 — priced from whatever benefits were last keyed in. Behind it sits a six-form paper intake packet and a portal session someone has to babysit.
Carelyze reads this document, pulls live coverage, prices every line — and then puts its result next to the EHR’s number, match or differ-by-$X. The design docs call that figure “a hint, not ground truth.”
Four sources, one deterministic engine, a trace on every line
source_kind · a closed taxonomyParsed from the uploaded PDF — an LLM extracts codes and fees, never invents them.
Pulled live, per-code and per-patient, from the carrier's own provider portal.
The clinic's contracted fees — a real uploaded row, or one learned from claims.
A prior clinic billed amount — usable input, never a contract rate.
- Provenance — which source supplied each number
- rules_evaluated — the human-readable rule trace
- Flags — info · warn · error, never silent
- Clinician adjustments — appended and audited
The decision: provenance is an enum, not a caption. Eight source_kind values — the four sources above plus portal_category, estimator, no_data, and assumed — cover every number the estimator can emit, so the UI renders trust mechanically, and a mock or assumed value cannot be styled like a live one.
The confidence score looked reassuring. I deleted it.
The May estimator summarized trust as a percentage, with a threshold routing low scores to review. But a front desk about to quote a number doesn't ask how sure the software feels — it asks which number came from where. That reframe is the decision I'd defend hardest: on July 15 I removed the score outright, and a closed provenance taxonomy took its job.


One number answered for the whole estimate — and answered none of the front desk’s real questions: which fee, which benefit, which rule needs attention before quoting.
The frontend’s provenance.ts calls itself “the single source of truth for where did this number come from?”. Amber tones are counted and surfaced loudly — a mock pull degrades every derived badge, so a mock benefit can never masquerade as a confirmed one.
“Never guess; flag ambiguity for staff.”
Written for patient matching — wrong patient means wrong PHI and wrong estimate — and enforced everywhere after: ambiguous fee schedules become a staff pick, unmatched read-backs become review items, and a June commit is titled “careClinic: eliminate all coverage-percent assumptions and fallbacks.”
Missing data is a state, not a guess
fee_schedule_status · pricing_statuspriced against the complete carrier + plan contract
verified per-code fees; catalog known incomplete
prior clinic charges — provisional, not contract rates
several plan schedules fit — staff must pick
no fee schedule uploaded for this plan
carrier not in the catalog
“…the portal may affirm coverage, but dollar amounts must not be quoted until a fee is supplied.” A missing number renders as an em-dash, never a plausible-looking $0.00 — in a money interface, that difference is a refund conversation.
The decision: six named states instead of one empty cell. Every “we don’t know” has a different fix — upload a schedule, pick a plan, wait for a claim — so each ships as its own status carrying its own next action. A blank field would have made all six look like the same shrug.

Corrections sit beside the math, never over it
A clinician adjustment lives in its own table; “the estimator output remains untouched” and every change lands in an append-only audit log. A database constraint keeps edited rows honest: billed = insurance + patient, always.
Deleted, with a guard test
The removal wasn’t a revert waiting to happen. A validator strips the retired score from old stored estimates, and the estimate screen’s test suite now asserts the word “confidence” never renders. Deleting your own feature is a decision; testing for its absence is follow-through.
The EHR is a signal, not a source
The practice’s own estimate appears as a delta badge — match or differ-by-$X — never as an input to the calculation. Agreement is a trust cue; disagreement is the product working.
Two LLM call sites. Zero in the calculator.
The entire backend calls a model in exactly two modules — both turn a PDF into typed structure through a Pydantic tool schema. The extraction prompt is versioned in code, and one of its rules reads the way the whole product thinks: “If a field is missing or unreadable, use null. Do NOT guess.”
The tool boundary is the trust boundary — everything after it is deterministic.
Claude Sonnet · reads the FEE column only — EHR-side estimates are explicitly ignored
Claude Opus · one full-PDF pass into a typed fee table
Pure rules. No I/O, no LLM import — behind 654 backend tests
Write the intent. Believe only the read-back.
Estimates trust the carrier portal; scheduling trusts the practice management system — “Dentrix is the committed-schedule system of record; Carelyze is the working surface.” So a successful write job is not a confirmed appointment: the appointment is confirmed when Dentrix says it back, and the state machine makes any other claim unrepresentable. The whole bridge is invariant-tested against a faithful Dentrix mock — the real database comes at the on-site visit.
Who is allowed to say “confirmed”
appointment_transitions.py · actor-gatedA patient can ask or cancel — never confirm.
Write queued to Dentrix through the bridge; the UI shows the wait honestly.
Only the read-back actor may complete this edge. Staff can’t. The system can’t.
every externally visible state gets a badge — no silent states
When the bridge reports a successful write, the row moves only to awaiting_readback — “a write ack happens ONLY here (read-back), never from a job result POST.”
Read-back rows link to pending intent only on exactly one deterministic match; anything ambiguous becomes a Dentrix-origin row and a review item for staff.
Public booking and bearer links — token hashes only, an OTP step-up for intake — cover cancel, reschedule_request, intake, feedback. The page comment: “There is deliberately NO confirm action here.”
The decision: every surface gets its verbs — staff queue writes, patients request and cancel, and only the Dentrix read-back may say “confirmed.”
The front door moved twice. The version that stuck does less.
Upload-first, then patient-first, then day-first. The end state is a screen that intentionally does less: Today scans the imported schedule, triages coverage, and stops at patient selection — “the shortest path from an imported clinic schedule to an estimate,” in its own docstring. Both redesign commits are mine.
Process one PDF
Drop a treatment plan, get an estimate. Useful — and detached from the clinic's day.
Start from a person
“Step 1 is the patient, not the carrier” — coverage on file comes along for free.
Prepare who's next
“The shortest path from an imported clinic schedule to an estimate.” Today is the front door.
Import today’s appointments, then choose a patient to start their estimate.
The marketing site animates. The money screen refuses to.
One brand serves a persuasion room and a clinical room. Two token files share a wordmark and a typeface, and one rule outranks both palettes: color only ever means state, and green only ever means verified.
Two rooms, one wordmark
globals.css @theme · both repos“a royal purple drawn from the logo wordmark, with a warm coral accent from the logo wing” — the token file’s own header
Different violets on purpose — the site sells in the logo’s brighter #442089, the workspace works in a calmer #2c2060. The rule they share is stricter than a hex: color only ever means state.
A face per job
next/font · both reposThe components, running
shipped classes, live
Voice is a safety feature
Every right-column line is shipped product copy. Each pair encodes a policy: route blame accurately, restate the safety rule instead of apologizing, deliver the raw reason as a sentence a human can act on.
The craft is a motion budget
- Reading the treatment plan
- Checking insurance coverage
- 3Calculating the estimate
- Used before
- $0.00
- Applied by estimate
- +$544.50
- Remaining after
- $955.50
carelyze.com animates hard: a GSAP revenue-cycle ring with every node placed by ring(deg), SVG steps that draw their own strokes, an activity log at 620 ms per line — all gated behind prefers-reduced-motion.
CareClinic ships no motion library. Outside the scripted import demo, its one timed animation is the login carousel — 6 s per slide, deliberately not pausing on hover because “that made it look frozen while reading.” Everything else is a state change.
Carelyze’s signature element is ten pixels tall: the source pill that follows every number, with its exact origin one hover away.
Four hero headlines to find one bad morning.
I designed and built carelyze.com through all four eras — 66 of its 69 commits. Each pivot narrowed the audience and sharpened the verb, and the last rewrite came out of sales calls, not a brainstorm.
Four hero headlines, quoted from git
care-landing · 69 commits · 2025–2026“Supercharge Healthcare Quality with AI Analyst”
CAHPS/HEDIS analytics for clinics. Many capabilities, no urgent buyer.
“Automate Annual Physicals & Boost Revenue”
SMS/email outreach funnels for small clinics. Closer, still a nice-to-have.
“Know what every patient owes — before they’re in the chair”
One document, one number, one desk — the first version a front desk could run on a real morning.
“Fewer surprise bills. Less under-collection. No more portal marathons.”
The estimator becomes the first hire of a team; claims and denials become the roadmap.
The decision: stop describing capability, start naming a bad morning. The July hero isn’t a brainstorm output — the commit is titled “Homepage revision from sales feedback: pain-first hero, demo video, 6 carriers”.
What’s live, what’s built to contract, what’s a demo.
A case study about evidence should survive its own standard. Everything above is sourced from the product, its git history, and its design docs — and the boundary between shipped and aspirational is drawn below, the same way the product draws it.
A demo that can’t lie — it isn’t wired to anything.
carelyze.com/try had to feel like the product on first pass while being structurally incapable of touching PHI or a live carrier session. So it’s scripted end to end — three sample plans, a staged activity log — and the interface discloses that itself, on every screen. One tell survives: the demo still wears the coarse confidence chip the product itself deleted in July.
The demo tells on itself
carelyze.com/tryFully scripted — “no PHI, no carrier calls, no backend” is the first comment in the demo's source
Labeled in the UI itself: “This is a mockup”, “no file is uploaded and no carrier is contacted”
Three sample plans stress different failure surfaces: deductible unmet, already met, near the annual max
The decision: disclosure in the interface’s voice, not in fine print. A visitor who skips every paragraph still cannot mistake it for a live pull.

The Dentrix write path still ends at three NotImplementedError stubs — it has never met a real Dentrix database. The /today Dentrix import dialog — my own screen — plays a scripted timeline and, unlike every other demo surface in the product, doesn’t label itself as one in its UI; that label is the first fix I’d ship. And “tomorrow priced before it starts” is still a design contract, not a measured median — no clinic-week has been instrumented yet.
Provenance is more honest than a score — and much harder to demo.
Deleting the confidence number cost the pitch its one-frame proof: eight badge states take thirty seconds to explain, and the score took none. I’d make the same call again — and next time design the thirty-second version of provenance first. Every enum name, hex, and quote in this page’s diagrams is copied from the product; where a screen is recreated rather than captured, its label says so.