13 · Duty of care — emergencies and incidents
Alhuda is responsible for pilgrims far from home, many elderly. Every emergency is recorded, owned, worked through a checklist, and reflected immediately in the traveller's journey, group operations and finance.
Incidents
INC-001 · Incident types
Status: DECIDED 2026-09-17 (owner requirement) · Owner: Operations
Medical (illness, injury) · Hospitalisation · Death · Missing traveller · Lost/stolen passport or documents · Theft / loss of money or belongings · Flight disruption (delay, cancellation, missed connection) · Hotel problem (overbooking, unsafe room) · Transport failure · Security / crowd incident · Legal / police matter · Behaviour / group conflict · Other.
Enforced by: Incident.type check constraint and open_incident (20260919130100_duty_of_care_incidents.sql); labels in src/lib/incidents.ts; test supabase/tests/incidents.sql.
INC-002 · Every incident has
Status: PROPOSED · Owner: Operations
Traveller(s) and group; type and severity (Critical / High / Medium / Low); where (city, hotel, hospital, address); when it happened and when reported; who reported (tour leader, customer, family, staff); owner (a named staff member) and escalation contact; status (Open → In progress → Resolved → Closed); checklist (INC-010+); documents (reports, certificates, receipts); communication log (family, consulate, insurer, hospital, Saudi authorities); cost and who bears it (company, insurer, customer); resolution notes. All changes are recorded (AUD-001).
Enforced by: tables Incident, IncidentTraveller, IncidentChecklistItem, IncidentUpdate, IncidentDocument, IncidentNotification, TravellerStateEvent with audit triggers (20260919130100_duty_of_care_incidents.sql); reads need incidents.view, writes go through open_incident, assign_incident_owner, update_incident_details, update_checklist_item, add_incident_update, add_incident_document, change_incident_status; costs are recorded on the incident only — nothing is posted to the ledger while INC-011 is OPEN. Board and drawer at /operations/incidents. Test supabase/tests/incidents.sql.
INC-003 · Response times and escalation
Status: PROPOSED (working default adopted 2026-09-18, owner to confirm the roster) · Owner: Management Duty desk: one named staff member on duty at all times during a departure season, recorded in a roster (name, phone, shift). Until the roster exists, the duty desk is the Operations Manager.
| Severity | Owner assigned | Management informed | First update | Update interval |
|---|---|---|---|---|
| Critical (death, missing, hospitalisation) | 15 min | 1 hour | 2 hours (family contacted) | every 4 hours until resolved |
| High | 1 hour | 4 hours | 4 hours | daily |
| Medium | 4 hours | next working day | 1 day | every 2 days |
| Low | 1 working day | — | 2 days | weekly |
"Management" for a Critical incident = everyone holding incidents.close (Operations Manager and above) plus the CEO and GM. If an incident is not acknowledged within its window, it escalates one level: duty owner → Operations Manager → CEO/GM, each escalation recorded on the incident.
Notifications: internal alerts (in-app, push to staff on duty) send automatically — they are internal reminders, which INT-003 allows. Anything leaving the company — a message to family, a consulate, an insurer or a group — is drafted by the system but sent by a person (UX-001).
Built pending confirmation (display only today): INCIDENT_SLA_PROPOSED in src/lib/incidents.ts drives the "Assign now / Update overdue / On track" chip on the board and the dashboard queue; a Critical incident writes IncidentNotification rows for everyone holding incidents.close and logs the escalation on the incident (20260919130100_duty_of_care_incidents.sql). Automatic sending and timed escalation are built once the roster is confirmed.
INC-004 · Effects on the journey
Status: PROPOSED · Owner: Operations
Recording an incident updates the traveller's journey (JRN-001) and appears at the top of the Customer 360 page and the group's operations board. A hospitalised traveller is flagged on rooming, transport and flight manifests; a deceased or missing traveller is removed from headcounts and manifests only through the incident checklist, never by deleting data.
Enforced by (partial): BookingPassenger.travellerState set only by set_traveller_state (20260919130100_duty_of_care_incidents.sql) — it never deletes or cancels a passenger, booking, ticket or visa; ActiveIncidentBanner on the booking and group pages (Customer 360 mounts it in Wave 2B) and TravellerStateBadge on the rooming and flight-manifest lists, fed by group_traveller_states. Removing a traveller from headcounts, transport and printed manifests is Wave 3/4. Test supabase/tests/incidents.sql.
INC-005 · Family and emergency contacts
Status: DECIDED 2026-09-25 (owner: "emergency contacts section also"), amended 2026-10-03 (owner: "keep it mandatory only for direct customers") · Owner: Operations
A direct customer's booking requires an emergency contact in India (name, relation, phone) before it is created. For a business partner's booking it is optional; if one is started it must be complete (name and a phone that can be dialled). Staff can add or change it later on the booking page either way. Contacts are notified according to the incident checklist, with every call/message logged.
Enforced by: BookingEmergencyContact — one per booking, covering every traveller on it, written only by set_booking_emergency_contact() (staff with bookings.edit / bookings.create, or the booking's partner or customer), 20260928190000_every_booking_names_an_emergency_contact.sql. The staff booking wizard (and the staff phone app) require it for a direct booking and offer it for a partner's; the partner portal and the partner app offer it as optional; each records it right after the booking is created; the booking page shows it and lets staff change it; the tour leader's manifest carries it on every traveller and the leader's SOS tab lists who to call, plus the operations desk. The communication log (add_incident_update) records every call. Test: supabase/tests/every_booking_names_an_emergency_contact.sql.
Not enforced: a booking created another way (import, an old booking) may still have none; the leader's SOS tab and the booking page say so in red rather than hiding it.
Death abroad
INC-010 · Death checklist
Status: DECIDED 2026-09-17 (requirement) · checklist contents PROPOSED — verify with Indian Consulate Jeddah and Saudi authorities · Owner: Operations When a traveller dies, the incident owner works through, and records the date, person and document for each step: 1. Confirm identity and place of death; secure the passport and belongings. 2. Inform management (Critical escalation) and assign a family liaison. 3. Inform the next of kin in India; record their wishes (burial in Saudi Arabia or repatriation) and religious requirements. 4. Obtain the hospital / police report and the Saudi death certificate. 5. Register the death with the Consulate General of India, Jeddah (and obtain the consular documents required for burial or repatriation). 6. Arrange burial in accordance with Saudi rules (e.g. Makkah/Madinah cemetery through the authorised process) or repatriation (airline cargo booking, embalming/transport documents, NOC). 7. Notify the insurer and open the claim (policy from file 14). 8. Cancel or annotate the passport as advised by the consulate; cancel the return ticket and visa records. 9. Group and family re-planning: if the deceased was a mahram or guardian, re-plan for dependants (PAX-021/022) — room changes, travel companion, early return — each as its own recorded action. 10. Finance: unused services handled by the cancellation chain (file 10) with a compassionate refund policy (INC-011); costs of burial/repatriation recorded against the incident with who bears them. 11. Communication to the group and tour leader (dignified, approved wording). 12. Close the incident only when all steps are done or explicitly marked not applicable with a reason.
Enforced by: the twelve steps are seeded verbatim as the death checklist template and copied onto every death incident when it is opened; each step records state, who, when, the note and a document (20260919130100_duty_of_care_incidents.sql). change_incident_status refuses to close while a step is neither done nor explicitly not applicable (which needs a reason), and closing needs incidents.close plus resolution notes. Step 9 is supported by incident_impact_suggestions (INT-141) listing dependants, a possible mahram and room-mates as suggested tasks; step 10 records the cost and who bears it on the incident only — the cancellation chain and the compassionate policy (INC-011) are not wired. Test supabase/tests/incidents.sql.
Checklist templates for the other types (hospitalisation, missing traveller, lost passport, flight disruption, and a generic one) are seeded in the same migration as PROPOSED operational defaults — confirm them with Operations before release.
INC-011 · Compassionate policy
Status: OPEN · Owner: Management Decide how cancellation charges, refunds for unused services, and costs for accompanying family (early return, extra nights) are handled when a traveller dies or is hospitalised. Not built (OPEN): nothing in the incident module changes money. Death step 10 records the cost, the currency and who bears it (company / insurer / customer) against the incident and nothing else; no cancellation, refund or ledger entry follows from an incident until this is decided.
INC-012 · Traveller state
Status: PROPOSED · Owner: Operations
Passenger states Hospitalised and Deceased exist (LC-003, JRN-001). Deceased is set only by completing checklist step 1 with management confirmation; it can never be undone except by a correction entry with management approval.
Enforced by: set_traveller_state (20260919130100_duty_of_care_incidents.sql) — hospitalised / missing / deceased flag BookingPassenger.travellerState, found and discharged clear it; Deceased needs incidents.confirm_death and the incident number typed back, and completes death step 1 with the confirming manager and time; changing a deceased record is a correction with the same confirmation, written as a new append-only TravellerStateEvent. Browser sessions cannot touch the state columns at all. journey_incident_state(passenger) serves the journey (JRN-001). Test supabase/tests/incidents.sql.
Monitoring
INC-020 · Incident board
Status: PROPOSED · Owner: Operations
A live board of open incidents by severity and age, filterable by group and city, visible to Operations management and the duty desk; critical incidents also notify management by push and WhatsApp.
Enforced by: /operations/incidents (route gated on incidents.view) with columns by severity, cards showing type, traveller, group, city, owner, age and the response-time chip, filters by group, city and open only, and the detail drawer; list_incidents / get_incident (20260919130100_duty_of_care_incidents.sql). Push and WhatsApp notification is recorded only (see INC-003) until the escalation chain is decided.