# Fable review — FULL: the built visit shell vs the approved analysis («شبه التحليل؟»)

Reviewer: Fable (design). Date: 2026-10-23. Review only — no code edited, nothing saved/signed/added/renamed/deleted on dev.
Reviewed build: `/app` `main-UGLV4HWO.js` = FE `279b10c3` (W1-FE-1 + W2-FS-3 + W2-FE-6 + W2-FE-2 + W4-FE-5 + W3-FE-4) on BE `9fae23849`.
Visits: general 34 · GYN 54 (24w, high-risk) + 62 (58y urogyn) · dental 37 · nutrition 43 (both tabs) · dental inpatient 27 (day 13/3) · widths 1600 / 1024 / 390.
Shots: `knowledge-base/plans/visit-shell-completion/shots/fable-FULL/` (index in §8). Prototype refs = `visit-shell-prototype.html` line numbers + screenshots `01…25`.
Synthetic passes (client-side `page.route` only, nothing reached the BE as a write): `mock-doctor-*` = layout payload with `allow_doctor_override:true` (dev has it off); `mock-insured-*` = encounter with `is_insured:true` (see P0-1 — it did not take, which is the finding).
Token `claude-visual-check-fableFULL` — revoked by exact name at the end.

---

## 0. Verdict

**The settings layer is the analysis.** Everything my spec (`FABLE-inplace-settings.md`) asked for is on `/app` and behaves like the prototype's «⚙ إعدادات الشكل / Program Setting» panel folded into the shell: the two-row purple bar (title · source · n cards · «＋ إضافة كارت» · «⚙ إعدادات التخصص» · «تلوين بالطبقة» / «الإعداد لمين؟» · level strip `افتراضي → العيادة ✓ → أنا` · one save that follows the scope · reset · إلغاء), the per-card ⚙ popover (ظاهر / إلزامي / Select|Text / 🔒 / source tag / «↺ رجّع الكارت» / «تم»), the header ⚙ on the dark strip, the «إعدادات التخصص» sheet from the inline-end edge (GYN 6 groups with «بيقفل: n كارت» + 🔒 on الصحة الجنسية, dental 8 groups + shell switch, general/nutrition «مفيش مجموعات», collapsed «عام للعيادة» with the allow-doctor switch and the 4 facility radio-cards + code chips, current = مستشفى), layer colouring of card edges, source tags on every head, dirty dots, and — in the synthetic doctor pass — «لي أنا / حفظ لي», read-only ظاهر/إلزامي rows with the note, RO sheet banner, «نسختك فوق شكل العيادة…», and the inline «احفظ / سيبها / ارجع» confirm. The picker is the prototype's picker (teal dialog, ★ block, category pills, sections, inline «＋ إضافة جديد»), and «✎ إدارة» turns it into the catalog editor exactly as §3 (strip, ⋮⋮, ↑↓, move select, ✎, 👁, 🗑, ★ untouched). Diagnosis = screenshot 06 (chapters, most-used, ★, «إضافة كود», first = primary). Rad = screenshot 02 (category grid). Finish panel has «افتح المحطة ↗» per line. Header is alerts-only with Rh/pregnancy/high-risk pills. Nutrition «الكشف» = the classic board 1:1 as 15 cards. Inpatient header reads «إقامة · جناح · سرير · اليوم 13/3» + «المعالج». Phone strip carries the bill / «باقي الإقامة» and opens the bottom sheet; tablet targets are 44px.

**Not yet «شبه التحليل» on what the owner sees first,** and one contract hole the owner will hit on day one:
1. **P0 — the insured path is dead on the real API.** `is_insured` is never sent by the BE (`EncounterResource` has no such key; the BE sign gate derives it from `ServiceOrder.payer_contract_id`), so forced dx / «مطلوب للمطالبة» / forced ⚙ row / ICD-only input never activate; the doctor only meets a bare 422 at sign.
2. The **vitals body** is still the tall tile panel on every visit (W1 P1-2, queued in W5-FE-7) — the first card on every screen is the one that looks least like 01/08.
3. The **lab station is unusable on this host**: the compact picker opens on «الكتالوج فاضي — اطلب من الدكتور أو الأدمن» because the whole catalog `forkJoin` dies on the `lis/investigations` 404.
4. The **GYN complaint catalog** shows 46 general rows first; the 20 GYN complaints are loose, uncategorised and in English.

