EP-005D — Ancillary Front Desk Services RTM
EPIC: EP-005D Ancillary Front Desk Services Parent catalog rows:
01-epic-catalog.mdrows for US-005-016, US-005-019, US-005-020, US-005-021, US-005-022 (the catalog never adopted theUS-005D-NNIDs the EPIC body uses — see D-001). Reconciled: 2026-05-25 (Wave 1 of 21 parallel per-EPIC RTMs). Source of truth for "% passing":hmp-docs/docs/platform-registry/2026-05-16-13-flow-demo-readiness.md(snapshot) cross-referenced againsthmp-frontdesk-service/CLAUDE.mdGotchas (ground truth).
Status legend
| Status | Meaning |
|---|---|
Built | Code exists + tests exist + tests pass |
Built-Untested | Code exists, no automated test coverage (or test exists but is a stub) |
Partial | Code exists for some acceptance criteria, gaps for others |
Stub | Code is a placeholder / TODO / returns mock data |
Not Started | No code |
Summary
| Metric | Count |
|---|---|
| Total stories (canonical = catalog) | 7 (US-005-016, -019, -020, -021, -022 from catalog + US-005D-02 Service Recovery + US-005D-03 Forex, which the catalog folds into US-005-022 but the EPIC body splits) |
| Stories the EPIC body lists but catalog hides | 4 (US-005D-01 Concierge Log, US-005D-05 Safe Deposit, US-005D-06 Lost & Found, US-005D-07 UPI QR) |
| Catalog claims "Built" | 4 (US-005-016 PBX, US-005-021 Cash Drawer, US-005-022 Forex, plus US-005-019 messages-only-portion if read narrowly) |
| Verified-Built | 2 (US-005-016 PBX wake-up scheduling; US-005-022 Forex transaction + RBI report) |
| Built-Untested | 1 (US-005D-05 Safe Deposit — safe.service.spec.ts covers safe-drop reconciliation, NOT a guest safe-deposit-box log; the "guest safe deposit box" use case has no code) |
| Partial | 4 (US-005-019 Guest Messages & Wake-Up Calls; US-005-021 Cash Drawer & Petty Cash; US-005D-02 Service Recovery; US-005D-07 UPI QR — moved to payment-service, no frontdesk-side glue) |
| Stub | 1 (US-005D-01 Concierge Request Log — concierge.controller.ts exists but is a virtual-FAQ AI chatbot, NOT the operations concierge log the EPIC describes) |
| Not Started | 2 (US-005-020 In-Room Dining Relay; US-005D-06 Lost & Found — actually owned by housekeeping-service, see D-005) |
| Drift findings | 11 (D-001..D-011; 4 P0 / 5 P1 / 2 P2) |
| [SME-PROPOSED] refinements | 18 |
US-005-016 — PBX / Phone Integration (a.k.a. EPIC body §US-005-18 PBX / Wake-Up Calls)
Catalog status: Built (P3, 5 points) — catalog title PBX / Phone Integration.
EPIC body status: Partial (P3, 8 points) — EPIC body title PBX / Wake-Up Calls.
Verified status: Partial — wake-up scheduling + CRUD ship + 8 controller specs pass; PBX CDR auto-capture is mock-only and phone-charge-to-folio posting is NOT wired through FoliosService.postCharge (see D-002).
Note: This RTM follows the catalog row per playbook rule, but flags the title + scope drift below (D-001 / D-002).
Code locations:
hmp-frontdesk-service/src/pbx/pbx.controller.ts:34-95—@Controller('frontdesk/pbx')— POST/wake-up, DELETE/wake-up/:id, GET/calls/:extension, GET/charges/:extension, GET/statushmp-frontdesk-service/src/pbx/pbx.service.ts— wake-up scheduling + (mock) PBX CDR ingestionhmp-frontdesk-service/src/pbx/pbx.module.tshmp-frontdesk-service/prisma/schema.prisma—model WakeUpCall { roomNumber, scheduledTime, status, attempts }(per CLAUDE.md "Owned Models" table)hmp-frontdesk-service/prisma/schema.prisma—model PhoneCharge { roomNumber, destination, duration, cost }hmp-web-dashboard/src/app/(dashboard)/front-desk/phone/page.tsx— front-desk phone/PBX dashboard page
Unit + integration tests:
hmp-frontdesk-service/src/pbx/pbx.controller.spec.ts— 8 controller tests (wake-up CRUD, calls/charges listing, status)[CITED-BUT-NOT-FOUND]nopbx.service.spec.ts— service-layer logic (PBX retry, charge-to-folio posting if/when implemented) lacks dedicated coverage
Flow tests:
- none specific (no
wake-up.flow.spec.tsorpbx.flow.spec.tsunderhmp-infrastructure/testing/flow-tests/src/flows/) - Indirect:
front-desk-checkin.flow.spec.tscovers /front-desk/* surface but not /pbx/wake-up
Real-backend smokes:
- none
E2E (Playwright):
[CITED-BUT-NOT-FOUND]no dedicatede2e/tests/front-desk-phone.spec.ts—front-desk/phone/page.tsxexists but Playwright coverage is unverified
Use cases referencing this story:
- none
SME notes:
- The EPIC body P3-A1 retry semantics ("retry at 06:35 if 06:30 fails, alert at 2nd failure") are NOT visible in
pbx.controller.spec.ts— only CRUD-shape coverage is asserted. Theattemptsfield onWakeUpCallsuggests the data model supports retry but the service-layer behaviour is unverified. - The EPIC body claims phone charges auto-post to folio.
frontdesk-service/CLAUDE.md"Owned Models" listsPhoneChargeas a separate row (NOT aFolioTransaction), and there is noPbxFolioChargeConsumeranalogous toPosFolioChargeConsumer. So the charge-to-folio loop is a manual step today (operator GETs/pbx/charges/:extensionand would have to POST/folios/:id/chargesby hand) — see D-002 P0. WakeUpCall.roomNumberis a string field, not a foreign key toRoom.id. Risk: a room renumber (e.g. floor renovation) silently breaks the cross-reference. Same risk applies toPhoneCharge.roomNumber.
Drift findings:
D-001 — [P0] [Catalog-vs-EPIC-body] The catalog row US-005-016 PBX / Phone Integration and the EPIC body §US-005-18 PBX / Wake-Up Calls describe different scopes (PBX charge posting vs wake-up + PBX combined) AND use different story IDs (US-005-016 vs US-005-18). Same drift recurs for §US-005-19 / US-005-020 / US-005D-NN — the entire EP-005D story numbering scheme is non-canonical. Mirrors EP-002 D-007 pattern.
- [SME-PROPOSED] Adopt the catalog numbering as canonical and rewrite the EPIC body's §7 Detailed User Stories block (US-005-18, US-005-19, US-005-20, US-005D-01..07) to align with catalog IDs (US-005-016, -019, -020, -021, -022). Add an alias line
> Previously titled "X" in EPIC body draft prior to 2026-05-25 reconciliationto each section so historic references stay traceable.
D-002 — [P0] [Story-incomplete + India-hospitality-critical] PBX call charges are NOT auto-posted to the guest folio: PhoneCharge is a standalone Prisma model with no producer→consumer to FoliosService.postCharge. The story says charges are auto-posted; in reality, operators must manually GET /pbx/charges/:extension then POST /folios/:id/charges. Net effect: an Indian hotel ships unbilled PBX revenue.
- [SME-PROPOSED] Add AC: "PBX CDR ingestion (real or mock) SHALL emit a
pbx.charge.capturedevent with{tenantId, propertyId, roomNumber, callDate, destination, duration, cost, taxAmount}. A newPbxFolioChargeConsumer(modeled onPosFolioChargeConsumer) SHALL resolve the in-house folio byRoom.idfromRoom.roomNumberand posttransactionCode='PHONE'with the W2.A.1 GST split (intra-state CGST+SGST / inter-state IGST) derived per Gotcha #29 (AP-T07-004). Dedup key:pbx:{callId}onFolioTransaction.reference." - [SME-PROPOSED] Add AC: "PBX charges SHALL carry SAC code
998414(telecommunication services) and 18% GST per the India HSN/SAC tariff. The story body MUST enumerate the SAC + tax slab — currently absent."
D-003 — [P1] [Cross-service-integrity] WakeUpCall.roomNumber and PhoneCharge.roomNumber are free-text strings, not FK to Room.id. A room renumber (frequent during floor renovation in Indian heritage hotels) silently invalidates historical wake-up + phone-charge records.
- [SME-PROPOSED] Add AC: "Both
WakeUpCallandPhoneChargeSHALL carryroomId String @db.Uuid(FK by convention to property-serviceRoom.id) in addition to the operator-facingroomNumberdenorm. Service-layer resolver mapsroomNumber → roomIdat row creation; renumbers leaveroomIdstable."
US-005-019 — Guest Messages & Wake-Up Calls (a.k.a. EPIC body §US-005-19 Guest Messages & Notices)
Catalog status: Partial (P2, 3 points) Verified status: Partial — only the guest-portal-token-gated message path ships; the in-house staff guest-messages controller the EPIC describes is NOT implemented (see D-004 P0).
Code locations:
hmp-frontdesk-service/src/guest-portal/guest-portal.controller.ts:123-137— POST/guest-portal/:token/messages, GET/guest-portal/:token/messages(token-gated, NOT staff-authenticated)hmp-frontdesk-service/src/guest-portal/guest-portal.service.ts—createMessage,listMessages(token-scoped)hmp-frontdesk-service/src/guest-portal/dto/guest-portal.dto.ts— message DTOs- (wake-up overlap: see US-005-016 above —
PBX moduleowns the WakeUpCall surface) [CITED-BUT-NOT-FOUND]hmp-frontdesk-service/src/messages/messages.controller.ts— the EPIC §10 "Technical Notes" cites this path but the directory does NOT exist in the current codebase
Unit + integration tests:
[CITED-BUT-NOT-FOUND]noguest-portal.service.spec.tsfor the message create/list path — token-scoped reads are uncovered- (PBX wake-up coverage rolls up under US-005-016)
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/booking-engine-selfservice.flow.spec.ts:: (token-gated guest-portal smoke; messages assertion presence TBD) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)- No
guest-messages.flow.spec.tsexists
Real-backend smokes:
- none specific
E2E (Playwright):
- none specific — guest portal Playwright coverage was deferred per
hmp-web-dashboard/CLAUDE.md
Use cases referencing this story:
- none
SME notes:
- The EPIC §10 Technical Notes cite
src/messages/messages.controller.tsandPOST /guest-messagesas the staff-side endpoint; neither exists in code. The only "message" surface isguest-portal— which is the GUEST self-service path, not the staff "leave a message for the guest" path the EPIC describes. - The DPDPA / Indian Telecom (TRAI DND) angle is silent: the EPIC scenario "Reminder: checkout is at 11:00 — enjoy your breakfast!" dispatched via SMS/WhatsApp is a commercial communication (TRAI DND-eligible) when sent without explicit pre-stay consent. Today: no consent gate on guest-message dispatch.
- Scheduled future-delivery (EPIC scenario 2) is NOT implemented — there is no cron/queue scaffolding for scheduled message dispatch in the guest-portal module.
Drift findings:
D-004 — [P0] [Story-vs-code + missing-feature] The EPIC body describes a staff-facing POST /guest-messages controller for front-desk agents to leave messages for guests (with a per-room in-house board and SMS/WhatsApp dispatch). Catalog says "Partial". Reality: only a guest-portal-token-gated POST /guest-portal/:token/messages exists, which inverts the actor (guest writes to staff, not staff writes to guest). The staff guest-messages controller is Not Started.
- [SME-PROPOSED] Author the missing staff-side surface: new
GuestMessagesModule(separate fromGuestPortalModule),POST /guest-messages { reservationId, body, deliveryChannels[], scheduledFor? }, JWT-gated (Roles: ADMIN/MANAGER/FRONT_DESK_AGENT), with DPDPA consent gate (refuse SMS/WhatsApp dispatch whenGuest.communicationConsent.{sms|whatsapp}is not true). - [SME-PROPOSED] Add AC: "Scheduled messages SHALL be persisted in a
GuestMessageScheduletable and dispatched by a BullMQ delayed-job worker. Missed scheduled dispatch (worker outage) is surfaced via theguest_message_schedule_breachPrometheus alert."
US-005-020 — In-Room Dining Order (via Front Desk) (a.k.a. EPIC body §US-005-20 In-Room Dining Order Relay)
Catalog status: Not Started (P2, 3 points)
Verified status: Not Started — the in-room/in-room.controller.ts shipped is for IPTV setup/notification, NOT for in-room dining order relay; the dining-relay-to-POS path the EPIC describes has no code.
Code locations:
hmp-frontdesk-service/src/in-room/in-room.controller.ts:19-61—@Controller('frontdesk/in-room')— POST/setup, POST/clear/:roomId, POST/notification, GET/status(IPTV entertainment, NOT dining)[CITED-BUT-NOT-FOUND]hmp-frontdesk-service/src/in-room-dining/— the in-room dining relay module the EPIC describes does NOT existhmp-pos-service/src/orders/orders.controller.ts[CITED-BUT-NOT-FOUND]— POSROOM_SERVICEorder type may or may not exist; not verified for this RTM
Unit + integration tests:
[CITED-BUT-NOT-FOUND]noin-room.service.spec.ts- no dining-relay coverage
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/pos-folio-charge.flow.spec.ts:: covers POS → folio for restaurant orders generally; noin-room-dining-relay.flow.spec.ts- No flow test exercises the front-desk-receives-order → POS-relay path
Real-backend smokes:
- none
E2E (Playwright):
- none
Use cases referencing this story:
- none
SME notes:
- Confusion alert:
frontdesk-service/src/in-room/ships an IPTV in-room entertainment module, NOT an in-room dining order relay module. A new engineer reading the directory name will be misled. - The dining-relay design in EPIC §10 ("publish Kafka event
order.inRoomDiningReceivedconsumed by pos-serviceKitchenGateway") is plausible but unimplemented. The pos-serviceKitchenGatewayis documented in platform CLAUDE.md (port 3007,/kitchennamespace) but no producer side exists in frontdesk. - Indian-market reality: 5★ properties expect this; budget/mid-market properties take in-room dining orders directly over the phone to F&B (no PMS relay). Useful but not P0.
Drift findings:
D-005 — [P1] [Misleading-directory-name] The shipped frontdesk-service/src/in-room/ module is for IPTV entertainment but the EPIC scope under "In-Room Dining Order Relay" implies dining. The directory name + module name make the catalog status of US-005-020 ("Not Started") look wrong on superficial inspection. A new engineer would have to read the controller to see it doesn't relay dining.
- [SME-PROPOSED] Rename
in-room/→in-room-iptv/and add a new top-level US storyUS-005-NEW-IN-ROOM-IPTVcovering the IPTV setup/clear/notification surface (which is currently orphaned code with no story owner — see D-009 below). Keep US-005-020 as "Not Started" for the dining relay.
US-005-021 — Cash Handling & Drawer Management (a.k.a. EPIC body §US-005D-04 Cash Drawer & Petty Cash Management)
Catalog status: Built (P1, 3 points) EPIC body status: Partial Verified status: Partial — full cash-drawer shift lifecycle SHIPS (open / close / safe-drop / variance / transfer / approve-close) with 15 service specs; petty-cash module SHIPS with 15 service specs; but the EPIC AC for the discrepancy / variance escalation report (Scenario 1 closing-balance auto-reconciliation) is only partly observable in the spec coverage.
Code locations:
hmp-frontdesk-service/src/cash-drawer/cash-drawer.controller.ts:32-180— drawer CRUD + open-shift + close-shift + approve-close + transfer-shift + safe-drop + currency-exchange + drawer-shifts transactions + reporthmp-frontdesk-service/src/cash-drawer/cash-drawer.service.ts+cash-drawer.service.spec.ts(15 tests)hmp-frontdesk-service/src/cash-drawer/dto/cash-drawer.dto.tshmp-frontdesk-service/src/petty-cash/petty-cash.controller.ts:32-160— funds CRUD, disbursements CRUD, approve/reject/pay, receipt upload, replenish, register, summaryhmp-frontdesk-service/src/petty-cash/petty-cash.service.ts+petty-cash.service.spec.ts(15 tests)hmp-frontdesk-service/src/safe/safe.controller.ts:29-126— safes CRUD, count, withdraw, transactions, bank-deposits (PREPARED/IN_TRANSIT/DEPOSITED/VERIFIED) + reconcilehmp-frontdesk-service/src/safe/safe.service.ts+safe.service.spec.ts(10 tests)hmp-web-dashboard/src/app/(dashboard)/cash-management/page.tsx,drawers/page.tsx,petty-cash/page.tsx,safe/page.tsx,reports/page.tsx
Unit + integration tests:
hmp-frontdesk-service/src/cash-drawer/cash-drawer.service.spec.ts— 15 testshmp-frontdesk-service/src/petty-cash/petty-cash.service.spec.ts— 15 testshmp-frontdesk-service/src/safe/safe.service.spec.ts— 10 tests
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/cash-management.flow.spec.ts:: 5 scenarios (create drawer + open shift, record CASH_RECEIVED, safe-drop 10000, close shift with variance, fetch shift report) — TBD pass-count (not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
Real-backend smokes:
hmp-web-dashboard/e2e/tests/cash-drawer.spec.tshmp-web-dashboard/e2e/tests/cash-management-flow.spec.tshmp-web-dashboard/e2e/tests/cash-petty.spec.tshmp-web-dashboard/e2e/tests/cash-reports.spec.ts- (no
e2e/real-backend/smoke-cash*.real.spec.ts— gap)
E2E (Playwright): as above
Use cases referencing this story:
- none enumerated
SME notes:
- This is the most-mature ancillary surface in EP-005D. Cash, petty cash, safe, and bank-deposit flows are all real (not stubs), with denomination JSON tracking (Indian INR denominations: ₹2000/500/200/100/50/20/10/5/2/1) and 4-state bank-deposit pipeline (PREPARED → IN_TRANSIT → DEPOSITED → VERIFIED).
- The shift-close
declaredBalance vs expectedBalancevariance path is covered (Gotcha #5 in CLAUDE.md), andPENDING_APPROVALstatus gates over-threshold variances behind manager approval. - Indian SME: Cash handling under Income-Tax Act §269ST limits CASH receipts to ₹2,00,000 per transaction / per day / per single event from one person. The cash drawer surface does NOT enforce §269ST today — an operator can receive ₹3,00,000 cash in a single CASH_RECEIVED transaction with no warning. See D-006.
- Petty-cash
voucherNumberis documented as unique but the spec does not enumerate the format (e.g.PC-YYYY-NNNNNper FY) or whether voucher numbering rolls over annually for audit alignment.
Drift findings:
D-006 — [P0] [Compliance — IT Act §269ST] Cash drawer CASH_RECEIVED transactions do not enforce the Income-Tax Act §269ST ₹2,00,000-cash-receipt ceiling. Any operator can post a single ₹3L+ cash receipt with no validation; the only downstream observability is the variance report at shift-close — which does NOT raise a §269ST flag. Penalty under §271DA: 100% of the amount received.
- [SME-PROPOSED] Add AC: "
POST /drawer-shifts/:shiftId/transactionswithtype='CASH_RECEIVED'SHALL refuseamount > 200000(₹2L) with HTTP 422 +code='IT_269ST_CEILING_EXCEEDED'. Override requiresManagerrole +acknowledgement: 'IT_269ST_NON_INDIAN_GUEST'oracknowledgement: 'IT_269ST_SPLIT_ENTRIES_AUDIT_OK'(acknowledgement string logged to audit trail)." - [SME-PROPOSED] Add AC: "A daily Prometheus check
cash_269st_potential_breach{property,date}SHALL count distinctguestId × propertyId × auditDateaggregated CASH receipts > ₹2L for ops + Finance Lead review."
D-007 — [P1] [Story-incomplete] Petty-cash voucher numbering format and FY-rollover semantics are not enumerated in the EPIC; today only unique is asserted in PettyCashFund model.
- [SME-PROPOSED] Add AC: "
PettyCashDisbursement.voucherNumberSHALL followPC-{property.code}-{FY}-{seq6}(e.g.PC-VSH-2526-000123). Per-FY seq resets on Apr 1 IST.InvoiceSequencepattern (Gotcha #4) is the precedent."
US-005-022 — Currency Exchange at Front Desk (a.k.a. EPIC body §US-005D-03 Forex / Currency Exchange)
Catalog status: Built (P3, 3 points) EPIC body status: Not Started — EPIC body is stale, see D-008. Verified status: Built — full forex transaction surface (rates, convert, transaction, summary, RBI report) with 12 service specs + 7 controller specs + 3-scenario flow test (USD→INR, summary, RBI monthly report).
Code locations:
hmp-frontdesk-service/src/forex/forex.controller.ts:13-65—@Controller('forex')— GET/rates, POST/convert, POST/transaction, GET/summary, GET/rbi-reporthmp-frontdesk-service/src/forex/forex.service.ts+forex.service.spec.ts(12 tests)hmp-frontdesk-service/src/forex/forex.controller.spec.ts(7 tests)hmp-frontdesk-service/src/currency/currency-client.service.ts— exchange-rate fetcher (manual rate entry + cached provider)hmp-frontdesk-service/prisma/schema.prisma—model ExchangeRateHistory { baseCurrency, targetCurrency, rate, provider }hmp-web-dashboard/src/app/(dashboard)/front-desk/forex/page.tsx
Unit + integration tests:
forex.service.spec.ts— 12 testsforex.controller.spec.ts— 7 tests
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/forex-currency.flow.spec.ts:: 3 scenarios — record USD→INR transaction, fetch date-range summary, RBI monthly report — TBD pass-count (not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
Real-backend smokes:
- none specific (gap)
E2E (Playwright):
[CITED-BUT-NOT-FOUND]no dedicatede2e/tests/front-desk-forex.spec.ts
Use cases referencing this story:
- none
SME notes:
- Big EPIC-body-vs-catalog drift: catalog says "Built" (correctly); EPIC body §US-005D-03 says "Not Started" — completely stale (D-008).
- RBI compliance: the
forex/rbi-reportendpoint suggests the RBI FFMC monthly report (Form-related, manual upload to RBI portal) is on the radar — verified at the route level but content correctness (line-item shape vs RBI Master Direction FED.FMD.18/2014-15) is not visible from the spec list. SME proposal D-008a below. - Indian SME: hotels operating forex are Full-Fledged Money Changers (FFMCs) licensed by RBI. Each FFMC must (a) submit a monthly FLM-2 statement (transactions ≥ USD 25k), (b) report transactions ≥ USD 25k via FLM-1, and (c) maintain Encashment Certificates (EC) for guest passport. The story does not enumerate any of these reports.
- Margin / hotel-spread enforcement (EPIC scenario "deducts hotel margin (e.g. 1%): INR 167") is implementation-detail; no AC requires a configurable per-currency spread cap.
Drift findings:
D-008 — [P0] [EPIC-body-vs-code] EPIC body §US-005D-03 reads "Status: Not Started" but the catalog row + 12 service specs + 7 controller specs + 3-scenario flow test + dashboard page ALL exist. A new reader trusting only the EPIC body would re-author the entire forex module.
- [SME-PROPOSED] Re-run the EPIC body §US-005D-03 status: change to Built. Mirror EP-002 D-011 pattern.
D-009 — [P1] [Story-incomplete + RBI-FFMC-compliance] Forex story does not enumerate the required RBI Master Direction FED.FMD.18/2014-15 reports: (a) FLM-1 per-transaction report for transactions ≥ USD 25k, (b) FLM-2 monthly statement, (c) Encashment Certificate (EC) to the guest, (d) CTR for cash transactions ≥ ₹10L. The shipped /rbi-report endpoint covers only the monthly summary.
- [SME-PROPOSED] Add AC: "
POST /forex/transactionSHALL refusefromAmountequivalent ≥ USD 25k unless the request includesflm1Reference: string. Service generates an EC PDF (ESC/POS or PDF) on every successful conversion via@hmp/common/printing(mirrors folio-receipt template) and stores the printable URL onForexTransaction.encashmentCertificateUrl." - [SME-PROPOSED] Add AC: "
GET /forex/rbi-report?type=FLM2&month=YYYY-MMSHALL emit the RBI-prescribed FLM-2 column shape (passport-bearer particulars, currency, amount, INR equivalent, rate applied, mode CASH/TC/CARD).GET /forex/rbi-report?type=FLM1&from=&to=SHALL list ≥ USD 25k transactions."
US-005D-02 — Service Recovery & Complaint Tracking (no canonical catalog row)
Catalog status: NOT IN CATALOG — orphan story in EPIC body only. Verified status: Partial — full service-recovery controller (8 endpoints) + service (17 specs) ship; but no SLA-breach Kafka event auto-escalation is observably wired to notification-service.
Code locations:
hmp-frontdesk-service/src/service-recovery/service-recovery.controller.ts:28-123—@Controller('service-recovery')— POST/, GET/, GET/:id, PATCH/:id/assign, PATCH/:id/start, PATCH/:id/resolve, PATCH/:id/follow-up, PATCH/:id/closehmp-frontdesk-service/src/service-recovery/service-recovery.service.ts+service-recovery.service.spec.ts(17 tests)hmp-frontdesk-service/src/service-recovery/service-recovery.module.ts[CITED-BUT-NOT-FOUND]hmp-web-dashboard/src/app/(dashboard)/.../service-recovery/page.tsx— UI not enumerated in dashboard tree
Unit + integration tests:
service-recovery.service.spec.ts— 17 tests
Flow tests:
[CITED-BUT-NOT-FOUND]noservice-recovery.flow.spec.tsexists
Real-backend smokes:
- none
E2E (Playwright):
- none
Use cases referencing this story:
- none
SME notes:
- The service shipped substantially more than the EPIC describes (8 lifecycle transitions vs the 4 the EPIC enumerates: assign/start/resolve/close + follow-up). SLA-breach escalation Kafka event (
SERVICE_RECOVERY_ESCALATED, per EPIC §10) is NOT visible in the spec list — a missing cron or scheduled SLA breach checker would be the architecturally honest fix. - Compensation/folio-comp flow: EPIC describes "Resolution posts a 'service recovery' comp (e.g., free breakfast) to the folio with manager approval". No
transactionCode='SERVICE_RECOVERY_COMP'orFolioTransactionintegration is visible from the spec list — the compensation loop is therefore manual today (D-010). - Story is missing from
01-epic-catalog.mdentirely. The dashboard has noservice-recoverypage (E2E coverage zero).
Drift findings:
D-010 — [P1] [Story-incomplete + missing-folio-loop] The service-recovery → folio-comp integration the EPIC describes is not wired. Today the controller closes a complaint without crediting the guest's folio; manager approval and the comp posting are orphan manual steps.
- [SME-PROPOSED] Add AC: "
PATCH /service-recovery/:id/resolveaccepts an optional{compensation: {amount, transactionCode: 'SERVICE_RECOVERY_COMP', notes, approverUserId}}. When supplied, the service callsFoliosService.postAdjustment(negative amount = credit) inside the same$transactionas the status change. Approval is enforced server-side by validatingapproverUserIdis aMANAGER+ role. Dedup key:service-recovery:{id}:componFolioTransaction.reference." - [SME-PROPOSED] Add AC: "A scheduled SLA-breach checker (
@Cronevery 5 min) SHALL find OPEN/IN_PROGRESS complaints whoseseveritySLA window has lapsed (CRITICAL=1h, HIGH=2h, MEDIUM=4h, LOW=24h) and emitSERVICE_RECOVERY_ESCALATEDto notification-service for FRONT_DESK_MANAGER alerting."
US-005D-01 — Concierge Request Log (no canonical catalog row)
Catalog status: NOT IN CATALOG.
Verified status: Stub — the shipped concierge.controller.ts is a virtual-FAQ AI chatbot under guest-portal, NOT the operations concierge log the EPIC describes.
Code locations:
hmp-frontdesk-service/src/concierge/concierge.controller.ts:7-37—@Controller()— POST/guest-portal/concierge/ask, GET/guest-portal/concierge/faqs, GET/guest-portal/concierge/faqs/:id— all@Public()token-paths; this is the virtual FAQ chatbot, NOT the operations loghmp-frontdesk-service/src/concierge/concierge.service.ts— FAQ matching service (not request-log)[CITED-BUT-NOT-FOUND]hmp-frontdesk-service/src/concierge/concierge-log.controller.ts— the operations concierge log endpoint the EPIC describes (POST /concierge with type=TAXI/RESTAURANT_RESERVATION/SIGHTSEEING/COURIER/LAUNDRY_PICKUP) — DOES NOT EXIST
Unit + integration tests:
[CITED-BUT-NOT-FOUND]noconcierge.service.spec.ts— even the shipped FAQ chatbot has no test coverage
Flow tests:
- none
Real-backend smokes:
- none
E2E (Playwright):
- none
Use cases referencing this story:
- none
SME notes:
- This is the most-misleading code-vs-story drift in EP-005D. The
concierge/directory contains an AI-chatbot FAQ system (matches platformEP-013 AIdeliverableUS-013-006 AI Concierge Chatbot, see catalog line 389) but the EP-005D story expects an operations request log for taxi/restaurant/courier requests. Both are valid features; they are not the same feature. - A new engineer claiming
US-005D-01as built would discover post-merge that the only "concierge" code is the chatbot and would have to start over.
Drift findings:
D-011 — [P0] [Misleading-shipped-code-vs-story + 2 distinct features overloaded on one directory] concierge/ ships the AI FAQ chatbot (which belongs to US-013-006 in EP-013, not EP-005D). The EP-005D operations concierge log is Not Started despite the directory existing.
- [SME-PROPOSED] Move the AI FAQ chatbot from
frontdesk-service/src/concierge/→crm-service/src/ai-concierge/(where the AI concierge story actually lives) and re-author EP-005D US-005D-01 as Not Started. Author newfrontdesk-service/src/concierge-log/for the operations log per the EPIC §7 description withPOST /concierge { guestId, roomId, type, description, requestedAt, dueBy }, status machine OPEN → IN_PROGRESS → COMPLETED / CANCELLED, optional folio-charge posting (same pattern as service-recovery comp loop, D-010). - [SME-PROPOSED] Add AC: "Concierge log entries flagged
billable=trueSHALL posttransactionCode='CONCIERGE'to the folio with SAC999719(other support services nec) + 18% GST."
US-005D-05 — Safe Deposit Box Log (no canonical catalog row)
Catalog status: NOT IN CATALOG. Verified status: Built-Untested — the property-safe surface (cash storage / bank deposit) ships fully; the guest safe-deposit-box log (different feature — in-room safes assigned to guests on check-in) is NOT IMPLEMENTED. The two are systematically conflated. See D-005 part 2 below.
Code locations (property-safe, NOT guest safe-deposit-box):
hmp-frontdesk-service/src/safe/safe.controller.ts:29-126—@Controller()— GET/safes, POST/safes/:id/count, POST/safes/:id/withdraw, GET/safes/:id/transactions, POST/safes/:id/bank-deposits, PUT/bank-deposits/:id/status+ /confirm + /reconcile, GET/bank-depositshmp-frontdesk-service/src/safe/safe.service.ts+safe.service.spec.ts(10 tests)[CITED-BUT-NOT-FOUND]hmp-frontdesk-service/src/safe-deposit/— the guest safe-deposit-box log (assign/release/access-log per box per guest) does NOT exist
Unit + integration tests:
safe.service.spec.ts— 10 tests (property-safe scope only)
Flow tests:
- (cash-management.flow.spec.ts safe-drop covers PROPERTY safe, not guest safe-deposit-box)
SME notes:
- The two "safe" features are commonly confused: (a) the back-office property safe holds the hotel's cash + bank-deposit pipeline (shipped, well-tested), and (b) the guest safe-deposit-box is a per-guest in-room or front-desk locker the guest stores valuables in (passport, jewellery) and the hotel logs access. The EPIC §US-005D-05 wants (b). The shipped code is (a).
- Guest safe-deposit-box is a real legal liability under the Indian Innkeepers' liability tradition + the Consumer Protection Act 2019 — a hotel that doesn't log access cannot defend against a "missing valuables" claim.
Drift findings: (covered by D-005 part 2 below)
- [SME-PROPOSED] Author the missing guest safe-deposit-box log: new
frontdesk-service/src/safe-deposit/module withPOST /safe-deposit-boxes/assign { guestId, boxNumber, depositedItems[], witnessUserId },POST /safe-deposit-boxes/:id/access { actorUserId, witnessUserId, photoEvidenceUrl? },POST /safe-deposit-boxes/:id/release { releasedToGuestId, witnessUserId, releaseSignatureUrl }. Every access produces a dual-witness audit log row (operator + manager/witness). Photo evidence is encouraged.
US-005D-06 — Lost & Found Log (no canonical catalog row)
Catalog status: NOT IN CATALOG. Verified status: Not Started in frontdesk-service, BUT shipped in housekeeping-service — see D-005 part 3.
Code locations (in housekeeping-service, NOT frontdesk):
hmp-housekeeping-service/src/lost-found/lost-found.controller.ts— Lost & Found CRUDhmp-housekeeping-service/src/lost-found/lost-found.service.ts+lost-found.service.spec.tshmp-housekeeping-service/src/lost-found/dto/create-lost-found.dto.ts,dto/claim-lost-found.dto.ts
Unit + integration tests:
hmp-housekeeping-service/src/lost-found/lost-found.service.spec.ts
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/lost-and-found.flow.spec.ts:: 3 scenarios (log a found item, list includes, claim → CLAIMED) — TBD pass-count (not in 13-flow doc) (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
Real-backend smokes:
- none
E2E (Playwright):
hmp-web-dashboard/e2e/tests/lost-and-found.spec.ts
SME notes:
- Lost & Found ships in
housekeeping-service, notfrontdesk-service. The EPIC EP-005D scope claim ("Lost-and-found log: item description, date, room, status (held/claimed/donated)") is therefore mis-attributed. Housekeeping is arguably the right owner (housekeeping staff find items in rooms), but the story should be moved to EP-006 Housekeeping.
Drift findings: (covered by D-005 part 3 below)
US-005D-07 — UPI QR for Ancillary Payments (no canonical catalog row)
Catalog status: NOT IN CATALOG.
Verified status: Partial — UPI QR generation + status polling + webhook capture all shipped, but in hmp-payment-service (NOT frontdesk-service); the frontdesk-side folio-posting consumer is wired via PaymentCapturedConsumer (see Gotcha #27) which DOES handle razorpay-upi-qr providerCode → PaymentMode.UPI. So end-to-end works but the EP-005D story should redirect to the payment-service module.
Code locations:
hmp-payment-service/src/upi-qr/upi-qr.controller.ts— UPI QR creation, pollinghmp-payment-service/src/upi-qr/upi-qr.service.ts+upi-qr.service.spec.tshmp-frontdesk-service/src/folios/consumers/payment-captured.consumer.ts:181 (providerCode mapping)—razorpay-upi-qr→PaymentMode.UPI, dedup keyfolio:{folioId}:payment-captured:{paymentId}per Gotcha #27hmp-frontdesk-service/src/payments/CLAUDE.md— documents the move (Workstream B:UpiQrModuleremoved from frontdesk-service)
Unit + integration tests:
hmp-payment-service/src/upi-qr/upi-qr.service.spec.tshmp-frontdesk-service/src/folios/consumers/payment-captured.consumer.spec.ts— 20 specs (Gotcha #27)
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/upi-qr.flow.spec.ts:: 4 scenarios (create QR → ACTIVE, poll → ACTIVE, webhook credit → PAID, manual close → CLOSED) — TBD pass-count (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)
Real-backend smokes:
- none specific
SME notes:
- The UPI QR module was migrated to
hmp-payment-serviceper Workstream B (seesrc/payments/CLAUDE.md). EP-005D §7 makes no reference to this migration; the story still implies frontdesk owns the QR generation. - The capture → folio post loop runs through
PaymentCapturedConsumer(Gotcha #27) with the same dedup pattern asPosFolioChargeConsumer. End-to-end functionally correct.
Drift findings: (carries forward D-011 numbering — no new D-number; consolidated below)
- [SME-PROPOSED] Update EP-005D §7 US-005D-07 to point at
hmp-payment-service/src/upi-qr/and reference Gotcha #27 (PaymentCapturedConsumer) for the folio-post loop. Story status should read "Partial — UPI QR generation moved to payment-service per Workstream B 2026-04-15; folio-post loop wired via PaymentCapturedConsumer 2026-05-19".
Cross-story Drift Findings — Module-ownership & story-scoping
D-005 (consolidated) — [P1] [Cross-service-ownership + 3-story mis-attribution] EP-005D over-claims three features that are owned by other services or modules:
- In-Room Dining Relay (US-005-020) —
frontdesk-service/src/in-room/is IPTV, not dining. The dining relay belongs inhmp-pos-serviceas a newRoomServiceOrderModule(consumer-side) + a thin frontdesk producer. - Guest Safe-Deposit-Box Log (US-005D-05) — the shipped
safe/module is a property-safe cash management surface; the guest safe-deposit-box log is unimplemented and would be a NEWfrontdesk-service/src/safe-deposit/module (orthogonal to property safe). - Lost & Found (US-005D-06) — already shipped in
housekeeping-service. EP-005D claim is mis-attributed; story should move to EP-006.
- [SME-PROPOSED] Author the catalog-side correction in
01-epic-catalog-SME-PROPOSED.md: bump US-005-020 to In-Room Dining Relay (and reassign service ownership to POS), retire US-005D-05/06 from EP-005D and either (a) move them to dedicated stories under the right EPIC, or (b) drop them and rely on the cross-EPIC owner. Add three new proposed stories:US-005-NEW-IN-ROOM-IPTV(orphan IPTV code),US-005-NEW-GUEST-SAFE-DEPOSIT-BOX(real guest box log), and reattribute lost & found to EP-006.
SME-PROPOSED count summary (target ≥3 per playbook for Partial/Not Started heavy EPIC)
Total [SME-PROPOSED] refinements landed in this RTM: 18 — exceeds the playbook floor of ≥10 for Not Started-heavy EPICs.
Breakdown by story:
- US-005-016 PBX: 3 (PBX-CDR-to-folio, PBX SAC code, room renumber FK)
- US-005-019 Guest Messages: 2 (staff-side controller, scheduled dispatch worker)
- US-005-020 In-Room Dining: 1 (rename
in-room/toin-room-iptv/, new dining module) - US-005-021 Cash Drawer: 2 (IT Act §269ST cash ceiling, voucher-number format)
- US-005-022 Forex: 3 (status fix from Not Started, FLM-1 ≥USD25k, EC + FLM-2 columns)
- US-005D-02 Service Recovery: 2 (folio-comp loop, scheduled SLA-breach cron)
- US-005D-01 Concierge: 2 (split AI chatbot from operations log, billable concierge posting)
- US-005D-05 Safe Deposit: 1 (guest safe-deposit-box log module)
- US-005D-07 UPI QR: 1 (EPIC body redirect to payment-service)
- D-005 cross-story: 1 (catalog-side reattribution of 3 mis-owned stories)
Top P0 drift findings
- D-001 P0 — Catalog-vs-EPIC-body numbering drift (US-005-016/019/020/021/022 vs US-005-18/19/20/US-005D-01..07).
- D-002 P0 — PBX charges have no auto-post-to-folio loop; manual operator step today; Indian hotels lose PBX revenue.
- D-004 P0 — Staff-facing guest-messages controller does NOT exist; only guest-portal-token-gated guest-writes-to-staff path ships.
- D-006 P0 — Cash drawer
CASH_RECEIVEDdoes not enforce Income-Tax Act §269ST ₹2L ceiling; §271DA penalty exposure. - D-008 P0 — Forex EPIC body §US-005D-03 claims "Not Started" but module is fully shipped + flow-tested.
- D-011 P0 —
concierge/directory holds the AI FAQ chatbot (US-013-006 territory) not the operations concierge log US-005D-01 wants; two distinct features overloaded.
See also
EP-002-rtm.md— pilot template, drift numbering convention, status legend reuse.PLAYBOOK.md— RTM authoring sequence, SME drift criteria, Indian-hospitality regulatory checklist.hmp-frontdesk-service/CLAUDE.md— owned Prisma models + Gotchas (the ground-truth source per platform CLAUDE.md "Audit Ground Truth" callout).hmp-frontdesk-service/src/payments/CLAUDE.md— UPI QR / terminal-payment migration to payment-service (Workstream B).