Follow-ups in today's apps
Usama asked for doctor follow-ups in the apps people use today. Nabil built it all, but only for the new chat, which is off with no date to turn on. This page shows what to build in today's apps, how, and what you build.
What Usama asked for
His Paper frames for today's apps. Tap one to see it large.
Doctor
Chief Resident's plan gets a Follow-up group: a date chip and the reason under it.
A new Follow-up tile in the Action Plan. Accept & Edit fills it.
Required on a reply. Send stays off with "Add a follow-up first."
An earlier date still ahead: "already requested", not required. Keeping it tells the patient "No change".
The form: 1, 2, 4, 6, 8 weeks or 3 months, a date, "Why then, for Maya (required)", and a quiet "No follow-up needed".
Patient
No more "Accept" or "I need to ask" choice. The check button closes the response.
Follow-up is the last page: the date large, "In 4 weeks", "Why then", and the reminder line.
Same date as before: "No change", same schedule.
No follow-up needed: the letter's line, and "Write to Dr. Syed any time in this chat."
On the morning of the day, a push and an email. Tapping opens the chat with that doctor.
How we build it
This is a proposal. Nabil confirms the server part before anyone starts.
Picks a date and a reason, or "No follow-up needed"
Nabil's check_ins row, saved when the reply is sent
A date, "No change", or not needed
Push and email at 8am, tap opens the chat
One home for a follow-up
Nabil's table already holds a follow-up: a date with a reason, "not needed", or "kept the same". Today each row must hang off a new-chat card. We let it hang off a new-chat card or a visit reply from today's app. Then the reminder, "already requested" and the letter's sentence keep working, and nothing is thrown away when the new chat goes live.
The doctor's draft
While the doctor writes the reply, the follow-up is one more field on the saved plan draft, beside diagnosis, prescriptions, labs, imaging, skincare and products.
The send rule lives on the server
The server refuses a reply with no follow-up unless an earlier date is still ahead. The app holds the button with "Add a follow-up first." using the same rule, so the two never disagree. Web and phone share that rule.
One switch, off at first
Reuse the check-ins-enabled switch. Everything ships dark and turns on per doctor.
Screens are code only
No new native parts, so phones get it as an over-the-air update. No store release.
Reuse, don't rewrite
Date maths, chip values and the letter line already exist from Nabil's work. The chips sit on the server today and move to a shared package. The patient page words there are a plain stand-in: Usama's words win.
Screen rules, in plain words
- What the server sends stays in the app's data cache. No second copy.
- The form is a small set of named states: empty, picking, filled, not needed.
- Date and wording rules are plain functions in
models/, and they get today's date passed in. - Calls to the server live in the module's
api/folder. - Web and phone share the rules in
packages/encounter. Each app draws its own screens.
Server rules, in plain words
- The database itself refuses a date with no reason.
- Every read is limited to the doctor's own clinic.
- A send that is retried can never make two follow-ups.
- Old phone builds keep working when the new field appears. Only add, never change.
Who does what
Nabil, the server
- The follow-up row for today's visit reply, and the send rule
- "Already requested" and "No change"
- Chief Resident suggests one in today's visits (Usama approves the prompt)
- The letter's sentence
- The reminder
- What the patient apps read
You, the screens
- Shared rules in
packages/encounter - Doctor phone and web: tile, form, hold, Chief Resident group
- Patient phone and web: the last page, without the accept/ask choice
- Reminder tap opens the chat
The order
- Nabil lands the shared shapes and the switch. Small, first.
- You build the screens on mocks at the same time.
- Nabil's send rule and the reminder.
- Join up, check on the local stack, turn on for one doctor.
Worth knowing
- The new chat has no switch-on date. Nabil's work reaches nobody until it does.
- Today's patient apps still show "Accept doctor's recommendations" and "I need to ask follow-up questions", which Usama said to remove.
- The doctor's Send on the case page is called "Preview" on both doctor apps. The real Send is on the last preview page. The hold goes on Preview.
- The visit-send workflow is pinned for runs already in flight, so the server change needs care. Nabil's call.
- An old ehr-mobile build saves the whole draft. If it drops fields it does not know, it could wipe a follow-up. Worth one check with Nabil.
- Nabil's reminder says "Your check-in is due" with no doctor name or medicine, on purpose. Usama's frame names both. See Your input.
The journey, after we build it
Rebuilt from today's code, with made-up people (Maya R., acne, tretinoin; Dr. Usama Syed). Parts that exist today are drawn as they are. New parts carry a New chip and use Usama's exact words.
Doctor
ehr-mobile1
Breakouts along the jaw for 3 months, worse before periods.
Open the proposal
Opens Maya's case and taps the Chief Resident.
Today Sketch: order from details/view.tsx
2
Reads the suggestion, now with a follow-up, and taps Accept & Edit.
New group. Panel is today's (proposal-panel/component.tsx)
3
Sees the new Follow-up tile marked Required. Preview stays off.
New tile and hold. Tiles and Preview are today's (action-plan-tile.tsx, preview-provider-response.tsx)
4
Taps Add, picks 4 weeks, writes why, taps Save.
New sheet. Words from Paper C-D8
5
The tile shows the date. Preview turns on.
New tile. The letter line is Nabil's (check-in-sentence.ts)
5b · a later visit
An earlier date is still ahead: not required.
New Paper C-D7
6
Taps Preview, pages through, taps Send on the last page. The case shows what was sent.
Today "Your Plan / Sent to the patient" (send/copy.ts). New read-only tile
Patient
expo-patient1
Opens the doctor's response from the chat.
Today overview-page.tsx
2
Reads each part of the plan. The choice at the bottom goes.
Today page, choice removed (view.tsx:342, plans/copy.ts:33)
3
The last page shows the date. The check button closes the response.
New page, words from Paper C-P2
3b · No change
New Paper C-P3
3c · Not needed
New Paper C-P4
4
How is your skin handling the tretinoin? Tap to check in before your refill.
On the morning of the day, a push and an email arrive.
New for today's app. Words from Paper C-P7; Nabil's server words differ, see Your input
5
Taps it. The chat with Dr. Syed opens and she writes.
Today chat screen, sketch only. The tap target is new for today's chat (push-tap-actions.ts:213)
Web
ehr-web and the patient dashboard, the same steps in a line eachDoctor, ehr-web
Patient, patient-app-web dashboard
What you do
Six pull requests, in this order. Each one ships dark behind the switch.
Before you start
- Nabil agrees the plan and takes the server side.
- The shared shapes (the draft field and what the apps read) land first.
a. Shared follow-up rules
The follow-up field on the draft, the "Add a follow-up first." hold, and the date and chip rules reused from Nabil's work.
packages/encounter/src/send/send-control.ts · packages/encounter/src/view-model/action-plan/tile-kinds.ts, copy.ts · a new packages/encounter/src/view-model/follow-up/ · packages/api-schemas/src/check-ins/check-ins.schemas.ts (checkInDueOn, visitReplyNeedsCheckIn)b. Doctor phone: tile and form
The Follow-up tile, the form sheet, "No follow-up needed" and "already requested".
apps/ehr-mobile/src/modules/encounter/ui/details/actions.tsx · details/action-plan/action-plan-tile.tsx · a new details/action-plan/follow-up-tile.tsx · a new route under app/(main)/encounter/[encounterId]/actions/c. Doctor web: the same
Tile, dialog and hold on the web case page.
apps/ehr-web/src/modules/encounter/action-plan/ui/provider-actions.tsx · a new action-plan/follow-up/d. Chief Resident's Follow-up group
The group in the plan card, on phone and web, once Nabil's proposal carries it.
packages/api-schemas/src/chief-resident/display/proposal-panel.ts · apps/ehr-mobile/src/modules/encounter/ui/chief-resident/proposal-panel/component.tsx · apps/ehr-web/src/modules/encounter/chief-resident/ui/proposal-panel.tsxe. Patient: the last page, no accept/ask
The last page in its three states, and removing the accept/ask choice.
apps/expo-patient/src/modules/plan/modal/view.tsx · packages/patient/src/plans/copy.ts · apps/patient-app-web/src/modules/patient-dashboard/plan-review/ui/review/f. Reminder tap opens the chat
The phone already opens any chat by its id. If Nabil's reminder carries today's chat id, this may be a check, not a build.
apps/expo-patient/src/modules/push-notifications/push-tap-actions.tsProof
Each PR: screens checked on the local stack, with pictures and a short video.
Sources
Every path is at commit 0e28d64745f.
Where the code differs from the plan
- There is no "Send" on the case page. Both doctor apps show "Preview" there; Send is on the last preview page. The hold sits on Preview. (
submit-encounter/preview-provider-response.tsx:58,response/preview/footer.tsx:83) - The chip values live on the server only.
CHECK_IN_CHIPSis inapps/api, so the apps can't import it. It moves topackages/api-schemasfirst. (check-in-decision.schema.ts:25) - Nabil's patient words are not Usama's.
check-in-page-words.tssays "Check in on Fri, Dec 4" and calls itself a stand-in "until Hassan's follow-up pages replace this plain one". Reuse its date maths, not its words. - The reminder hides the doctor and the medicine on purpose. Today: "Your check-in is due" and "Your doctor asked to hear from you today. Tell them how it's going." Usama's C-P7 names Dr. Syed and tretinoin. Its tap carries the new chat's id. (
patient-check-in-due-copy.ts:10,push-renderer.service.ts:1310) - The doctor's "already requested" read is per new chat.
GET /v1/chats/{chatId}/check-in. Today's visits need one per visit. (chats.routes.ts:347) - Nabil's send check reads new-chat cards.
sendGateinsend-gate.tsworks on new-chat cards; today's visits reuse the plainervisitReplyNeedsCheckInandstandingDateAhead. Also, the existing "held" kind insend-control.ts:34means "draft still saving"; the follow-up hold fits better as a disabled Preview with a reason, like the draft hold.
Code
apps/api/src/db/models/replies/card-records.ts:567check_ins table. chat_card_id is required (line 574) and must point to a check_in card (lines 598, 626). Date needs a reason (line 639).apps/api/src/shared/check-ins/Nabil's service, repository, reminder, sentence, page, switchapps/api/src/shared/check-ins/check-in-switch.ts:21Reads check-ins-enabled per doctor, off by defaultpackages/api-schemas/src/posthog/feature-flags.ts:2116CHECK_INS_ENABLED, key check-ins-enabled, a release flagapps/api/src/routes/encounters/encounters.routes.ts:196POST /v1/encounters/{id}/plan existsapps/api/src/routes/encounters/encounters.routes.ts:413PATCH and GET /v1/encounters/{id}/plan-draft exist (also line 451)packages/api-schemas/src/encounters/schemas/plan-draft.schema.ts:370The draft's shape, where a follow-up field would gopackages/api-schemas/src/check-ins/check-ins.schemas.ts:24Outcomes date, not_needed, unchanged; visitReplyNeedsCheckIn 147, standingDateAhead 157, checkInDueOn 170packages/api-schemas/src/replies/send-gate.ts:85New-chat send check; visitReplyCheckIn at 183packages/chat/src/replies/models/reply-words.ts:140"Add a follow-up first."apps/api/src/shared/chief-resident/schemas/check-in-decision.schema.ts:25Chips 1, 2, 4, 6, 8 weeks and 3 monthsapps/api/src/shared/check-ins/check-in-sentence.ts:15The letter's "not needed" linepackages/chat/src/core/models/check-in-page-words.ts:62Stand-in patient page wordsapps/api/src/shared/product-notifications/patient-check-in-due-copy.ts:10Reminder title and body todayapps/api/src/shared/product-notifications/product-notification-push-renderer.service.ts:1310Reminder push, tap data reply_delivered with the chat idapps/api/src/shared/check-ins/check-in-reminder.ts:61Reminder checks the follow-up still stands, 8am her timeapps/api/src/temporal/queues/encounter/workflows/clarification-request-v6.workflow.ts:78Notes "plan's pinned sequence"packages/encounter/src/send/send-control.ts:34The shared Send controlpackages/encounter/src/send/copy.ts:55"Your Plan", "Sent to the patient"packages/encounter/src/view-model/action-plan/tile-kinds.ts:2The six tiles, in orderpackages/encounter/src/view-model/action-plan/copy.ts:8Tile namespackages/encounter/src/view-model/diagnosis/constants.ts:11"No formal diagnosis required", the checkbox patternpackages/encounter/src/chief-resident/proposal-cta.ts:9"Write my own", "Discuss with AI", "Accept & Edit" (16)packages/api-schemas/src/chief-resident/display/proposal-panel.ts:94Chief Resident groups; no follow-up todayapps/ehr-mobile/src/modules/encounter/ui/details/view.tsx:729Case page order: Chief Resident, then Action Plan (736)apps/ehr-mobile/src/modules/encounter/ui/details/actions.tsx:28Tiles in orderapps/ehr-mobile/src/modules/encounter/ui/details/action-plan/action-plan-tile.tsx:38Tile patternapps/ehr-mobile/src/modules/encounter/ui/details/submit-encounter/index.tsx:329Hold reason line under the buttonapps/ehr-mobile/src/modules/encounter/ui/chief-resident/proposal-panel/component.tsx:272Plan card group row; headings 852, 906apps/ehr-mobile/global.css:45Doctor app colours (brand 45, background 65)apps/expo-patient/src/modules/plan/modal/view.tsx:342Accept/ask choice; "Submit decision" at 425packages/patient/src/plans/copy.ts:33"Accept doctor's recommendations", "I need to ask follow-up questions"apps/patient-app-web/src/modules/patient-dashboard/plan-review/models/plan.ts:121Patient web uses the same choice copyapps/expo-patient/src/modules/plan/models/presentation.ts:44"PROVIDER RESPONSE" labelapps/expo-patient/src/modules/plan/components/overview-page/overview-page.tsx:79Overview pageapps/expo-patient/src/shared/modal/components/modal-page-header.tsx:22Page header and gradientapps/expo-patient/src/shared/modal/components/bottom-bar.tsx:137Bottom barapps/expo-patient/global.css:355Patient app colours (ink-forest 355, brand-secondary 98)apps/expo-patient/src/modules/push-notifications/push-tap-actions.ts:213Tap opens a chat by id, new or today'sapps/ehr-web/src/modules/encounter/action-plan/ui/provider-actions.tsx:114Web tilesapps/ehr-web/src/modules/encounter/ui/detail/submit/index.tsx:105Web Send controlHandoff
Section 2Doctor-requested follow-up: the action, C-D4 to C-D8, reasoning, letter, reminderSection 3Patient response, no accept/ask, the last page's three statesSection 6Who does whatNot checked
- That the visit-send workflow is pinned: seen only in a comment (
clarification-request-v6.workflow.ts:78). I did not readprovider-review-treatment-plan.workflow.ts. - That old phone builds keep a field they don't know when they save the draft.
- Whether today's Chief Resident reasoning for visits already decides a follow-up; the visit panel's groups have none.
- Which page the reminder email opens on patient web.
- The web patient pages and web doctor form were not drawn, only listed by file.
- The chat screen in step 5 is a sketch, not rebuilt.
Your input
One home, or kept apart?
Today's visit follow-ups go in Nabil's table beside the new chat's. Fine, or do you want them fully separate?
Reminder words
Usama's frame names Dr. Syed and tretinoin on the lock screen. Nabil's server says "Your check-in is due", with no name or medicine, on purpose. Which do we ship?
Who does the reminder tap?
The phone may already open the right chat if the server sends today's chat id. Nabil checks it, or you take it?
One doctor first?
Turn it on for one doctor (you, or Usama) first, then everyone?
Accept/ask: remove now or behind the switch?
Take the choice out for every patient now, or only where the follow-up page is on?