So: **design layer = approved with fixes; overall = «شبه التحليل» once P0-1, the W5-FE-7 list and P1-1…P1-4 below land.** Nothing here needs a re-design; everything is a fix inside the shapes that already exist.

---

## 1. P0

**P0-1 · `is_insured` never reaches the shell.** Where: FE `visit-shell.component.ts:264` `isInsured = encounter()?.is_insured` (typed in `clinic-encounter.service.ts:93`, never populated); BE: `grep -rn is_insured Modules/ app/` = **0 hits** (no resource, no model, no module emits it); `GET /api/clinic/encounters/34` returns no key containing "insur" (checked live); `EncounterService::sign` L244-249 blocks via `DiagnosisService::blockSignForInsured()` (= `ServiceOrder.payer_contract_id` non-null). History: W1-BE-A's brief and report cover only the patient-header `insurance` object (B1) — the encounter-level flag the shell was built against was never in any BE brief, so it was never built. Effect on `/app`: Decision 1 (forced dx card, disabled ✕, «مفروض: مريض مؤمّن» row, ICD-only input, hard «لازم قبل التوقيع» line, positive «مطلوب للمطالبة ✓») is unreachable on any real visit; `mock-insured-21/22/23` show the dx card hideable, the ⚙ «إلزامي» switch off, the finish panel soft-only. Prototype 1209. Fix (BE, one field + one test): expose `is_insured` on `EncounterResource` using the same resolver the sign gate uses (the encounter's service order `payer_contract_id`, or `DiagnosisService::isInsured($encounter)` if it exists) — the FE needs no change. Re-run my W1 mock after it lands (that pass was synthetic too, which is why nobody saw this).

## 2. P1

**P1-1 · Header ⚙ popover is clipped by the app sidebar.** `v34-1600-12-hdr-gear.png`, `v34-1024-12-hdr-gear.png`: title «إعدادات «الهيدر والتنبيهات»» and all three row labels («التأمين في الهيدر», «آخر زيارة في الهيدر», «اسم الطبيب في الهيدر») are cut; probe: popover `left 1039 → right 1419` while the shell frame ends at ≈1362 (the RTL sidebar occupies 1378-1600). `visit-card-settings.component.ts` `pos()` (L339) clamps to `innerWidth`, not to the frame. Fix: clamp to `document.querySelector('.frame').getBoundingClientRect()` (inline-end = frame right in RTL) or align the popover's inline-end to the trigger's inline-end; keep the 380px width.

**P1-2 · Lab compact picker is empty and blames the admin.** `v34-1600-24-picker-lab.png`: «الكتالوج فاضي — اطلب من الدكتور أو الأدمن يضيف القيم» for a super-admin, while the console shows `404 /api/lis/investigations?…` on every general/GYN load. `encounter/orders/lab-orders.component.ts:399-430` `loadCatalog()` `forkJoin({services, investigations, packages})` → one 404 empties all three (the `error:` branch only clears the spinner). Fix: (a) W5-FE-7 P1-4 (point/guard the LIS call); (b) `catchError(() => of([]))` per source so clinic services still load; (c) a distinct failed-load state in the dialog — «تعذّر تحميل الكتالوج — جرّب تاني» — spec §3.7 only distinguished 403, add this one. Also: the dialog has no «＋ إضافة» row and no «✎ إدارة» (W2-FE-2 kept lab add standalone-only) — acceptable, but then the empty-state copy must not point at the doctor.

