AI Workflow Example

A health record where the AI never gets the last word

Solene is a patient-owned record for the menopause transition and pelvic-floor health. Two apps share one design language: a patient app and a clinician portal. The rule that shaped every decision is that a physician approves anything a patient sees, and that rule is enforced in code rather than promised in a policy document.

Women's midlife healthHIPAA-aware from the first commitExpo and React Native patient appNext.js clinician portalPlaceholder identity, synthetic data
What it is

A patient-owned record for perimenopause, menopause and pelvic-floor health

Who it serves

Women navigating the midlife transition, and the clinicians who review their records

Surface

An iOS and Android patient app, plus a browser portal for the practice

Stage shown here

A UI and UX prototype on mocked fixtures, captured at one commit

The problem

Summarizing a clinical visit is easy. Being accountable for the summary is not.

Midlife women's health runs on long histories. The intake for this record merges two published instruments, the NAMS Menopause Health Questionnaire and the Australian Pelvic Floor Questionnaire, which together come to 204 questions across 17 chapters covering gynecologic history, hormone therapy, and bladder, bowel and prolapse function. No patient should answer all 204, and no clinician has time to read a transcript of one that they did.

That makes AI summarization obviously useful and quietly dangerous. A generated visit summary that reaches a patient unreviewed is not a product defect, it is a clinical one, and it is the practice that carries it. So the interesting design question was never how to generate a summary. It was where the human sits, how visible that seat is to the patient, and what stops anyone from routing around it later when someone is in a hurry.

Scope

The speciality is the product, not a category label

It would have been easier to build a general health record and describe it as a menopause one. The difference shows up in the question bank. Nearly half of it is gynecologic history, sexual health, bladder, bowel, prolapse and hormone therapy, which is exactly the territory general intake forms skip or reduce to a single checkbox.

That territory is also the part patients find hardest to raise in a ten-minute appointment. Putting it in a private, self-paced intake that reads the question aloud if you want it to, and lets you switch to a plain form if you would rather scan, is a clinical decision as much as a design one.

How we work

Five stages, each one leaving evidence behind

Every claim on this page traces to something in the repository: a file, a commit, or the output of a command. Where we could not verify something, we left it out.

Five delivery stages from brief to shipped
The process. The stages below walk through each one, with the decision that shaped it.
The stages

What happens at each stage, and what it produced here

01

Let the clinical instrument set the scope

Stage 1

The temptation on a health product is to design a friendly form and then work out what to ask. We inverted it. The question bank is a transcription of two published instruments, with the original wording kept intact, because a clinician reading the answers needs to know the question was the standard one.

The cost of that fidelity is length, so the flow engine does the work instead of the patient. 75 of the 204 questions carry a condition and never appear unless an earlier answer opens them. Those conditions are declarative data on each question, in the shape FHIR uses for Questionnaire.item.enableWhen, rather than branching code. That choice was made so the same bank can be handed to a real FHIR-backed system later without rewriting the logic that decides what to ask.

The weighting is the giveaway that this is not a general-purpose form: 33 questions on gynecologic history, 22 on sexual health, 16 on bladder, 13 on bowel, 8 on menopause and hormone therapy, 6 on prolapse. Ten are demographics. A patient answering the bladder chapter is reading the Australian Pelvic Floor Questionnaire's own wording, because a clinician comparing this answer to a previous one needs the question to have been the standard one.

The intake is also the first thing a new patient does, so it got the engagement work: a chapter-by-chapter chat view with a mascot that reads each question aloud, and a plain stepper for anyone who would rather scan a form. Both views are switchable mid-flow, at the same question.

A health record where the AI never gets the last word 1
204 questions in the bank; 75 appear only if an earlier answer opens them.
02

Ship the design system before any screen

Stage 2

The two apps cannot share components. One is Expo and React Native, drawing frosted glass with expo-blur and a Skia specular edge; the other is Next.js, doing it with CSS backdrop-filter. What they share is a package of plain TypeScript tokens: spacing, radii, blur, elevation, the color palette, the type scale, spring configs.

Component names are deliberately identical across both apps even though the implementations are not, so a change to a card is one conversation rather than two. To stop the tokens being bypassed under deadline, the lint config bans hardcoded hex colors and numeric font sizes in application source outright. It is not a review comment, it fails the build.

