Status: IMPLEMENTED — Tier 1 built, deployed & verified live (2026-08-23)
Scope: AI features that assist a patient while making a booking in the
Personal Web App (PWA). This is the "AI is parked" follow-on from the
PWA Features — Dental Specification:
that spec deferred all patient-facing AI ("they come in a later session once
the PWA is in place") — this plan picks up the booking part of that session.
Admin decisions from 2026-08-23 are incorporated (see §9 and §10).
When a patient books through the PWA, the easy parts are already solved:
the slot picker, up-front cost display, recall prompts and cancel/reschedule
are plain UI and work. The genuinely hard parts are the judgement-adjacent
moments:
- "Which appointment type / NHS band do I actually need?"
- "Is my problem urgent enough for the emergency slot?"
- "Which clinician will I see — is mine available?"
- "What will this cost, and what does the band actually cover?"
These are where patients guess wrong, book the wrong thing, or give up and
phone reception. That is where AI can help — provided it follows the house
standard: embedded in the booking screen, taking part in the actual step,
never a chat window, and never substituting clinical judgement.
¶ 2. Ground rules applied (house standard, adapted for patients)
The justified-AI checklist
and the product principles from the
Smart Scheduling pass §7
(2026-08-21) were written for staff workflows. For patient self-service they
apply with the same spirit, adapted:
| Staff-facing rule |
Patient-facing equivalent |
| Embed in the UI of the task |
Embed in the booking screen/step the patient is on |
| Never a chat window as the end result |
No patient chat bot; the booking flow stays the flow |
| Agent takes part in the process |
Widget produces a recommendation the patient confirms and acts on, inside the flow |
| Don't substitute professional judgement |
Advisory only — never a diagnosis; staff always see the suggestion alongside the patient's choice |
| Free staff for human-contact work |
Fewer wrong-type bookings, fewer calls to reception; human-contact work (speaking to a patient) goes to a human |
Every proposed feature must still answer the five questions; a "no" on 1–4,
or a "no" on 5, means reject.
From the dental PWA spec (Tier 1, live 2026-08-22):
- Choose appointment category → NHS Band 1/2/3 or private, cost shown up front (FP17 banding)
- Pick a clinician / slot
- Family/household: book for a linked member
- Recall-driven booking ("You're due a check-up" → 1-click)
- Emergency request (simple form → emergency slots; AI triage was parked)
- Manage: view / cancel / reschedule / reminders
Steps 1, 2 and 5 are the AI-relevant friction points. Everything else is
plain automation and stays plain.
- Appointment category — defaulted from the patient's record (NHS
band / private flag). Confirmed with one tap; the AI only reassesses when
the patient indicates something different (Feature A).
- Date preference — the patient chooses a date; the system allocates
the clinician (Feature D). No clinician picker.
- Family/household — book for a linked member, defaults read from that
member's record.
- Recall-driven booking — unchanged (plain automation).
- Emergency request — urgency check (Feature B, Tier 2).
- Confirmation & escalation — straightforward bookings are confirmed
on the spot; anything else is offered a handoff to reception, with a
callback request if reception is unavailable or closed (Feature E).
- Manage: view / cancel / reschedule / reminders — unchanged.
- Where it sits: booking step 1 — the appointment-category screen.
- What it does: the patient's default NHS band / NHS-or-private flag is
read from their record and pre-filled — the patient does not re-derive
it every booking. If they indicate something out of the ordinary (pain,
new problem, change since last visit), a compact widget with a few
structured questions plus an optional short free-text description suggests
whether the band or urgency should change, with a plain-language cost and
coverage explanation drawn from the real FP17 banding data.
- How it takes part: the suggestion appears as a pre-filled
recommendation on the actual booking form that the patient confirms —
they can change it. The practice sees what was suggested and what the
patient chose.
- What it does NOT do: no diagnosis, no clinical wording ("likely decay"
is out; "this usually fits a Band 2 examination with possible x-ray —
please confirm" is in), no auto-booking, no change to the record without
staff confirmation.
- Justification checklist: (1) plain automation covers the default-from-
record case — the free-text understanding when something differs is the AI
part, with the structured questionnaire as fallback; (2) judgement-heavy —
yes, reassessment is exactly the patient guesswork we want to reduce;
(3) embeds — yes, step 1; (4) takes part — yes, recommendation →
confirmation; (5) frees staff — yes, fewer wrong-type bookings and calls.
- Backend dependencies:
dental_fp17claim banding serving patient-facing
cost/band info (already on the spec's VERIFY list), band field on the
patient record, appointment-categories API.
- Where it sits: the emergency request form.
- What it does: an embedded check (structured questions + short free-text)
that advises one of: book a normal appointment / use the emergency
request / this sounds urgent — consider NHS 111 or A&E. Shown as a
banner on the form, not a chat.
- How it takes part: the recommendation sets an urgency flag on the
request that staff see; the patient still submits the form themselves.
- What it does NOT do: never blocks the form, never diagnoses, never
routes to A&E on the patient's behalf.
- Justification checklist: (1) plain automation covers simple cases; the
free-text is the AI bit; (2) judgement-heavy — yes, urgency is the
judgement-adjacent guess; (3) embeds — yes, the form; (4) takes part —
yes; (5) frees staff — yes, fewer mis-routed emergency requests.
- Backend dependencies: emergency-slot module (VERIFY), urgency flag on
the request record.
- Status: Tier 2 — only after the Tier 1 features are stable and reviewed.
- Where it sits: the booking confirmation screen.
- What it does: an inline card explaining what the booked band covers,
what to bring (payment, HC2/exemption certificate), and what happens at the
visit — personalised to the appointment type and household member.
- Note / likely verdict: if this can be templated from practice config,
it is plain content, not AI — and the default should be
config/content. AI only earns a place if it must interpret free-text or
unstructured practice notes. Marked "likely NOT justified — prefer
config-driven content" unless the user decides otherwise.
- Where it sits: booking step 2 — replacing the clinician picker with a
date preference.
- What it does: the patient's registered clinician is read from their
record. Allocation preference, in order:
- Registered clinician — if available on the requested date, allocate to
them.
- Usual cover / same-team clinician with availability — if the registered
clinician is away (holiday, not in).
- Any available clinician who can handle the appointment type.
- How it takes part: the allocation is made automatically as part of the
booking. The confirmation screen explains the choice in plain language
("Dr X is away that week, so I've booked you with Dr Y, who covers her
patients"). Staff can always see and override the allocation (audit trail).
- What it does NOT do: no arbitrary rotation without patient context; no
allocation that ignores the registered clinician when they ARE available.
- Justification checklist: (1) plain automation? — the core allocation
rules are deterministic and should be built as a rules engine, not AI;
the AI earns its place in the softer layer: explaining the choice to the
patient and handling edge cases the rules cannot (unusual requests,
specialisms, fair rotation of workload); (2) judgement-heavy — partially,
in the explanation/edge-case layer; (3) embeds — yes, the booking screen;
(4) takes part — yes, the allocation is part of the booking; (5) frees
staff — yes, reception no longer fields "can I see my dentist?" calls.
- Backend dependencies: registered-clinician field on the patient record
(VERIFY — shape of the dental patient/registration data), clinician
availability from schedule blocks, cover-mapping.
- Open item (per admin, 2026-08-23): whether allocation must respect
clinician scope (hygienist vs dentist vs specialist, which bands each
clinician covers) — left open by design. The allocation must be
configurable per practice rather than hard-coded, so the rule can be
tightened later without a rebuild.
- Where it sits: the end of the booking flow — before the confirmation
screen.
- What it does: decides whether a booking is straightforward or not.
- Straightforward → the appointment is confirmed on the spot, no
staff touch.
- Not straightforward → the patient is prompted to speak to a member
of staff. The agent offers to put the patient in touch with the
receptionist; if the receptionist is unavailable or it is out of
office hours, it leaves a message (callback request) for the patient
to be contacted.
- Proposed starter set of non-straightforward triggers (open for
refinement — see §8):
- New patient with no record / first visit
- Registered clinician unavailable AND no cover colleague for the
requested date (allocation cannot resolve it)
- Feature A reassessment flags something unusual (pain, band change,
urgency)
- Complex / flagged medical history on the record
- Exemption or payment queries that cannot be answered from the record
(e.g. no HC2 on file, private pricing question)
- Household booking where the linked member has no record yet
- Patient explicitly says they need to discuss something first
- How it takes part: Feature A's assessment feeds this decision — if the
AI is confident and the rules pass, instant confirmation; if not, the
escalation offer appears. The patient chooses the offer (talk now / leave
message); the agent never overrides a human.
- What it does NOT do: no silent deferral — the patient always gets an
outcome (confirmation or a clear next step); no blocking of the form.
- Justification checklist: (1) plain automation — the trigger rules are
automation; the AI part is the confidence assessment feeding the routing
and the natural-language offer; (2) judgement-heavy — yes, "is this
simple enough to confirm?" is a judgement; (3) embeds — yes, the final
booking step; (4) takes part — yes, it performs the routing; (5) frees
staff — yes: straightforward bookings need no staff time at all, and
staff time is spent only where human contact is genuinely needed.
- Backend dependencies (VERIFY for OpenCode): the current dental PWA
books directly into slots (DentalBookView) — instant confirmation is a
real change to that flow; a landing place for callback requests on the
staff side (queue/task, not a chat); reception availability / out-of-hours
detection.
¶ 5. Explicitly rejected (and why)
- Patient chat bot / Q&A window — the §7 anti-pattern; a chat window is
never the end result of an AI feature.
- AI slot picker — plain UI; fails checklist Q1.
- AI form auto-fill (medical history / intake) — plain form plus PII
risk; fails Q1/Q5 and DSPT caution.
- AI diagnosis or clinical triage scoring that staff never see —
substitutes professional judgement; fails the standard outright.
- Cloud-API processing of patient symptom text — see guardrails; sending
patient data to an external provider is not acceptable at DSPT Category 3
without explicit approval.
- Patient symptom text is PII. The AI layer for patient-facing features
must run on-tenant (the existing local Ollama endpoint on the VPS),
not external APIs, unless the user explicitly approves a reviewed provider.
The dental app already has env-gated Ollama usage (nomic-embed-text for
agent memory) — the same pattern applies to inference.
- Dummy/synthetic data only in dev, test and demos. Never real patient
data in any AI context.
- Advisory only, with an audit trail — the patient confirms every
suggestion and staff can see suggestion + choice on the booking record.
This includes allocations (Feature D) and confirm/escalate decisions
(Feature E).
- Callback requests are patient data — stored only for the contact
purpose, with the same PII discipline; no training on them; dummy data in
dev.
- No training on patient data. No free-text stored beyond the booking
record. No secrets in any config or documentation.
- Tier 1: Features A (re-scoped), D (allocation) and E (confirm /
escalate) — the core booking flow, after the base flow is stable.
VERIFY: FP17 banding patient-facing, band + registered-clinician fields on
the record, DentalBookView instant-confirm change, callback-request
landing place, reception availability / OOH detection.
- Tier 2: Feature B — depends on the emergency-module VERIFY and the
review of Tier 1.
- Feature C: decide content-vs-AI first (default: content).
- Confirm Feature C as plain content (recommended) or as AI?
- Is the on-tenant Ollama endpoint acceptable as the patient-facing
inference layer, or do you want a reviewed external provider?
- Who sets the band on the patient record — staff at registration only,
or can a patient-triggered reassessment (Feature A) update it with staff
confirmation?
- Clinician scope configurability (Feature D) — confirm the per-practice
configurable-cover approach while the scope question stays open.
- Confirm or adjust the non-straightforward trigger list (Feature E).
- Should the urgency check (B) also offer a "book a normal appointment
instead" shortcut back into the main flow?
- Keep this plan here under
development/projects/patient-web-app/, or
move it next to the dental PWA spec under development/projects/dental_app/?
- NHS band could and should be in the patient's record details, so they shouldn't need to address this each time they want to make an appointment.
- The patient should not need to pick the clinician - patients are usually registered with clinicians, so this should again be in their records; in cases where the clinician is on holiday or not in on the day the patient wants to make an appointment, so long as there are other clinicians available the AI should take care of this by alotting the patient with that clinician.
- §3.1 target journey added; step 2 becomes date preference + auto-allocation
(no clinician picker).
- Feature A re-scoped: default band read from the patient record; AI only
reassesses when something differs.
- Feature D (new): registered-clinician auto-allocation — rules engine plus
patient-facing explanation; scope configurability left open per admin.
- Feature E (new): confirmation & escalation routing — straightforward
bookings confirmed on the spot; otherwise reception handoff with callback
request fallback (reception unavailable / out of hours).
- §8 open questions updated to reflect the new decisions and the remaining
open items.
Tier 1 (Features A, D, E) is built, deployed and verified live on
test-client-dental. Feature C shipped as plain content (per the default).
Feature B (Tier 2) not started, as specified. Commit 1bc9d8b2 on develop
(dental_booking_app) + PWA patient-portal-pwa rebuilt. No LLM calls — all
rules are deterministic and on-tenant (DSPT Cat 3).
| Plan dependency |
Verdict |
| FP17 banding → patient-facing cost/band info |
Exists — dental/fp17.py NHS_BAND_CHARGES, patient_portal/services/booking.py::band_cost_for, GET /dental/booking/cost/. Plain-language coverage/exemption content was missing → added to DentalPractice.booking_config['coverage'] (configurable). |
| Band + registered-clinician fields on the patient record |
Missing — built. DentalPatientProfile += default_nhs_band, default_is_private, registered_clinician, medical_history_flags. Band stays staff-confirmed (patients never auto-write the record). |
DentalBookView instant-confirm is a real change |
Confirmed — create previously wrote status='BOOKED' immediately (services/booking.py:131-141). Now routed via evaluate_booking(): confirmed on the spot / needs_confirmation flag / not_booked + callback offer (decision: hard blockers do not book). No new appointment status was needed. |
| Callback-request landing place on the staff side |
Missing — built. New DentalCallbackRequest model + /dental/callbacks/ staff queue (reception-grade, practice-scoped) + needs_confirmation badges in appointments/triage. |
| Reception availability / out-of-hours detection |
Missing — built. DentalPractice.booking_config['opening_hours'] (per practice, configurable) + is_reception_available(). No separate practice-hours model existed. |
- Feature A —
dental/booking_assist.py::reassess(); endpoints GET /dental/booking/defaults/, POST /dental/booking/assess/ (patient_portal views); PWA reassessment widget + suggestion card. Audit: suggestion + patient choice stored in DentalAppointment.ai_suggestion_json.
- Feature D —
dental/allocation.py::allocate_clinician() (registered → cover_map → any capable); GET /dental/booking/allocation/; create accepts no provider_id. Cover mapping configurable per practice via booking_config.cover_map.
- Feature E —
dental/booking_assist.py::evaluate_booking(); extended create response; POST /dental/booking/request_callback/. Triggers configurable per practice (booking_config.non_straightforward_triggers). Reception availability from opening_hours.
- Feature C — plain content card from
booking_config['coverage'] (not AI).
- Q1 (Feature C content-vs-AI): content (per default).
- Q3 (who sets the band): conservative — staff-confirmed; reassessment never writes the record.
- Q7 (page location): kept under
development/projects/patient-web-app/ as instructed.
- Remaining §8 items (clinician scope configurability, trigger list, Feature B) left open per the plan; trigger list and cover map are JSON-configurable per practice so they can be tightened without a rebuild.