**P1-3 · GYN complaints are buried under the general list.** `v54-1600-20-picker-complaint.png`: pills «الكل · عام · تنفسي · هضمي · ألم · جلدية · مسالك · أعصاب · أطفال», sections start with «عام 6». API (`?lookup_key=complaint&specialty=gyn_ob`): 74 rows = 46 general children + 8 general categories + **20 `gyn_ob` rows with `parent_id:null` and `value_ar` = English** («Heavy menstrual bleeding», «Dysmenorrhea»…) → with no parent they would land in the loose «أخرى» section below the categorised general rows, in English (I saw the API rows and the top of the list; the bottom of the dialog was not shot). Prototype 741/814: the specialty's own list is what the GYN doctor picks from. Fix: seed a parent «شكاوى النسا» (code `complaint_gyn`) with Arabic `value_ar` for the 20 (BE seeder, idempotent on code); in the picker, order sections so rows whose `specialty` matches the visit come first, then core. Same check for dental/nutrition complaint rows if any.

**P1-4 · Specialty-card fields have no ⚙ (spec §2.3 rows dropped).** Dental edit mode shows 1 gear total (note-only on the chart, `v37-1600-13-gear-0.png`), nutrition 0, GYN first-visit none (`v54-1600-10-edit.png`). Spec §2.3 listed `nut_how_heard/nut_job/…`, `dent_pain_level/dent_radiotherapy/…`, `spouse_name/menarche/…` as visible/required rules (their consumers exist: `IntakeCompleteness.php:33`, `SpecCardCtx.fields`). W3-FE-4 §48 dropped them because the registry gives codes without labels. The owner's rule «مش من شاشات تانية» is only half met for the specialties. Fix (orchestrator decision): either (a) the popover lists registry codes with the history-question label (one lookup on `history_questions.code` → `label_ar`, the FE already has `HistoryQuestion`), or (b) accept and say so in the note: «حقول كروت التخصص بتتظبط من الحقول السريرية ↗» (today the spec cards have no note at all).

**P1-5 · Inpatient still has two entry forms + the intake wizard pops on a stay.** `v27-1600-52-scroll.png`: «جولة التمريض (NEWS2)» card keeps its own RR / O₂ / pain / I-O grid with «حفظ» under the MAR, while the vitals «المزيد» carries the same inpatient pack (W1 P1-5, queued — confirmed still live). New: the first-visit intake wizard auto-opens on encounter 27 — day 13 of an admission (`visit_count<=1` is true for any first encounter, stays included); in the scroll pass it was on top of every shot (`v27-1600-51…53`) although my dismiss had run at load — either it opened late (after the dismiss found nothing) or it re-opened on the tab switch; I cannot tell which from the shots, so treat "re-opens" as unconfirmed. Fix: gate auto-open to outpatient kinds (not inpatient / day-case / emergency).

**P1-6 · «إلزامي» tag still absent (W1 P1-1).** Dental jaw toolbar: «✕ إخفاء» greyed with no reason on the card (`v37-1600-10-edit.png`); the GYN gyn_cats head has no tag either (`v54-1600-13-gear-3.png` — its ✕ is greyed). Queued in W5-FE-7; keep it, and after P0-1 the insured dx gets the amber «مفروض: مريض مؤمّن».

## 3. P2 — polish (none blocks the merge)