Accessibility was treated as a constraint rather than a phase. Text over glass gets an adaptive scrim to hold WCAG AA. Clinical-critical text, meaning dosages, instructions, consent and allergies, is always placed on a solid surface rather than a translucent one. A Calm mode that turns off blur and the animated background shipped from the beginning, alongside honoring the operating system's reduced-motion setting.

A health record where the AI never gets the last word 2
One token package, two native renderers, and lint as the enforcement point.
03

Make the approval gate a function

Stage 3

Every AI-produced item carries a review status. A single shared predicate decides whether a patient may see it, and every patient-facing render is guarded by that predicate. The rule is short enough to hold in your head and mechanical enough that it cannot drift.

The detail that makes it hold is that mock fixtures are required to carry a status too. An unreviewed item cannot exist even in test data, so a developer cannot accidentally build a screen against a shape the gate would never allow.

What the clinician actually reviews is one artifact, not four: the patient-readable summary, the SOAP note, the treatment plan and the suggested labs are approved together. The SOAP note is marked provider-only and is never released to the patient. Until approval, the draft carries a line on screen saying it is not medical advice.

A health record where the AI never gets the last word 3
The five review states. Four of them stop at the line; isPatientVisible lets exactly one through.
04

Audit screens against the spec, and act on the result

Stage 4

Three screens were scored against the design system and a written definition of done. The audit raised 15 findings and all 15 were resolved, with fixes recorded per finding: a missing error state, a skeleton shimmer that ignored the reduced-motion setting, a detail view using a raw modal instead of the glass sheet component.

The part we would point at is the six findings that were left as partial passes and written down rather than quietly closed, including some remaining magic numbers and a badge still using text glyphs instead of the icon component. An audit that resolves everything on the first pass is usually an audit that was scored generously.

Four days after that audit, one of the three screens was deleted. Fixing it was what made clear it should not exist.

05

Put the rules in a gate, not in a checklist

Stage 5

Seven commands run before anything merges: a React version guard, type-checking across three workspaces, lint at zero warnings, a formatting check, a prototype guard, a cross-app link guard, and the tests.

Three of those seven were written for this project specifically. The prototype guard fails the build if analytics SDKs, secrets, or anything matching a PHI pattern appear in application source, which matters on a codebase where the real thing will hold protected health information. The cross-app link guard checks that IDs referenced across the two apps actually resolve, so a document the portal links to cannot go missing from the patient app. The React guard exists because of a bug described further down.

The run behind this page came back green with 53 tests passing across the three workspaces. We ran it on the exact commit these screens were captured from, and on the unmodified code rather than the renamed copy used for the captures.

A health record where the AI never gets the last word 4
The gate output. Real, and re-run on the captured commit before publishing this page.
The mechanism

Approved together, released separately

The part of the design that prose keeps fumbling: the clinician approves one artifact, but what crosses into the patient app is decided per part. The clinical note is not withheld by policy, it is withheld by the release path.

A transcript produces four AI drafts, approved in one review, with the SOAP note stopping at the release boundary
Drawn from the artifact shape in the review fixtures, not an idealized version of it.
What review cost

Four things we changed our minds about

Each of these took work to build and then more work to remove. They are here because a review loop that never reverses a decision is not a review loop.

Deleting is a design decision

Two features shipped, were used, and were then taken out. In both cases the reason was that the thing was standing in for a job that already had a better home, or was describing something the build did not actually do.

A whole tab, audited and then deleted

BeforeFive tabs, with a Care tab holding three unrelated jobs
AfterFour tabs, each job on the screen that already owned it
The screen was audited on 07/10/2026 and four findings were fixed. On 07/14 it was retired: lab and imaging follow-ups moved to Home as a node on the patient's timeline, the treatment plan was already on the visit-summary reader, and medications were already on Profile. Nothing was deleted from the data model, only the screen, the route and the tab entry.

A voice intake deleted, then built for real

BeforeA voice interview with a waveform that was not listening to anything
AfterPush-to-talk on every question type, with native speech reading each question aloud
The first version was a shell. No audio library was ever installed, so the waveform animated over nothing and a listening state was displayed that did not exist. It was removed on 07/03/2026 rather than dressed up, and the chat presentation was kept because that part was genuinely a second interface. Voice returned on 07/07, after a written plan for push-to-talk and screen-reader behavior, and this time it records.

