Working implementation plan for configuring the dental app's triage system: a symptom-based urgency model that helps staff route and manage patients safely, built on what already exists in the codebase. Design research lives on Appointments — Triage. Scope: staff-facing triage first; patient PWA parked; AI features declined (see decisions below).
Status (2026-09-01): Plan agreed with product owner. Dataset acquired (Wildstash, 2,494 records). IMPLEMENTED by OpenCode 2026-09-01 on feature/dental-triage (commit 5c9e4f44), deployed VPS1 demo + VPS2 production. See handoff agents/handoff/from-opencode. Open questions §11 answered: staff-only checker; URGENT needs_confirmation only outside staff review; is_emergency zones kept; A&E guidance configurable per practice. UPDATED (2026-09-02): product-owner decision confirmed — AI for triage is declined outright, not parked (see §8). Triage is feature-complete barring minor modifications.
Give the dental surgery a triage system that:
The app already has a meaningful triage foundation — the work is configuration and gap-closing, not greenfield:
| Component | Location | What it does today |
|---|---|---|
| Appointment triage fields | dental/models.py (DentalAppointment) |
patient_reported_symptoms, ai_priority_score (1–5, default 1), ai_analysis_json, snomed_concept_id, needs_confirmation |
| Triage service | dental/ai_triage.py |
Rule-based triage + optional AI triage via agent capability dental_triage, with automatic fallback to rules |
| Symptom → urgency scoring | dental/snomed.py (get_urgency_from_symptoms) |
Keyword + SNOMED matching, 1–5 (e.g. swelling/abscess/trauma = 5, cracked/broken = 4, bleeding = 3) |
| Staff triage dashboard | dental/views.py (triage_dashboard), dental/templates/dental/triage_dashboard.html, route /dental/triage/ |
"Needs Triage" + "Triaged" queues with priority badges, SNOMED code, Confirm flag |
| Booking triggers triage | dental/views.py (appointment_create) |
Staff booking with symptoms runs rule-based triage, lands in queue |
| Emergency slots | patient_portal/services/booking.py (emergency_slots_for) |
Emergency slots derived from is_emergency schedule zones |
| Patient portal API | patient_portal/views.py (PatientDentalBookingViewSet) |
emergency_slots + emergency booking actions; booking-assist routing → confirmed / needs_confirmation / not_booked |
| Patient PWA | patient-portal-pwa/src/views/DentalBookView.vue |
⚠️ Currently has working emergency self-booking (loadEmergency / bookEmergency) — contradicts policy below |
| Dental Triage Agent | Agent ID 6 (integrations_agentconfig), capability dental_triage |
Prompt mirrors ai_triage.py; deepseek-chat |
| Practice feature flags | dental/models.py (DentalPractice.feature_flags) |
e.g. telehealth_triage (default off), ai_radiograph_analysis |
| PACS / radiograph hook | dental/radiograph_ai.py (Phase 8 pipeline) |
analyze_radiograph hook — image side of triage later; not in scope now |
Four tiers, mapped onto the existing 1–5 score so no data model break is needed:
| Tier | Typical symptoms | Target timeframe | Score mapping | Booking outcome |
|---|---|---|---|---|
| EMERGENCY | Breathing/swallowing difficulty, swelling spreading to eye/neck, uncontrollable facial bleeding | Immediate | 5 | Hard stop — no booking; emergency guidance (A&E / 999), alert staff |
| URGENT | Severe unmanageable pain, dental trauma, localised swelling/abscess | Within 24h | 4 | Priority booking into emergency slots (is_emergency zones), notify triage staff, optional photo upload, short slot hold |
| NON_URGENT | Mild/moderate pain, lost filling/crown/bridge, broken denture/wire, gum bleeding | Within 7 days / next routine slot | 2–3 | Standard booking + self-care (AAA) advice; may carry needs_confirmation |
| ROUTINE | Check-ups, hygienist, cosmetic, general enquiries | Routine availability | 1 | Ordinary calendar |
The existing keyword rules in snomed.py already score the bulk of this correctly; the plan formalises the tier mapping and adds the questionnaire + routing behaviour on top.
Replace reliance on free-text with a step-by-step flow (research: Appointments — Triage):
triageLevel: EMERGENCY. Never enters the calendar loop.State tracking (Rule 2): store structured answers (per-question selections + computed tier) on the appointment — extend ai_analysis_json (e.g. triage_answers, triage_level) so front-desk can see exactly why a slot was chosen.
Slot hold (Rule 3): urgent/priority slots unlocked through the flow get a 5–10 minute hold so abandoned checkouts don't leave slots locked.
Current dashboard is read-only. Target workflow:
triage_count) — keep in sync./dental/booking/emergency_slots/ + /emergency/). Policy is patients must NOT self-book emergency appointments. This endpoint/UI must be gated (urgent cases → request a callback / needs_confirmation instead of direct slot booking) as part of this work, since leaving it live contradicts the triage design even while the rest of the PWA is parked./root/work/dental_booking_app/data/wildstash/Dental_Cases_TrainingData.jsonl (5.6 MB, 2,494 records).messages (system/user/assistant). Patient profile embedded in the user message (age, sex, occupation, chief complaint, history, clinical findings, radiographic findings, medical history, medications, habits). Urgency embedded in the assistant text: **Urgency:** Elective (0) / Moderate (1) / Urgent (2).dental/management/commands/ingest_wildstash.py) that:
huggingface_hub installed in dedicated venv /root/venvs/hf-tools; CLI: hf download Wildstash/dental-treatment-planning-2.5k --repo-type dataset.Decision: AI will not be introduced to dental triage. Confirmed by the product owner on 2026-09-02, following review of the implemented system: the configured rules and the Wildstash validation dataset are more than sufficient, and there is no intention to revisit AI for this part of the app.
Rationale (agreed with product owner):
What this means now: the rule-based path (triage_symptoms_rule_based) is the only triage path in use. The dormant triage_symptoms_with_ai function and the Dental Triage Agent (agent config ID 6, capability dental_triage) remain in the codebase but are never to be wired in or promoted — treat them as dead code. Do not build AI triage UI. Triage is feature-complete barring minor modifications.
Ordered; each item references the files touched today:
dental/views.py (appointment_create), new template(s), dental/ai_triage.py: Q1/Q2/Q3 flow, hard stop on red flags, store triage_answers + triage_level in ai_analysis_json.dental/snomed.py / new helper: score ↔ tier ↔ target timeframe; urgent → emergency zones + 5–10 min slot hold; non-urgent → standard + self-care advice payload.patient_portal/views.py + patient_portal/services/booking.py + DentalBookView.vue: remove/deprecate emergency_slots/emergency direct booking; urgent patients get callback request / needs_confirmation instead.dental/views.py (triage_dashboard) + template: practice filter, re-run triage, override with reason (audit), mark handled, contact link.dental/management/commands/ingest_wildstash.py (parse urgency from assistant text, seed synthetic data / validate routing).dental/tests.py breaks the pre-existing VulnerabilityComplianceTests (flagged 2026-08-26 handoff) — isolate new tests carefully.needs_confirmation, or only when booked outside staff review?is_emergency zone model as the source of urgency capacity (recommended)?