- **P2-1 Exam ⚙ rows wrap** (`v34-1600-13-gear-3.png`): 8 regions × (label · ظاهر · إلزامي · Text|Select · 🔒 · source) break into two lines each, and the popover runs past the 1000px viewport (max-height 70vh → it scrolls, but the footer «تم» is off-screen on first open). Fix: 2-line grammar on purpose (label row / controls row) or drop the source tag to a tooltip on narrow rows; open scrolled to top with the footer sticky.
- **P2-2 Note-only popovers quote the sheet as text** («تتحكم فيها من «إعدادات التخصص»» on gyn_cats / dent_workspace, `v54…gear-3`, `v37…gear-0`): make «إعدادات التخصص» a link that opens the sheet (`svc.sheetOpen.set(true)`).
- **P2-3 Bare-card toolbar overlaps the chart toolbar** in dental edit mode (`v37-1600-10-edit.png`, `v27-1600-10-edit.png`): `.etb.over` sits on «الفكّين · اللثة · التقويم» and «الكل · اتعمل · مخطط». Give the bare card a top inset (toolbar height) in `.ed` mode instead of overlaying.
- **P2-4 Level wording**: card head says «العميل» (`VISIT_SHELL.FROM.CLIENT`), bar/popover/sheet say «العيادة». One word — «العيادة».
- **P2-5 Two durations on the GYN complaint card** (`v54-1600-30-finish.png`): shared «مدة الشكوى» (W3) above the GYN «أسئلة الشكوى (نسا) → المدة mo». Hide the GYN one when `complaint_duration` is visible, or map both to one store.
- **P2-6 Catalog has two cards named «الأدوية الحالية»** (`v34-1600-19-addp.png`: `home_meds` and the new `meds`). Rename `meds` → «أدوية الروشتات الحالية» (or «الأدوية الموصوفة»), keep `home_meds` «أدوية من خارج العيادة».
- **P2-7 Follow-up date input is native en-US** `mm/dd/yyyy` (`v34-1600-26-followup.png`); the «متابعة بعد» chips are the prototype — show the resulting date as text «→ 5 نوفمبر» next to the chips and keep the native input as the override.
- **P2-8 Finish panel scrolls under the sticky header** on long screens (`v54-1600-30-finish.png`: panel top hidden behind the header). `toggleFinish()` should `scrollIntoView` with the header offset (`scroll-margin-top`).
- **P2-9 Empty collapsed lines look broken** (`v34-1600-00-page.png`: التاريخ المرضي / الفحص / التحاليل / الأشعة heads with nothing): print a muted «—» / «لا شيء مسجّل» like the prototype's «أشعة: —».
- **P2-10 Tablet (1024) edit mode is heavy** (`v34-1024-10-edit.png`): bar wraps to 4 rows, each card toolbar is 2 rows of 44px (~110px per card × 11). Keep 44px but go icon-only for ↑↓◀▶/size/collapse on `vs-touch` (labels in `title`).
- **P2-11 Dental stay has no desktop «حساب الإقامة»** (dental `hideRail`): the bill only shows in the phone strip («باقي الإقامة 2,400» — `v27-390-00-page.png`). On a stay, show the end rail (or a `stay_bill` card) regardless of `hideRail`.
- **P2-12 Sheet for general/nutrition is 90% empty** (`v34-1600-14-sheet.png`): open the «عام للعيادة» details by default when the groups section is empty.
- **P2-13 Complaint picker manage row is dense** (`v34-1600-21-picker-manage.png`: 7 controls per row at 14px icons). Fine on desktop; make the icons 18px / 28px hit-area and group ✎👁🗑 with 6px gaps so «نقل إلى» (select) is not read as part of the label.
- Carry-forwards already in W5-FE-7 (unchanged, confirmed live): vitals tiles + «⚠ 1 قيمة خطرة» floating pill, GYN pack on a non-OB visit (`v62-1600-00-page.png`), LMP wrap, English dates in header/rail («Oct 22, 2026»), board footer count per tab (dental 1 / 7 / 10 كارت), 15-station GYN rail.

---

## 4. W1 review findings — status on this build

| W1 item | Now |
|---|---|
| P1-1 «إلزامي» tag | ❌ still missing (W5-FE-7) |
| P1-2 vitals tiles / compact unreachable | ⚠ tiles still; **collapsed line + rail line now exist** («79 كجم · 122/78 · BMI 24.4 · نبض 98 · 38.4°» — prototype format) ✓ |
| P1-3 nutrition pack surface | ⚠ unchanged (W5) |
| P1-4 `lis/investigations` 404 | ❌ still live and now empties the new lab picker (P1-2 above) |
| P1-5 inpatient two forms | ❌ still (P1-5 above) |
| P2-1 signed bar | not re-checked (no signed visit in scope) |
| P2-2 English dates | ❌ still |
| P2-3 LMP tile | ❌ still |
| P2-4 antenatal on non-OB | ❌ still (62) |
| P2-5 header meds chips | ✅ fixed — header is alerts-only |
| P2-6 finish copy / «افتح المحطة» | ✅ links present; positive insured line blocked by P0-1 |
| P2-7 footer count per tab | ❌ still |
| P2-8 danger pill | ❌ still |
| §C P1-8 edit bar wrap | ✅ fixed — intentional two rows |
| §C P1-9 32px targets | ✅ 44px on `vs-touch` |
| §C P1-10 GYN rail / unnumbered first-visit card | ⚠ team collapsed (16 rows vs 21); first-visit card still bare + unnumbered on head and rail (consistent) |
| §C P2-12 app-bar pills always | ✅ |
| §C P2-14 `.from` colours | ✅ (افتراضي grey, العيادة violet) |

