# AF2 — make D0 isolation real + encounter kind from admissions + report writers keyed by ref_key

Read `_COMMON.md` first. Migration timestamp range: `2026_11_03_110000`–`110099`.

## Why
An audit found: (1) `history_questions.clinical_type` was added with NO backfill, so every `nut_*`/`dent_*` field is NULL (= core) and leaks into general clinics; packs never stamp it; the registration-field reader has no filter. (2) `encounters.kind/admission_id` are never written for inpatient encounters. (3) Clinical-document writers still look up by (company, encounter, ref_key) although the unique key is now (company_id, ref_key).

## Do
1. **Backfill migration** `2026_11_03_110000_backfill_history_questions_clinical_type.php`: set `clinical_type='nutrition'` where `code LIKE 'nut\_%'`, `'dental'` where `code LIKE 'dent\_%'`, and `'gyn_ob'` for the codes the `obgy` pack defines that are GYN-specific (read `Modules/Clinic/app/Support/ClinicProfilePacks.php` — keep genuinely shared fields like marital status as NULL/core; spouse/husband/obstetric fields → gyn_ob). Only update rows where clinical_type IS NULL. Idempotent, no down data loss (down = no-op).
2. `Modules/Clinic/app/Services/ClinicProfilePackService.php` `apply()` and `installMissing()`: stamp `clinical_type` from the pack (map pack code → type via `Modules\Clinic\Enums\ClinicalType` — nutrition→nutrition, dental→dental, obgy→gyn_ob (per-question: only the GYN-specific ones, same rule as the backfill — put that rule in ONE place, e.g. a static helper on ClinicProfilePacks), quick_reg/ksa_nphies→null). ALSO: on re-apply of an EXISTING question keep the clinic's `sort_order`, `label_ar`, `label_en`, `section` (they may have edited them) — only update structural `input_type/scope/category/clinical_type`. New questions get everything from the pack. Keep the existing behaviour that is_active/is_required are set only on create.
3. `Modules/Clinic/app/Http/Controllers/PatientProfileFieldController.php` `show` (registration fields): scope the questions with `HistoryQuestion::forClinicalType($type)` where `$type` comes from an optional `clinical_type` query param or, when a `department_id` is given, from that department's `clinical_type` (via `ClinicalType::from(...)->fieldScope()`); with neither, keep current behaviour (all) — but a department of type general must yield core only.
4. `Modules/Clinic/app/Http/Controllers/EncounterHistoryController.php`: where it uses the code-prefix filters (`isNutritionQuestion`/`isDentalQuestion`), switch to the `clinical_type` column via the encounter's department type; keep the prefix check only as a fallback when `clinical_type` is NULL (one-release fallback per design-data-layers.md).
5. Hospital `Modules/Hospital/app/Services/AdmissionService.php` (the `open()` encounter call ~line 126): pass `kind` (inpatient, or day_case when the admission is a day case — read the Admission model for its type field) and `admission_id`. Plus migration `2026_11_03_110001_backfill_encounters_kind_from_admissions.php` setting kind/admission_id from `admissions.encounter_id`.
6. `Modules/Clinic/app/Http/Resources/EncounterResource.php`: expose `kind` (string value) and `admission_id`.
7. Report writers — change the lookup key to `(company_id, ref_key)` only (keep setting encounter_id in the values): `Modules/Clinic/app/Listeners/DropLabReportOnEncounter.php`, `DropRadReportOnEncounter.php`, `Modules/Clinic/app/Actions/SubmitExternalResult.php`, `RefreshEncounterLabReport` (find its path). Use `withTrashed()`-aware lookup if the model soft-deletes (restore if trashed) so the unique index can never throw 1062.
8. `Modules/Clinic/database/migrations/2026_11_03_100003_make_clinical_documents_encounter_nullable.php`: when duplicates exist, do not skip silently — dedupe deterministically (keep the newest id per (company_id, ref_key), null the ref_key on older ones with a `data` note) then create the unique. (It already ran on dev; this matters for client installs.)

## Tests
`Modules/Clinic/tests/Feature/Af2IsolationTest.php`: pack apply stamps clinical_type; re-apply keeps a clinic-edited label and sort_order; registration fields for a general department exclude nut_/dent_ fields; EncounterResource exposes kind; report writer re-run with a different/null encounter_id updates the same row (no duplicate). Hospital: if the Hospital module has an admission test factory, add a test that admitting creates an inpatient encounter with admission_id; otherwise say so.

## Out of scope
HistoryService, HistoryQuestion model/resource/requests (E1e owns), anything prescription/attachments/routes/seeders/FE.
