Skip to main content

EP-005D — Ancillary Front Desk Services RTM

EPIC: EP-005D Ancillary Front Desk Services Parent catalog rows: 01-epic-catalog.md rows for US-005-016, US-005-019, US-005-020, US-005-021, US-005-022 (the catalog never adopted the US-005D-NN IDs 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 against hmp-frontdesk-service/CLAUDE.md Gotchas (ground truth).

Status legend​

StatusMeaning
BuiltCode exists + tests exist + tests pass
Built-UntestedCode exists, no automated test coverage (or test exists but is a stub)
PartialCode exists for some acceptance criteria, gaps for others
StubCode is a placeholder / TODO / returns mock data
Not StartedNo code

Summary​

MetricCount
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 hides4 (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-Built2 (US-005-016 PBX wake-up scheduling; US-005-022 Forex transaction + RBI report)
Built-Untested1 (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)
Partial4 (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)
Stub1 (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 Started2 (US-005-020 In-Room Dining Relay; US-005D-06 Lost & Found — actually owned by housekeeping-service, see D-005)
Drift findings11 (D-001..D-011; 4 P0 / 5 P1 / 2 P2)
[SME-PROPOSED] refinements18

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 /status
  • hmp-frontdesk-service/src/pbx/pbx.service.ts — wake-up scheduling + (mock) PBX CDR ingestion
  • hmp-frontdesk-service/src/pbx/pbx.module.ts
  • hmp-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] no pbx.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.ts or pbx.flow.spec.ts under hmp-infrastructure/testing/flow-tests/src/flows/)
  • Indirect: front-desk-checkin.flow.spec.ts covers /front-desk/* surface but not /pbx/wake-up

Real-backend smokes:

  • none

E2E (Playwright):

  • [CITED-BUT-NOT-FOUND] no dedicated e2e/tests/front-desk-phone.spec.ts — front-desk/phone/page.tsx exists 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. The attempts field on WakeUpCall suggests 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" lists PhoneCharge as a separate row (NOT a FolioTransaction), and there is no PbxFolioChargeConsumer analogous to PosFolioChargeConsumer. So the charge-to-folio loop is a manual step today (operator GETs /pbx/charges/:extension and would have to POST /folios/:id/charges by hand) — see D-002 P0.
  • WakeUpCall.roomNumber is a string field, not a foreign key to Room.id. Risk: a room renumber (e.g. floor renovation) silently breaks the cross-reference. Same risk applies to PhoneCharge.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 reconciliation to 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.captured event with {tenantId, propertyId, roomNumber, callDate, destination, duration, cost, taxAmount}. A new PbxFolioChargeConsumer (modeled on PosFolioChargeConsumer) SHALL resolve the in-house folio by Room.id from Room.roomNumber and post transactionCode='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} on FolioTransaction.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 WakeUpCall and PhoneCharge SHALL carry roomId String @db.Uuid (FK by convention to property-service Room.id) in addition to the operator-facing roomNumber denorm. Service-layer resolver maps roomNumber → roomId at row creation; renumbers leave roomId stable."

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 module owns 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] no guest-portal.service.spec.ts for 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.ts exists

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.ts and POST /guest-messages as the staff-side endpoint; neither exists in code. The only "message" surface is guest-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 from GuestPortalModule), POST /guest-messages { reservationId, body, deliveryChannels[], scheduledFor? }, JWT-gated (Roles: ADMIN/MANAGER/FRONT_DESK_AGENT), with DPDPA consent gate (refuse SMS/WhatsApp dispatch when Guest.communicationConsent.{sms|whatsapp} is not true).
  • [SME-PROPOSED] Add AC: "Scheduled messages SHALL be persisted in a GuestMessageSchedule table and dispatched by a BullMQ delayed-job worker. Missed scheduled dispatch (worker outage) is surfaced via the guest_message_schedule_breach Prometheus 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 exist
  • hmp-pos-service/src/orders/orders.controller.ts [CITED-BUT-NOT-FOUND] — POS ROOM_SERVICE order type may or may not exist; not verified for this RTM

Unit + integration tests:

  • [CITED-BUT-NOT-FOUND] no in-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; no in-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.inRoomDiningReceived consumed by pos-service KitchenGateway") is plausible but unimplemented. The pos-service KitchenGateway is documented in platform CLAUDE.md (port 3007, /kitchen namespace) 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 story US-005-NEW-IN-ROOM-IPTV covering 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 + report
  • hmp-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.ts
  • hmp-frontdesk-service/src/petty-cash/petty-cash.controller.ts:32-160 — funds CRUD, disbursements CRUD, approve/reject/pay, receipt upload, replenish, register, summary
  • hmp-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) + reconcile
  • hmp-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 tests
  • hmp-frontdesk-service/src/petty-cash/petty-cash.service.spec.ts — 15 tests
  • hmp-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.ts
  • hmp-web-dashboard/e2e/tests/cash-management-flow.spec.ts
  • hmp-web-dashboard/e2e/tests/cash-petty.spec.ts
  • hmp-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 expectedBalance variance path is covered (Gotcha #5 in CLAUDE.md), and PENDING_APPROVAL status 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 voucherNumber is documented as unique but the spec does not enumerate the format (e.g. PC-YYYY-NNNNN per 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/transactions with type='CASH_RECEIVED' SHALL refuse amount > 200000 (₹2L) with HTTP 422 + code='IT_269ST_CEILING_EXCEEDED'. Override requires Manager role + acknowledgement: 'IT_269ST_NON_INDIAN_GUEST' or acknowledgement: '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 distinct guestId × propertyId × auditDate aggregated 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.voucherNumber SHALL follow PC-{property.code}-{FY}-{seq6} (e.g. PC-VSH-2526-000123). Per-FY seq resets on Apr 1 IST. InvoiceSequence pattern (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-report
  • hmp-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 tests
  • forex.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 dedicated e2e/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-report endpoint 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/transaction SHALL refuse fromAmount equivalent ≥ USD 25k unless the request includes flm1Reference: 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 on ForexTransaction.encashmentCertificateUrl."
  • [SME-PROPOSED] Add AC: "GET /forex/rbi-report?type=FLM2&month=YYYY-MM SHALL 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/close
  • hmp-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] no service-recovery.flow.spec.ts exists

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' or FolioTransaction integration is visible from the spec list — the compensation loop is therefore manual today (D-010).
  • Story is missing from 01-epic-catalog.md entirely. The dashboard has no service-recovery page (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/resolve accepts an optional {compensation: {amount, transactionCode: 'SERVICE_RECOVERY_COMP', notes, approverUserId}}. When supplied, the service calls FoliosService.postAdjustment (negative amount = credit) inside the same $transaction as the status change. Approval is enforced server-side by validating approverUserId is a MANAGER+ role. Dedup key: service-recovery:{id}:comp on FolioTransaction.reference."
  • [SME-PROPOSED] Add AC: "A scheduled SLA-breach checker (@Cron every 5 min) SHALL find OPEN/IN_PROGRESS complaints whose severity SLA window has lapsed (CRITICAL=1h, HIGH=2h, MEDIUM=4h, LOW=24h) and emit SERVICE_RECOVERY_ESCALATED to 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 log
  • hmp-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] no concierge.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 platform EP-013 AI deliverable US-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-01 as 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 new frontdesk-service/src/concierge-log/ for the operations log per the EPIC §7 description with POST /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=true SHALL post transactionCode='CONCIERGE' to the folio with SAC 999719 (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-deposits
  • hmp-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 with POST /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 CRUD
  • hmp-housekeeping-service/src/lost-found/lost-found.service.ts + lost-found.service.spec.ts
  • hmp-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, not frontdesk-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, polling
  • hmp-payment-service/src/upi-qr/upi-qr.service.ts + upi-qr.service.spec.ts
  • hmp-frontdesk-service/src/folios/consumers/payment-captured.consumer.ts:181 (providerCode mapping) — razorpay-upi-qr → PaymentMode.UPI, dedup key folio:{folioId}:payment-captured:{paymentId} per Gotcha #27
  • hmp-frontdesk-service/src/payments/CLAUDE.md — documents the move (Workstream B: UpiQrModule removed from frontdesk-service)

Unit + integration tests:

  • hmp-payment-service/src/upi-qr/upi-qr.service.spec.ts
  • hmp-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-service per Workstream B (see src/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 as PosFolioChargeConsumer. 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:

  1. In-Room Dining Relay (US-005-020) — frontdesk-service/src/in-room/ is IPTV, not dining. The dining relay belongs in hmp-pos-service as a new RoomServiceOrderModule (consumer-side) + a thin frontdesk producer.
  2. 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 NEW frontdesk-service/src/safe-deposit/ module (orthogonal to property safe).
  3. 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/ to in-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​

  1. 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).
  2. D-002 P0 — PBX charges have no auto-post-to-folio loop; manual operator step today; Indian hotels lose PBX revenue.
  3. D-004 P0 — Staff-facing guest-messages controller does NOT exist; only guest-portal-token-gated guest-writes-to-staff path ships.
  4. D-006 P0 — Cash drawer CASH_RECEIVED does not enforce Income-Tax Act §269ST ₹2L ceiling; §271DA penalty exposure.
  5. D-008 P0 — Forex EPIC body §US-005D-03 claims "Not Started" but module is fully shipped + flow-tested.
  6. 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).