## 5. Prototype feature checklist (as seen live)

| Prototype feature (line) | Live | Note / shot |
|---|---|---|
| Dark header: MRN · age · blood · insurance kv · last visit · doctor · «ملف المريض» (1171) | ✅ | insurance «نقدي» for cash; «المعالج» on a stay (`v27-1600-00`) |
| Header alerts only (allergies / anticoagulant / Rh / diabetes / pregnancy) (581) | ✅ | «Rh سالب», «حمل عالي الخطورة», GA/EDD/G3P1 pills (`v54-1600-00`) |
| App-bar pills layout source · encounter.kind · autosave (1166) | ✅ | «تسلسل محطات · عمود واحد», «عيادة خارجية», «لوحة كروت · 2 أعمدة»; autosave pulse not exercised |
| Inpatient chip bed · ward · day n/N + attending (1172) | ✅ | «اليوم 13/3» |
| Start rail: episode, previous visits (dx line), current meds + sentence, last results, timeline (1222-1224) | ✅ | timeline is a collapsed card (accepted) |
| Live summary rail numbered = station numbers (1230) | ✅ | |
| Orders & bill rail / «حساب الإقامة» (1231) | ⚠ | outpatient ✓; stay bill only in phone strip on dental stays (P2-11) |
| Quick bar opens pickers (1196) | ⚠ | bar renders; opening not exercised — `QUICK_TARGETS` wired per the W4 report |
| Flow stations: number, «تم», next opens, collapse to one line (1373) | ✅ | empty lines for no-data cards (P2-9) |
| Vitals: 6-col labelled inputs, pack row, computed BMI, danger colouring (803-811) | ❌ body / ✅ data | tiles; packs render (GYN antenatal, inpatient «المزيد», nutrition when added) |
| Complaint: picker + ★ quick + free text + duration + HPI (813-820) | ✅ | `v54-1600-30`; GYN double duration (P2-5) |
| History on the catalog (742) | ⚠ | no general lookup questions on dev — not visible |
| Exam: one catalog, regions, box per region, picker behind head button, «الباقي سليم» (830-832) | ✅ | `v34-1600-22-picker-exam` |
| Diagnosis: ★, chapters, inline add, first = primary, Select/Text rule (838-845) | ✅ | insured ICD-force blocked by P0-1 |
| Lab: chips + «اختيار من الفئات…» + grid dialog + ★ + free text (846) | ⚠ | UI ✅, catalog empty here (P1-2) |
| Rad: same (746) | ✅ | grid CT/XRAY (`v34-1600-28-picker-rad`) |
| Rx: ★ formulary, presets, interaction note element (848-852) | ⚠ | legacy prescription body (accepted); `rx.interact` element in ⚙ ✓; formulary ★ / presets not exercised (per W2-FE-2 report) |
| Follow-up: N days / date / instructions / book (864-865) | ✅ | native date input (P2-7) |
| Finish: hard/soft, per-line «افتح المحطة», sign + print menu (1209-1210) | ✅ | insured lines blocked by P0-1 |
| Signed: all collapsed, «موقّعة من …» | not re-checked | |
| Edit mode: toolbar per card, «＋ إضافة كارت» catalog, two-row bar (1113-1157) | ✅ | 12 addable on general incl. results / photos / meds / intake (`v34-1600-19-addp`) |
| Settings: per-field visible/required/input, elements, features, shell switch, scope, doctor override (1245-1265) | ✅ | per-card ⚙ + header ⚙ + sheet; doctor scope ✅ (synthetic); specialty-card fields ✗ (P1-4) |
| Facility type / contexts (526-527) | ✅ | 4 radio-cards + code chips + inline confirm (`v34-1600-15-sheet-global`) |
| «تلوين الحقول بالطبقة» (163) | ✅ | card edges L1-L4 (`v34-1600-11-edit-layers`) |
| Card head layer + source tags (1128) | ✅ in edit mode | accepted deviation |
| Required/forced cards marked (463, 1209) | ❌ | P1-6 / P0-1 |
| Picker: ★ per doctor, search, categories, inline add, manage (727-801) | ✅ | `v34-1600-20/21` |
| GYN: categories + modules cards, feature groups, private 🔒 (519, 1083) | ✅ | `v54-1600-14-sheet` |
| GYN complaint specialty list | ❌ | P1-3 |
| Nutrition 14 cards, 3-col board (448-461) | ✅ | 15 cards = classic (`v43-1600-40-tab-1`) |
| Dental chart required, plan/FMX cards (463-468) | ⚠ | chart ✕ disabled ✓; plan/FMX listed as addable per W3 report, not added/opened; «إلزامي» tag missing |
| Inpatient context cards MAR / round / notes / discharge (530) | ✅ | `v27-1600-52/53`; two vitals forms (P1-5) |
| Phone: one column, sticky «ملخص · فاتورة» strip, bottom sheet (1182) | ✅ | `v34-390-01-phone-sheet`, `v27-390-00` |
| Tablet ≥44px (§10) | ✅ | heavy edit mode (P2-10) |
| Keyboard Alt+F / Alt+↑↓ / Esc chain | not exercised | |