One clinical fact, one review story

The approval gate only works if there is exactly one path to the patient. Anything that creates a second path is a hole, even when each path looks reasonable on its own.

Two ways to author a plan became one

BeforeA clinician could hand-author a care plan on a separate screen, outside the AI pipeline
AfterThe treatment plan exists only inside the visit artifact a clinician approves
The authoring screen, its goal editor and its templates were deleted. The note, the plan and the suggested labs are one thing a physician signs off together, and the labs now carry IDs matching the follow-up items the patient will tick off in the app. The cross-app link guard checks that pairing on every build.

A dependency pin that crashed the app in silence

BeforeOne React version forced across the whole workspace by a root override
AfterEach app resolves the major it declares, checked by a guard that runs first in the gate
The override pulled the patient app down a React major, and the Skia reconciler then failed at bundle load. The symptom was a white screen with no build error, which is the worst kind of failure to inherit. Removing the override let each app resolve its own pin, and the guard now fails loudly instead of shipping.
The product

The two sides of one record

The portal screens below were captured from the running application. The patient screens were captured from an iOS simulator build, not a browser at phone width, so the layout shown is the real native one.

The design-system reference page in the portal
The design-system reference page, generated from the shared tokens.
Clinician dashboard showing the review queue, the day's visits and outstanding follow-ups
The clinician's day. What is waiting on a human review comes first, before the schedule.
Patient detail screen in the portal
A patient record in the portal, with clinical signals surfaced in the header.
Granular consent management in the clinician portal
Consent is granular and dated. Recording, de-identified data use and care-team sharing are granted and revoked one at a time.
The intake chat view on an iOS simulator
The intake as the patient meets it, chapter 1 of 17.
A pelvic-floor question from the Australian Pelvic Floor Questionnaire
The pelvic-floor chapter, in the published wording, with a four-point bother scale.
Patient app home screen showing a timeline of appointments and actions
The patient's home is a timeline, and four tabs. Everything on it has been approved.
Patient app record screen
The record itself, as the patient sees it.
An approved summary in the patient app, naming the reviewing clinician
Opening one names the clinician who approved it, and says plainly that it is not medical advice.
The patient's full record, ready to export and share
Patient-owned means portable: a full copy to hand to a family member or a new clinician, containing only approved content.
What this way of building produces

Measured, not estimated

Every figure here came from a command run against the repository at the captured commit. There are no adoption numbers or time-saved percentages on this page, because we do not have any we could stand behind.

204

Questions, 17 chapters

Transcribed from two published clinical instruments, with 75 of them conditional on an earlier answer.

53

Tests across three workspaces

47 in the patient app, 3 in the portal, 3 in the shared design package. Counted from the gate run, not estimated.

7

Commands in the merge gate

Three of them are guards written for this project: PHI patterns, cross-app links, and React versions.

15

Audit findings raised and resolved

With six remaining partial passes written down rather than closed, and one audited screen later deleted.

Summary

What to take from this

If you are weighing an AI feature inside a regulated product, these are the points we would argue for:

  • ✓
    Decide where the human sits before designing the AI output, and make that seat visible to the end user.
  • ✓
    Enforce the review rule in code, not in a policy document. Here it is one shared predicate that every patient-facing render is guarded by, and mock data has to satisfy it too.
  • ✓
    Let the clinical instrument set the scope of what you ask, and keep the published wording intact so a clinician can trust the answer.
  • ✓
    Ship the design tokens before the screens, and let lint fail the build when someone bypasses them.
  • ✓
    Write the guards your specific product needs. A generic gate would not have caught a PHI pattern in source or a broken cross-app ID.
  • ✓
    Treat a reversal as a working review loop rather than a mistake. Four features here were built and then removed, one of them four days after being audited and fixed.
  • ✓
    Keep planned work and delivered work clearly separate. Everything on this page exists at the captured commit; the prototype runs on mocked fixtures and no real patient data was used.
  • ✓
    Confirm BAA coverage with any third-party service before real protected health information touches it.

Building something that has to be reviewed?

We build custom software, web and mobile apps, and HIPAA-aware healthcare products. If you have a regulated workflow with an AI-shaped opportunity in it, we would like to see it.