## 6. The prototype review's ❌ rows — closed?

| Review row | Status |
|---|---|
| A4 results card · A5 photos · A54 intake card · B66/68 | ✅ in the catalog (`v34-1600-19-addp`) |
| A31 vitals pack · A32 modes | ✅ packs / ⚠ modes exist, body still tiles |
| A47 settings panel · A48 fields · A49 input · A50 elements · A52 features · A55 doctor scope · A56 shell switch · A57 layer colouring | ✅ all in «تخصيص الشاشة» (doctor scope seen synthetically) |
| B4 Rh chip | ✅ «Rh سالب» |
| B35 «حساب الإقامة» | ✅ rail/strip (⚠ dental stay desktop, P2-11) |
| C14 history picker | ⚠ not visible on dev data |
| C23 doctor override of rules | ✅ (synthetic) |
| C30 managed catalog | ✅ «✎ إدارة» |
| Hidden-under-❌ but still open: insured forcing (Decision 1) | ❌ P0-1 |

## 7. Not verified (needs data or a write)
Save / reset / «شيل نسختي» / facility-type confirm → PUT; dirty-switch «احفظ» path; real doctor-override (dev off — synthetic only); real insured visit (none, and P0-1); signed visit; sign + print menu; follow-up save / «احجز» dialog; picker rename/hide/reorder/delete/undo (opened manage mode only); history picker (no general lookup questions); `dent_plan` / `dent_fmx` cards (not added); nutrition `results`/`photos` cards; hard finish block; `Alt+S` / `L` / `F2` keys; the 24h I/O line.

## 8. Shots (`shots/fable-FULL/`)
`v{34,54,62,37,43,27}-1600-00-page` · `-10-edit` · `-11-edit-layers` · `-12-hdr-gear` · `-13-gear-{0..4}` · `-14-sheet` · `-15-sheet-global` · `-19-addp` · `-20-picker-complaint` · `-21-picker-manage` · `-22-picker-exam` · `-23-picker-dx` · `-24-picker-lab` · `-25-rx-card` · `-26-followup` · `-27-rad-card` · `-28-picker-rad` · `-30-finish` · `-40-tab-{0,1}` · `-5{1..4}-scroll`; `v*-1024-00-page`, `v34-1024-1{0,1,2}`; `v*-390-00-page`, `v*-390-01-phone-sheet`; `mock-doctor-1{0..6}-*`; `mock-insured-2{0..3}-*`.
