EP-006 — Housekeeping Management RTM
EPIC: EP-006 Housekeeping Management Reconciled: 2026-05-25 Wave 1 / 21 parallel agents — see
./PLAYBOOK.mdfor the pattern;./EP-002-rtm.mdis the template. Source of truth for "% passing":hmp-docs/docs/platform-registry/2026-05-16-13-flow-demo-readiness.md(flow-5 FD/HK Updates: 11/12 PASS; flow-9 Night Audit: 12/12 PASS). Code ground-truth:hmp-housekeeping-service/CLAUDE.md+ per-module CLAUDE.md (src/tasks/,src/rooms/,src/maintenance/,src/sync/).
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 (EPIC body) | 16 (US-006-001..US-006-016) |
| Catalog claims "Built" | 11 |
| Catalog claims "Partial" | 3 (US-006-006, -007, -011) |
| Catalog claims "Not Started" | 2 (US-006-013 auto-reorder, US-006-016 auto-task on checkout) |
| Verified-Built | 7 (US-006-001, -002, -003, -005, -008, -009, -010) |
| Built-Untested | 2 (US-006-012 lost & found, US-006-015 KPI dashboard) |
| Partial | 5 (US-006-004, -006, -007, -011, -014) |
| Stub | 0 |
| Not Started | 2 (US-006-013, US-006-016) |
| Drift findings | 12 (D-001..D-012; 5 P0 / 5 P1 / 2 P2) |
| [SME-PROPOSED] refinements | 24 |
[CITED-BUT-NOT-FOUND] markers | 4 |
Catalog vs EPIC-body title drift — the catalog row
US-006-005 — Linen Inventory & Movement Trackingcollapses two EPIC-body stories (US-006-009 Linen Inventory Management+US-006-010 Linen Movement Tracking); the catalog rowUS-006-006 — Minibar Par Managementmaps to EPICUS-006-011; catalogUS-006-007 — Maintenancemaps to EPICUS-006-008; catalogUS-006-008 — Supervisor Inspectionmaps to EPICUS-006-007; catalogUS-006-009 — Lost & Foundmaps to EPICUS-006-012; catalogUS-006-010 — KPI Dashboardmaps to EPICUS-006-015; catalogUS-006-011 — Discrepancy Reportmaps to EPICUS-006-014. Catalog also lists 3 stories with no EPIC-body counterpart (US-006-013 Turndown Scheduling,US-006-014 Room Cleaning Priority Queue,US-006-015 Deep Clean Schedulingin catalog ≠ EPIC). This RTM follows the EPIC body numbering (US-006-001..US-006-016) — every story-section ID below maps to an EPIC body story, with the catalog row called out in-line. Catalog-only stories are surfaced as D-001 below, modeled on EP-002 D-007.
US-006-001 — Real-Time Room Status Board
Catalog status: Built (P0, 5 points) Verified status: Built
Code locations:
hmp-housekeeping-service/src/rooms/rooms.controller.ts:18-44— GET/rooms/status(board), with explicit tenant guard viagetRequestContext()?.tenantIdhmp-housekeeping-service/src/rooms/rooms.controller.ts— PATCH/rooms/:id/status, POST/rooms/:id/inspection, POST/rooms/:id/request-inspection, GET/rooms/discrepancyhmp-housekeeping-service/src/rooms/rooms.service.ts—getRoomStatusBoard(),updateRoomStatus(),inspectRoom(),getDiscrepancies()hmp-housekeeping-service/src/gateway/housekeeping.gateway.ts— WebSocket/housekeepingnamespace;task:created/task:updated/task:assigned/task:inspectedserver-to-clienthmp-housekeeping-service/src/events/events.service.ts— publishesROOM_STATUS_CHANGED/ROOM_INSPECTEDKafka eventshmp-web-dashboard/src/components/modules/housekeeping/room-status-board.tsx[CITED-BUT-NOT-FOUND]— review file cites; not re-grep'd this passhmp-web-dashboard/src/app/(dashboard)/housekeeping/page.tsx— Housekeeping page hosting the board tab
Unit + integration tests:
hmp-housekeeping-service/src/rooms/rooms.service.spec.ts— 13 describe/it blocks (board grouping, status update + Kafka, inspect-success/fail, discrepancy classification)hmp-housekeeping-service/src/rooms/rooms.controller.spec.ts— 6 controller delegation tests including qa-PMS-7 gateway-path coveragehmp-housekeeping-service/src/rooms/rooms-enhanced.service.spec.ts— 4 supplemental testshmp-housekeeping-service/src/gateway/housekeeping.gateway.spec.ts— 24 WebSocket emission tests
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/flow-5-fd-hk-updates.spec.ts— 11/12 PASS (2026-05-16-13-flow-demo-readiness.md, 2026-05-16)hmp-infrastructure/testing/flow-tests/src/flows/housekeeping-lifecycle.flow.spec.ts— TBD (not enumerated in 13-flow doc)
Real-backend smokes:
hmp-web-dashboard/e2e/real-backend/smoke-housekeeping.real.spec.ts
E2E (Playwright):
hmp-web-dashboard/e2e/tests/housekeeping-board.spec.tshmp-web-dashboard/e2e/tests/housekeeping-room-status.spec.tshmp-web-dashboard/e2e/tests/04-housekeeping.spec.tshmp-web-dashboard/e2e/persona-housekeeping.spec.ts
Use cases referencing this story: none
SME notes:
- The status enum on the EPIC body lists
CLEAN, DIRTY, INSPECTED, OUT_OF_ORDER, OUT_OF_SERVICE, DO_NOT_DISTURB, PICKUP(7 values). The PrismaHousekeepingStatusenum implements only 4 (CLEAN, DIRTY, INSPECTED, PICKUP) per the US-006-001 review file Gap 4.IN_PROGRESSlives in the frontend display layer (derived from task state).OUT_OF_ORDER/OUT_OF_SERVICE/DO_NOT_DISTURBare on the parentRoomtable'sRoomStatus/frontOfficeStatuscolumn owned by property-service — a different namespace. - qa-PMS-7 gateway routing nuance is documented in the service-root CLAUDE.md:
PATCH /api/rooms/:id/statuslands on property-service (the canonical Room owner). To reach the HK-side state machine the dashboard must hitPATCH /api/housekeeping/rooms/:id/status. Easy to miss; covered byrooms.controller.spec.ts. - Real-time path is Kafka → analytics; the WebSocket
/housekeepingnamespace + Redis adapter is what powers the dashboard board's < 2-second AC. Review file Gap 1 (API gateway routing conflict) is resolved by the qa-PMS-7 fix.
Drift findings:
D-002 — [P1] [Status-enum-drift] EPIC body lists 7 statuses on the room board but the Prisma HousekeepingStatus enum has 4. OUT_OF_ORDER/OUT_OF_SERVICE/DO_NOT_DISTURB live on the property-service Room table — a separate namespace. The board UI conflates the two enums and shows all 7 by joining the cross-service read model.
- [SME-PROPOSED] Add AC: "The room status board SHALL surface BOTH the HK-side
housekeepingStatus(CLEAN/DIRTY/INSPECTED/PICKUP) AND the property-sideRoomStatus/frontOfficeStatus(OUT_OF_ORDER/OUT_OF_SERVICE/OCCUPIED/VACANT) and the per-roomdndflag (today onReservationnotRoom). The two enums SHALL remain in their owning service — no enum widening on either side. The dashboard's 7-color legend SHALL document which colour comes from which enum."
US-006-002 — Create Housekeeping Task
Catalog status: Built (P0, 5 points) — catalog title "Task CRUD (6 Types, 4 Priorities)" Verified status: Built
Code locations:
hmp-housekeeping-service/src/tasks/tasks.controller.ts:41-50— POST/tasks(role-gated SUPER_ADMIN/ADMIN/MANAGER/HOUSEKEEPING,@Audit)hmp-housekeeping-service/src/tasks/tasks.controller.ts:62-71— POST/tasks/internal(service-account, role-bypass, called by frontdesk on move-bed/checkout)hmp-housekeeping-service/src/tasks/tasks.controller.ts:79-91— POST/tasks/internal/cancel-by-bed(service-account)hmp-housekeeping-service/src/tasks/tasks.service.ts—create()(sets room DIRTY, publishesHOUSEKEEPING_TASK_CREATED, WebSockettask:created)hmp-housekeeping-service/src/tasks/dto/task.dto.ts—CreateTaskDto(taskType: 6-enum, priority: 4-enum, notes, scheduledFor optional)
Unit + integration tests:
hmp-housekeeping-service/src/tasks/tasks.service.spec.ts— 15 specshmp-housekeeping-service/src/tasks/tasks.controller.spec.ts— 17 specshmp-housekeeping-service/src/tasks/__tests__/task-cross-tenant-denial.spec.ts— 12 specs (canonical Pick D Phase-3 port; ORPHANED-TEST until cross-tenant validation increate()is shipped — see D-009)hmp-housekeeping-service/src/events/events.service.spec.ts— 26 specs (coversRESERVATION_CHECKED_OUT→ auto-create CHECKOUT_CLEANING, with W1.B.9 dedup; AP-T05-001)
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/flow-5-fd-hk-updates.spec.ts:: HK task lifecycle — 11/12 PASShmp-infrastructure/testing/flow-tests/src/flows/housekeeping-lifecycle.flow.spec.ts— TBD
Real-backend smokes: smoke-housekeeping.real.spec.ts
E2E (Playwright):
hmp-web-dashboard/e2e/tests/17-housekeeping-tasks.spec.ts
Use cases referencing this story: none
SME notes:
- The taskType enum in
tasks.service.ts/Prisma usesDAILY_CLEANING, CHECKOUT_CLEANING, DEEP_CLEANING, TURNDOWN, INSPECTION, SPECIAL_REQUESTpersrc/tasks/CLAUDE.md. The EPIC body story-text saysCLEANING, DEEP_CLEAN, TURNDOWN, INSPECTION, MAINTENANCE, CUSTOM. Two of six values disagree (CLEANINGvsDAILY_CLEANING+CHECKOUT_CLEANING;MAINTENANCEvsSPECIAL_REQUEST;CUSTOMnot present). The dashboard task-list filter has its own third copy. See D-003. - AP-T05-001 dedup:
EventsServiceskips creating a second CHECKOUT_CLEANING for the samereservationId+roomIdwithin the day. 3 tests inevents.service.spec.ts. - Cross-tenant denial spec is ORPHANED: 12 tests in
task-cross-tenant-denial.spec.tsexist but their #3 (create-time tenant validation) is documented in the spec header as a[Deviation from canonical Pick D — documented]. The service'screate()does NOT callprisma.room.findUniqueto verify the room belongs to the requesting tenant. See D-006 below — the same gap the Task #11 security review flagged.
Drift findings:
D-003 — [P0] [Story-vs-code-enum-drift] Three copies of the task-type enum disagree: EPIC body (CLEANING/DEEP_CLEAN/TURNDOWN/INSPECTION/MAINTENANCE/CUSTOM), Prisma+service (DAILY_CLEANING/CHECKOUT_CLEANING/DEEP_CLEANING/TURNDOWN/INSPECTION/SPECIAL_REQUEST), dashboard filter list. A frontend sending the EPIC value MAINTENANCE would Prisma-fail at runtime; sending CLEANING (no underscore) would 422.
- [SME-PROPOSED] Adopt the code enum as canonical (already shipped, has 6 production values). Rewrite EPIC-body story to use the same six. Add an AC: "
POST /tasksSHALL accepttaskTypefrom the canonical 6-enum and 400 withcode=TASK_TYPE_INVALIDfor any other value, including the deprecatedCLEANING/MAINTENANCE/CUSTOMvalues." - [SME-PROPOSED] Add AC: "MAINTENANCE intent SHALL be expressed via
POST /maintenance(separate domain — see US-006-008) and explicittaskType=INSPECTIONfor post-fix verification; do not overloadHousekeepingTaskfor maintenance."
D-004 — [P1] [Story-incomplete] EPIC body story mentions scheduledFor for shift-planning but does not document India shift-handover semantics. Typical Indian SME hotel runs 3 shifts (morning 6 AM–2 PM / evening 2–10 PM / night 10 PM–6 AM) and the supervisor needs to see "tasks left over from previous shift" + "tasks I created for next shift". Today scheduledFor is a free DateTime with no shift discriminator.
- [SME-PROPOSED] [Indian-hospitality SME] Add AC: "
HousekeepingTaskSHALL carry an optionalshiftenum (MORNING/EVENING/NIGHT) populated either explicitly or auto-computed fromscheduledForagainst the property's shift-window config. The mobile task queue MAY default to filtering by the active shift. The shift-handover report (new in US-006-NEW-SHIFT-HANDOVER, see D-001) SHALL show overdue + handed-over counts per shift."
US-006-003 — Assign Task to Staff
Catalog status: Built (P0, 3 points) Verified status: Built
Code locations:
hmp-housekeeping-service/src/tasks/tasks.controller.ts:144-158— PATCH/tasks/:id/assign(role-gated,@Audit,x-property-idtenant header)hmp-housekeeping-service/src/tasks/tasks.service.ts—assign()(auto-transitions PENDING → IN_PROGRESS, publishesHOUSEKEEPING_TASK_ASSIGNED, WebSockettask:assigned)- Workload safeguard —
SafeguardService.HK_STAFF_OVERLOADEDenforced inassign()persrc/tasks/CLAUDE.md. Blocks if staff already has>= maxTasksPerStaffactive tasks. hmp-housekeeping-service/src/staff/staff.service.ts— staff lookups for assignee validation- No auto-assign endpoint shipped —
POST /tasks/auto-assigndoes NOT exist (review file Gap 1 still open) hmp-web-dashboard/e2e/tests/housekeeping-assign.spec.ts— assignment Playwright
Unit + integration tests:
hmp-housekeeping-service/src/tasks/tasks.service.spec.ts— assignment cases inside the 15-spec suitehmp-housekeeping-service/src/staff/staff.service.spec.ts— 10 specs (staff lookup)
Flow tests: covered indirectly by flow-5 (11/12 PASS)
E2E: housekeeping-assign.spec.ts
Use cases referencing this story: none
SME notes:
- HK_STAFF_OVERLOADED safeguard (per CLAUDE.md gotcha) is the workload guard the US-006-003 review file Gap 6 said was missing. It IS shipped — the review file is stale.
- Auto-assign (review Gap 1, EPIC body AC-2 "Auto-assign tasks at shift start") is still not shipped. Supervisor must manually assign each task or assign-by-bulk via parallel POSTs.
- Push notification on assign (review Gap 4):
HOUSEKEEPING_TASK_ASSIGNEDevent is published per CLAUDE.md "On Assign" side effects — notification-service consumes via the standard topic-routing. The WebSockettask:assignedis the in-app notification surface; FCM/push is delivered out-of-band by notification-service when it sees the Kafka event. Review file is stale. - The staff-on-shift / clocked-in concept does not exist as a backend model — there is no
StaffShiftorClockEventtable. Auto-assignment AC-2 in EPIC body assumes shift attendance is tracked; it isn't.
Drift findings:
D-005 — [P1] [Story-incomplete + integration-gap] Auto-assign (POST /tasks/auto-assign) is documented in catalog/EPIC body and in the US-006-003 review file as a key AC but is not implemented. Beyond the missing endpoint, the prerequisite StaffShift/clock-in model is also not in the schema — auto-assign can't distribute by "currently on shift" without it.
- [SME-PROPOSED] [Indian-hospitality SME] Add AC: "Hotels SHALL be able to mark staff as clocked-in via
POST /staff/:id/clock-in(writes aStaffShiftrow withshift,clockInAt,clockOutAt?,floor[]).POST /tasks/auto-assignSHALL distribute pending tasks to staff whoseStaffShift.clockOutAt IS NULLANDshiftmatches the task'sscheduledForwindow AND whosefloor[]intersects the task's room floor, balancing by current active-task count (target ±2 across the pool)."
US-006-004 — Mobile Task Queue
Catalog status: Built (P0, 5 points) Verified status: Built
Code locations:
hmp-mobile-apps/apps/housekeeping/app/(tabs)/my-tasks.tsx— task queue screenhmp-mobile-apps/apps/housekeeping/app/(tabs)/tasks.tsx— full task listhmp-mobile-apps/apps/housekeeping/app/(tabs)/rooms.tsx— room viewhmp-mobile-apps/apps/housekeeping/app/(tabs)/maintenance.tsx— maintenance entryhmp-mobile-apps/apps/housekeeping/app/room/[id].tsx— per-room detailhmp-mobile-apps/packages/shared-realtime/src/notifications-store.ts+useNotifications.ts— push/FCM ingest + WS reconnect with auth.token from storehmp-mobile-apps/packages/shared-realtime/src/fcm-foreground-suppressor.ts— foreground FCM suppressionhmp-housekeeping-service/src/sync/sync.controller.ts+sync.service.ts— offline batch endpointPOST /api/sync(last-write-wins, idempotency viamutationId)hmp-housekeeping-service/src/gateway/housekeeping.gateway.ts— WebSocket source-of-truth real-time stream
Unit + integration tests:
hmp-housekeeping-service/src/sync/sync.service.spec.ts— 10 specs (batch sync + idempotency)hmp-mobile-apps/packages/shared-realtime/src/notifications-store.test.ts— storehmp-mobile-apps/packages/shared-realtime/src/useNotifications.test.ts— hookhmp-mobile-apps/packages/shared-realtime/src/deep-link-handler.test.ts— deep link routing
Flow tests: none specific to mobile
E2E (Playwright): dashboard E2Es (no Detox/Maestro mobile E2E found)
Use cases referencing this story: none
SME notes:
- Offline sync is shipped (CLAUDE.md gotcha 4: last-write-wins +
mutationIdidempotency, batched mutations). - No mobile E2E — no Detox / Maestro / Playwright-mobile test exercises the swipe-to-start/skip flow. Unit tests cover the realtime store but not the queue UI itself.
- Mobile uses the same JWT as dashboard (HS256 in dev, RS256 in prod per AP-CC-3 gotcha 7); the realtime store handles reconnect tokens.
Drift findings: (none net new — feeds D-010 photo-evidence ask below)
- [SME-PROPOSED] Add AC: "Mobile task queue SHALL be exercised by at least one Detox or Maestro E2E covering: load queue → swipe-start → swipe-skip → mark complete with photo evidence → offline batching of 3 mutations → reconnect → server-side merge. Today no mobile E2E exists for the queue."
US-006-005 — Update Task Status (In-Progress / Complete)
Catalog status: Built (P0, 3 points) Verified status: Built
Code locations:
hmp-housekeeping-service/src/tasks/tasks.controller.ts:160-174— PATCH/tasks/:id/status(status validated via@IsInonUpdateTaskStatusDto)hmp-housekeeping-service/src/tasks/tasks.service.ts:39-45—TASK_STATUS_TRANSITIONSexported state-machine (PENDING→IN_PROGRESS/CANCELLED; IN_PROGRESS→COMPLETED/CANCELLED; COMPLETED→INSPECTED; INSPECTED/CANCELLED terminal)hmp-housekeeping-service/src/tasks/tasks.service.ts—updateStatus(); on COMPLETED publishesHOUSEKEEPING_TASK_COMPLETEDcarryingassignedTo+cleanTimeMinutes+completedAt+targetHousekeepingStatus(FU-55, see gotcha 5)- HK_INSPECTION_PHOTO_REQUIRED safeguard (per
src/tasks/CLAUDE.md) — ifrequireInspectionPhotois enabled and no photos attached, COMPLETED is blocked
Unit + integration tests:
hmp-housekeeping-service/src/tasks/tasks.service.spec.ts— 15 specs incl. FU-55 producer-contract regression test "publishes assignedTo + cleanTimeMinutes + completedAt"hmp-housekeeping-service/src/tasks/inspection.service.spec.ts— 6 specs
Flow tests:
flow-5-fd-hk-updates.spec.ts— 11/12 PASS (covers complete + room status cascade)
Real-backend smokes: smoke-housekeeping.real.spec.ts
Use cases referencing this story: none
SME notes:
- Declarative state-machine is shipped (exported
TASK_STATUS_TRANSITIONSattasks.service.ts:39). The US-006-001 D-006 finding in EP-002 (no central state-machine) is NOT applicable to HK tasks — the HK task state-machine is explicit. Room status across services (the gap EP-002 D-006 calls out) IS still last-writer-wins, see D-007 below. - FU-55 (2026-05-23) — analytics MV
mv_hk_staff_productivitydepends on the COMPLETED event carryingassignedTo. Regression test locked attasks.service.spec.ts.
Drift findings: (none net new for this story)
US-006-006 — Photo Upload for Task Completion
Catalog status: Partial (P1, 5 points)
Verified status: Partial — PhotosService + presigned URL endpoint shipped; sharp compression / thumbnail / size-cap still missing per review file
Code locations:
hmp-housekeeping-service/src/photos/photos.controller.ts— POST/housekeeping/photos/presigned-url, GET/housekeeping/photos/:referenceIdhmp-housekeeping-service/src/photos/photos.service.ts— presigned URL generation + photo listinghmp-housekeeping-service/src/photos/photos.module.ts— wires StorageService- HK_INSPECTION_PHOTO_REQUIRED safeguard — on complete, blocks if no photos
Unit + integration tests:
hmp-housekeeping-service/src/photos/photos.service.spec.ts— 9 specs
Flow tests: none specific
Use cases referencing this story: none
SME notes:
- Review file (
US-006-004-review.mdper EPIC numbering) verdict NOT_IMPL is stale —PhotosServiceis now shipped with presigned-URL workflow. The review file pre-dates the photo module. - Gaps remaining vs EPIC body AC-1/-2/-3 per CLAUDE.md photos module: no Sharp resize/compress pipeline, no thumbnail generation step, no
photoType(BEFORE/AFTER) discriminator onHousekeepingPhoto, nofileSizeBytesenforcement at S3 presigned-URLcontent-length-rangetime. - Base64-in-DB anti-pattern from the original review IS resolved —
HousekeepingPhoto.photoUrlis an S3 URL.
Drift findings:
D-010 — [P1] [Story-incomplete + missing-AC] EPIC body AC says "before/after comparison view" but HousekeepingPhoto has no photoType column to distinguish them. Today supervisors see an unordered photo array. Without photoType the comparison UI can't reliably split BEFORE vs AFTER.
- [SME-PROPOSED] Add AC: "
HousekeepingPhotoSHALL carry aphotoType: BEFORE | AFTER | INSPECTION_PASS | INSPECTION_FAILenum. POST presigned-URL request SHALL acceptphotoTypeand embed it as an S3 object tag for storage tier rules." - [SME-PROPOSED] Add AC: "Presigned-URL response SHALL enforce
content-length-range≤ 1 MB andcontent-type IN ('image/jpeg','image/png','image/webp'). The mobile client SHALL pre-compress with Sharp/expo-image-manipulator to 1080px longest edge + 70% JPEG quality before upload." - [SME-PROPOSED] [Indian-hospitality SME] Add AC: "On 2G/3G networks (still common in Tier-2/3 city basements) the upload SHALL fall back to background-queue with WiFi-only resume — task completion MUST not be blocked on photo upload (status flips to COMPLETED immediately; photo upload status tracked separately on
HousekeepingPhoto.uploadStatus)."
US-006-007 — Supervisor Inspection with Checklist Scoring
Catalog status: Partial (P1, 8 points)
Verified status: Partial — INSPECTED status + inspectRoom shipped; no structured checklist / per-item scoring
Code locations:
hmp-housekeeping-service/src/tasks/inspection.service.spec.ts— 6 specs (inspection flow)hmp-housekeeping-service/src/rooms/rooms.controller.ts— POST/rooms/:id/inspection(binary pass)hmp-housekeeping-service/src/rooms/rooms.controller.ts— POST/rooms/:id/request-inspection(queues INSPECTION task)hmp-housekeeping-service/src/rooms/rooms.service.ts—inspectRoom()(finds latest COMPLETED task, marksinspectedBy/inspectedAt, publishesROOM_INSPECTED)- No
InspectionTemplate/InspectionChecklistPrisma model — per US-006-001 review file analysis, inspection is binary hmp-housekeeping-service/src/tasks/inspection.service.ts[CITED-BUT-NOT-FOUND]— spec exists, source file referenced from CLAUDE.md
Unit + integration tests:
inspection.service.spec.ts— 6 specs
Flow tests: flow-5 covers inspection happy path
Use cases referencing this story: none
SME notes:
- Gap matches EPIC body and review — only binary pass/fail. No
bathroom: 8 items/bedroom: 8 itemschecklist, no weighted score, no template-per-room-type. - Failed-inspection auto-creates a re-clean task IS partially shipped via
request-inspection(queues INSPECTION task) but the re-clean (CLEANING task on fail) is not auto-created today.
Drift findings:
D-011 — [P1] [Story-incomplete + India-hospitality-critical] Inspection is binary. EPIC body AC-2 ("Failed inspection triggers re-clean task with specific failed items") is not implemented. EPIC AC-1 ("Deluxe Inspection Checklist with 5 sections / 26 items / weighted score") has zero schema/code backing.
- [SME-PROPOSED] [Indian-hospitality SME] Add AC: "New
InspectionTemplatePrisma model linked toRoomType(or property-default) withsections[] (Section { name, weight, items[] })andItem { code, label, weight, isRequired }. Default templates SHALL ship for: Standard Room (4 sections / 18 items), Deluxe (5/26), Suite (6/34) — common Indian SME hotel inventory." - [SME-PROPOSED] Add AC: "Failed inspection (
score < passingThresholddefault 85%) SHALL atomically (a) mark current task INSPECTED-FAIL, (b) create a new CLEANING task withpriority=HIGHand notes auto-populated with failed item labels, (c) reverthousekeepingStatusto DIRTY, (d) publishHOUSEKEEPING_INSPECTION_FAILEDcarrying the original housekeeper UUID for analytics."
US-006-008 — Maintenance Request Creation
Catalog status: Built (P0, 5 points) — catalog calls this US-006-007 Verified status: Built
Code locations:
hmp-housekeeping-service/src/maintenance/maintenance.controller.ts— POST/GET/PATCH/maintenance, GET/maintenance/:idhmp-housekeeping-service/src/maintenance/maintenance.service.ts— full lifecycle (status: OPEN/IN_PROGRESS/WAITING_PARTS/COMPLETED/CANCELLED) + Kafka eventshmp-housekeeping-service/src/tasks/escalation.service.ts— POST/tasks/:id/escalatecreates engineering Work Order via asset-service, setsHousekeepingTask.workOrderId(idempotent: 400 if already linked)hmp-housekeeping-service/src/maintenance/CLAUDE.md— module-level documentationhmp-web-dashboard/src/app/(dashboard)/housekeeping/maintenance/page.tsx— maintenance dashboard
Unit + integration tests:
hmp-housekeeping-service/src/maintenance/maintenance.service.spec.ts— 6 specshmp-housekeeping-service/src/tasks/escalation.service.spec.ts— 6 specs (double-escalation safety, atomic WO write-back)hmp-web-dashboard/src/app/(dashboard)/housekeeping/maintenance/__tests__/maintenance-page.test.tsx
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/maintenance-requests.flow.spec.ts— TBD- flow-5 covers maintenance happy-path inside the 11/12 PASS suite
E2E (Playwright): hmp-web-dashboard/e2e/tests/housekeeping-maintenance.spec.ts
Use cases referencing this story: none
SME notes:
- Two paths to engineering: (a) free-standing maintenance request via
POST /maintenance, (b) escalate a cleaning task to a Work Order viaPOST /tasks/:id/escalate. The escalation path is owned byhmp-asset-service(port 3016) per platform CLAUDE.md — HK records theworkOrderIdbut Engineering owns the WO. Double-escalation guarded (BadRequestException ifworkOrderIdalready set). Race condition (two near-simultaneous clicks) acknowledged as v1 limitation, slated for Phase 5 unique-constraint hardening on asset-service. - Out-of-order cascade — review file AC-2 expects HK to mark room OUT_OF_ORDER for ELECTRICAL/URGENT. Today this requires a separate
PATCH /rooms/:id/statusto property-service. Not auto-cascaded from maintenance creation.
Drift findings:
D-008 — [P1] [Cross-service-handoff] EPIC body AC-2 expects critical maintenance (ELECTRICAL+URGENT) to auto-set room status to OUT_OF_ORDER and notify reservation-service. Today the dashboard must do two separate writes (POST /maintenance + PATCH /api/rooms/:id/status) and reservation-service consumes nothing. If the dashboard step 2 fails or the supervisor forgets, the room can be sold while it has live exposed wiring.
- [SME-PROPOSED] Add AC: "
MaintenanceService.create()SHALL, whencategory=ELECTRICAL|GAS|PLUMBINGANDpriority=URGENT, atomically (transaction) callPropertyServiceClient.updateRoomStatus(roomId, 'OUT_OF_ORDER', { reason: 'auto-OOO from URGENT maintenance ' + requestId })and publishMAINTENANCE_AUTO_OOOfor reservation-service consumption (which SHALL flag any future-arrival reservation on that room for room-reassign)."
US-006-009 — Linen Inventory Management
Catalog status: Built (P0, 3 points) — catalog collapses US-006-009 + US-006-010 into one row "US-006-005 Linen Inventory & Movement Tracking" Verified status: Built
Code locations:
hmp-housekeeping-service/src/inventory/inventory.controller.ts— GET/inventory/linen, POST/inventory/linen/movementhmp-housekeeping-service/src/inventory/inventory.service.ts—getLinenInventory(),createLinenMovement()hmp-housekeeping-service/src/inventory/dto/inventory.dto.ts—CreateLinenMovementDto- Atomic bucket update — verified in CLAUDE.md key Prisma models:
LinenInventorycarriestotalQuantity, inRooms, inLaundry, inStock, damaged, reorderLevel. The US-006-005 (catalog numbering) review file flagged thatcreateLinenMovement()did NOT update buckets — needs re-verification (see D-012) hmp-web-dashboard/src/app/(dashboard)/housekeeping/inventory/page.tsx— inventory dashboardhmp-web-dashboard/src/app/(dashboard)/housekeeping/inventory/reorder/page.tsx— reorder page
Unit + integration tests:
hmp-housekeeping-service/src/inventory/inventory.service.spec.ts— 7 specs (the review noted the "should create linen movement and update inventory" test title was misleading — needs Task #6 verification)hmp-web-dashboard/src/app/(dashboard)/housekeeping/inventory/__tests__/inventory-page.test.tsx
Flow tests: none specific
Use cases referencing this story: none
SME notes:
- Review file
US-006-005-review.md(catalog numbering) is 2.5 months stale; per the live CLAUDE.md the schema has the 6 buckets. The bucket-update-on-movement gap may still be open — see D-012. - EPIC body lists 8 linen categories (BED_SHEET, PILLOW_COVER, DUVET_COVER, BATH_TOWEL, HAND_TOWEL, FACE_TOWEL, BATH_MAT, BATHROBE). Code uses a free-text
itemTypestring. No enum constraint.
Drift findings:
D-012 — [P0] [Code-vs-review-vs-AC] Review (2026-03-07) said createLinenMovement() did not update bucket counts; CLAUDE.md does not call this out as a fixed gotcha. If still broken in 2026-05, every movement is a no-op on the inventory display — the dashboard shows perpetually-stale counts.
- [SME-PROPOSED] Verify in code (Task 6 in PLAYBOOK terms): grep
inventory.service.tsforprisma.$transactionaround the movement create + bucket update. If absent, ship the atomic transaction now. Add 6 unit tests, one perLinenMovementType, asserting BOTH the movement row IS written AND the correctLinenInventorybucket pair IS adjusted. - [SME-PROPOSED] [Indian-hospitality SME] Add AC: "
linen-categoriesSHALL be a typed enum (BED_SHEET, PILLOW_COVER, DUVET_COVER, BATH_TOWEL, HAND_TOWEL, FACE_TOWEL, BATH_MAT, BATHROBE, NAPKIN, TABLE_CLOTH) with per-category default par computed asrooms × multiplier(towels 3×, sheets 2× to account forinLaundryover 24-hour dhobi turnaround standard in India). Tenants MAY override per-property."
US-006-010 — Linen Movement Tracking
Catalog status: Built (P0, 3 points) — collapsed into catalog US-006-005 with US-006-009 Verified status: Built
Code locations:
hmp-housekeeping-service/src/inventory/inventory.controller.ts— POST/inventory/linen/movementhmp-housekeeping-service/src/inventory/inventory.service.ts—createLinenMovement()- Movement types per CLAUDE.md:
ISSUE_TO_ROOM, RETURN_FROM_ROOM, SEND_TO_LAUNDRY, RECEIVE_FROM_LAUNDRY (named WASHED in EPIC), WRITE_OFF (named DAMAGED+DISCARDED in EPIC), PURCHASE - Auto-reorder integration:
src/inventory/auto-reorder.service.ts+auto-reorder.controller.ts(18 specs) covers US-006-013
Unit + integration tests:
inventory.service.spec.ts— 7 specs
Flow tests: none specific
Use cases referencing this story: none
SME notes:
- Movement-type enum drift — code has 6 types, EPIC body lists 6 with different names (ISSUED/RETURNED/DAMAGED/DISCARDED/WASHED/IN_LAUNDRY vs code's ISSUE_TO_ROOM/RETURN_FROM_ROOM/WRITE_OFF/PURCHASE/SEND_TO_LAUNDRY/RECEIVE_FROM_LAUNDRY). DAMAGED+DISCARDED in EPIC roll up into WRITE_OFF in code — losing the "still recoverable, just stained" vs "send-to-trash" distinction.
- WRITE_OFF reason field is optional per the review file — D-012 covers.
Drift findings: (feeds D-012 above)
- [SME-PROPOSED] Add AC: "Split
WRITE_OFFintoDAMAGED(stained/torn — still pending decision) andDISCARDED(final write-off). AddingDAMAGEDgives accounts team a 30-day window to recover or discard. Monthly loss-rate calculation SHALL excludeDAMAGEDrows still within recovery window."
US-006-011 — Minibar Par Level Management
Catalog status: Built (P1, 3 points) — catalog calls this US-006-006 Verified status: Built
Code locations:
hmp-housekeeping-service/src/minibar/minibar.controller.ts— GET/minibar/:roomId, POST/minibar/:roomId/consume, POST/minibar/:roomId/replenish, POST/minibar/:roomId/chargehmp-housekeeping-service/src/minibar/minibar.service.ts—getByRoom,updateConsumption,replenishAll,chargeToFoliohmp-housekeeping-service/src/minibar/dto/minibar.dto.ts—ConsumeItemDto,ChargeToFolioDto- chargeToFolio: posts consumption charges to folio service per CLAUDE.md "Dependencies on Other Services"
Unit + integration tests:
hmp-housekeeping-service/src/minibar/minibar.service.spec.ts— 19 specs
Flow tests: none specific
Use cases referencing this story: none
SME notes:
- GST slabs on minibar are India-specific and the EPIC body explicitly calls them out (5%/12%/18%/28% with examples). The minibar DTOs don't enforce a slab; the GST math happens at folio-post time in
hmp-frontdesk-service(and downstream@hmp/common/printing/receipt-templatesfor the receipt). The minibarsellingPriceis GST-inclusive per current convention but no AC documents this. - "Restock list for floor" (EPIC AC-3) requires aggregation across rooms — not currently a single endpoint, supervisor must call
GET /minibar/:roomIdper room.
Drift findings: (none net new beyond cross-cutting D-012 enum concerns)
- [SME-PROPOSED] [Indian-hospitality SME] Add AC: "
MinibarItemSHALL carrygstSlab: '5'|'12'|'18'|'28'andpricingMode: 'GST_INCLUSIVE'|'GST_EXCLUSIVE'(defaultGST_INCLUSIVE). When consumption is posted to folio, the line item SHALL be split into base + GST per the slab so the e-invoice IRN payload carries correct HSN+rate (cross-references EP-002 D-008 on rate-plan pricing mode)." - [SME-PROPOSED] Add new endpoint
GET /minibar/floor/:floor/restock-list?propertyId=Xreturning an aggregated pick-list grouped by item, sorted by quantity-needed descending. Standard turndown workflow in Indian hotels.
US-006-012 — Lost & Found Register
Catalog status: Built (P1, 3 points) — catalog calls this US-006-009 Verified status: Built-Untested — 16 service specs but no flow / smoke / E2E
Code locations:
hmp-housekeeping-service/src/lost-found/lost-found.controller.ts— POST/GET/GET-by-id, PATCH/:id/claim, PATCH/:id/disposehmp-housekeeping-service/src/lost-found/lost-found.service.ts— full CRUD with filter (propertyId/status/category/fromDate/toDate)hmp-housekeeping-service/src/lost-found/dto/create-lost-found.dto.ts+claim-lost-found.dto.ts- Reporting-feeds: GET
/reporting-feeds/housekeeping/lost-and-found(per CLAUDE.md) — analytics-service consumes - Note: also a legacy
InventoryControllerat/api/lost-foundper CLAUDE.md gotcha 1
Unit + integration tests:
lost-found.service.spec.ts— 16 specsreporting-feeds-hk.controller.spec.ts— 16 specs (covers lost-and-found feed)
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/lost-and-found.flow.spec.ts— TBD (file exists)
Use cases referencing this story: none
SME notes:
- Two controllers (CLAUDE.md gotcha 1) —
InventoryController:/api/lost-found(legacy) ANDLostFoundController:/api/housekeeping/lost-found(newer). Newer one is more complete. Document deprecation timeline. - 90-day disposal in EPIC AC-3 is documented but the auto-flagging cron is not in the code (no scheduler runs
findManyNotifiedOlderThan90). Manual review only. - Auto-link to last guest (EPIC AC-1) requires guest-service cross-service read. Not verified in the source — see D-009.
- High-value approval (JEWELRY/ELECTRONICS Manager approval) is not implemented — no role-gated branch.
Drift findings:
D-009 — [P0] [Security + missing-validation, Task #11] Per the security gap surfaced in Task #11 (referenced in the agent brief), roomId is NOT validated for tenant on task create AND the same hole applies symmetrically to lost-found.create() — the service writes whatever roomId is on the DTO without verifying it belongs to the requesting tenant's property. Combined with the cross-tenant denial spec being an ORPHANED test for the create-path (see D-006 below), a forged roomId in a POST /housekeeping/lost-found body would auto-link the item to "last guest who checked out of that room" — leaking tenant-B's guest into a tenant-A lost-found list.
- [SME-PROPOSED] Add AC: "
HousekeepingTask.create,LostFound.create,Maintenance.create, andMinibar.consumeSHALL callPropertyServiceClient.assertRoomOwnership(roomId, tenantId)(or use local Room read-model lookup) BEFORE writing the row. Reject forgedroomIdwithForbiddenException('ROOM_NOT_OWNED_BY_TENANT'). Add 4 cross-tenant.spec.tscases covering each create path."
US-006-013 — Auto-Reorder Alerts (Linen / Amenities)
Catalog status: Not Started (P2, 5 points) — catalog calls this US-006-012 Verified status: Built (catalog stale)
Code locations:
hmp-housekeeping-service/src/inventory/auto-reorder.controller.ts— GET/POST/DELETE reorder rules; POST manual reorder check; GET/PATCH PO approve/receivehmp-housekeeping-service/src/inventory/auto-reorder.service.ts— auto-reorder evaluation + PO draftinghmp-housekeeping-service/src/inventory/dto/reorder-rule.dto.ts—ReorderRuleDTO- Prisma models (per CLAUDE.md):
ReorderRule(P3-1: per-property thresholds, unique on(propertyId, itemName)),PurchaseOrder(DRAFT → APPROVED → RECEIVED)
Unit + integration tests:
auto-reorder.controller.spec.ts— 18 specs
Flow tests: none specific
Use cases referencing this story: none
SME notes:
- Catalog says "Not Started" — code IS shipped (P3-1 commit per CLAUDE.md). The 18-spec suite and the PurchaseOrder model are evidence. This is the inverse of EP-002 D-011 (review stale) — here the catalog is stale.
- Notification-service integration: the EPIC body AC-1 asks for push + email when stock drops below reorder point. The auto-reorder service publishes the
purchase-order.draftedevent; notification-service consumption needs verification (out-of-scope here). - Duplicate-suppression (AC-2) and resolution clear (AC-3) are not surfaced in the public API — needs verification.
Drift findings:
D-006 — [P0] [Catalog-vs-code, mirrors EP-002 D-011] Catalog row "US-006-012 (catalog) / US-006-013 (EPIC) Auto-Reorder Alerts" claims Not Started but the code, schema (ReorderRule + PurchaseOrder), and 18-spec suite are shipped. A new reviewer reading the catalog alone would reopen closed P3-1 work.
- [SME-PROPOSED] Promote the catalog row to
Built(orPartialuntil notification-service Kafka consumption is verified). Add a regression test assertingauto-reorder.service.tspublisheshousekeeping.inventory.below_reorderon bucket drop belowreorderPoint.
US-006-014 — Discrepancy Report (FD vs HK)
Catalog status: Partial (P1, 5 points) — catalog calls this US-006-011 Verified status: Partial
Code locations:
hmp-housekeeping-service/src/rooms/rooms.controller.ts— GET/rooms/discrepancyhmp-housekeeping-service/src/rooms/rooms.service.ts—getDiscrepancies()(queriesOR: [{ frontOfficeStatus: OCCUPIED, housekeepingStatus: CLEAN }, { frontOfficeStatus: VACANT, housekeepingStatus: DIRTY }], classifies asPOSSIBLE_SKIP/NEEDS_ATTENTION)hmp-housekeeping-service/src/reporting-feeds/reporting-feeds-hk.controller.ts— GET/reporting-feeds/housekeeping/discrepancies(analytics live-proxy)
Unit + integration tests:
rooms.service.spec.ts— discrepancy classification within the 13-spec suitereporting-feeds-hk.controller.spec.ts— 16 specs
Use cases referencing this story: none
SME notes:
- Today's report: list with classification. Per EPIC body AC, the report should also show duration, suggested action, resolve action with logging, and auto-refresh every 60s + a daily summary email. None of those are shipped.
- Cross-service — the
discrepancyread joins FO and HK status, both on the localRoomread-model populated by property-service sync. The freshness of the local Room mirror is bounded by the sync cadence.
Drift findings:
D-007 — [P0] [Story-incomplete + cross-service-handoff] Discrepancy detection is read-only — no resolution endpoint, no logging of resolver action, no duration tracking, no email. EPIC body AC-2 ("Click Resolve on Room 412 → updates FD status to Vacant → log resolver") requires writes to property-service (front-office status) and a DiscrepancyResolution audit table. Neither exists. Without resolution path the report decays into a status board the supervisor reads, not acts on.
- [SME-PROPOSED] Add AC: "New
POST /rooms/:id/discrepancy/resolvebody{ action: 'UPDATE_FO_TO_VACANT' | 'UPDATE_FO_TO_OCCUPIED' | 'UPDATE_HK_TO_CLEAN' | 'UPDATE_HK_TO_DIRTY' | 'CONFIRM_NO_ACTION', reason }SHALL (a) call the corresponding service's update endpoint atomically, (b) writeDiscrepancyResolutionaudit row with(roomId, detectedAt, resolvedAt, resolverUserId, action, reason), (c) emitHOUSEKEEPING_DISCREPANCY_RESOLVED." - [SME-PROPOSED] Add AC: "Discrepancy detected SHALL stamp a
firstDetectedAton the Room row (or sidecar table). Resolution payload computesdurationMinutes. Daily summary at night-audit time (already wired vianight-audit-hk-reset.consumer) emails Front Office Manager + General Manager with totals + average + most-frequent-type."
US-006-015 — Housekeeping KPI Dashboard
Catalog status: Built (P1, 5 points) — catalog calls this US-006-010 Verified status: Built-Untested — analytics reporting-feed shipped + MV populated; in-service KPI endpoint not verified
Code locations:
hmp-housekeeping-service/src/reporting-feeds/reporting-feeds-hk.controller.ts— analytics live-proxy (room-status-snapshot, room-status-detail, lost-and-found, discrepancies)- Analytics MV (consumed via
mv_hk_staff_productivityper CLAUDE.md gotcha 5 + FU-55): populated byHOUSEKEEPING_TASK_COMPLETEDKafka events whenassignedTois truthy hmp-web-dashboard/src/app/(dashboard)/housekeeping/kpis/page.tsx— KPI dashboard- No service-local
GET /api/kpisshipped per current source structure — KPIs are computed in analytics-service and surfaced via the reporting-feeds endpoints
Unit + integration tests:
reporting-feeds-hk.controller.spec.ts— 16 specs (covers the analytics live-proxy)- No service-level KPI spec (KPIs live in analytics-service)
Use cases referencing this story: none
SME notes:
- The KPI surface is distributed — HK service publishes events; analytics-service maintains
MvHkStaffProductivity; dashboard hits analytics or HK reporting-feeds. The EPIC body story says "GET /api/v1/housekeeping/kpis" but no such endpoint exists in HK service — the dashboard must call analytics. - FU-55 producer contract locks
assignedTointo the completed event; the MV's composite-unique key depends on it. WithoutassignedTo, the row is silently dropped and the dashboard goes empty. Documented in CLAUDE.md gotcha 5.
Drift findings: (none net new — feeds D-005 above on shift model)
- [SME-PROPOSED] Add AC: "EPIC body MUST clarify the KPI surface lives in analytics-service, not housekeeping-service. The dashboard's KPI page SHALL call
/reporting-feeds/housekeeping/...(live-proxy) or/api/analytics/housekeeping-productivity(MV-backed)."
US-006-016 — Auto-Task Generation on Checkout
Catalog status: Not Started (P2, 5 points) — no catalog row (catalog only has 15) Verified status: Built
Code locations:
hmp-housekeeping-service/src/events/events.service.ts:163— subscribes toRESERVATION_CHECKED_OUT, dedup viahousekeepingTask.findFirst(W1.B.9 / AP-T05-001)EventTasksContextinterface intasks.service.ts:29-34— supports tenant context injection for event-driven creates (no AsyncLocalStorage)
Unit + integration tests:
events.service.spec.ts— 26 specs incl. 3 W1.B.9 dedup testsnight-audit-hk-reset.consumer.spec.ts— 16 specs (the night-audit-side bulk task generation)
Flow tests: covered by flow-5 indirectly
Use cases referencing this story: none
SME notes:
- EPIC body says "Not Started", code IS shipped — same drift class as US-006-013/D-006. The CHECKOUT_CLEANING auto-task with dedup is a 2026-05-07 production feature (AP-T05-001 per CLAUDE.md gotcha 0).
- VIP arrival priority elevation (EPIC body AC-2) is NOT shipped — no event listener checks the next arriving guest's
loyaltyTierto bumppriorityto HIGH. - Extended-stay suppression (EPIC body AC-3) is implicit (no checkout = no event = no task) but the corollary "auto-create TURNDOWN" is not implemented either.
Drift findings: (rolls into D-006 for catalog-vs-code numbering)
- [SME-PROPOSED] Add AC: "On
RESERVATION_CHECKED_OUT, if a future-dated arriving reservation for the sameroomIdwithin next 4 hours hasguest.loyaltyTier IN ('PLATINUM','DIAMOND')OR aVIPreservation tag, the auto-created CHECKOUT_CLEANING task SHALL be set topriority=HIGHandnotespopulated with arrival ETA + guest tier." - [SME-PROPOSED] [Indian-hospitality SME] Add AC: "Add scheduled job
dailyStayoverTaskGenerator(cron 06:00 IST per property timezone) that creates DAILY_CLEANING tasks for each occupied room AND TURNDOWN tasks for each occupied room (cron 17:00 IST). Both are dedup'd against the room's existing pending tasks for the same shift. This is the standard Indian SME hotel double-cycle expected by housekeeping supervisors."
Cross-cutting drift findings
D-001 — [P0] [Catalog-vs-EPIC-body, mirrors EP-002 D-007] Catalog (01-epic-catalog.md) and EPIC body (epics/EP-006-housekeeping.md) disagree on numbering and content from US-006-005 onward:
| Catalog US ID | Catalog title | EPIC body match |
|---|---|---|
| US-006-005 | Linen Inventory & Movement Tracking | EPIC US-006-009 + US-006-010 (collapsed) |
| US-006-006 | Minibar Par Management | EPIC US-006-011 |
| US-006-007 | Maintenance Request | EPIC US-006-008 |
| US-006-008 | Supervisor Inspection | EPIC US-006-007 |
| US-006-009 | Lost & Found | EPIC US-006-012 |
| US-006-010 | KPI Dashboard | EPIC US-006-015 |
| US-006-011 | Discrepancy Report | EPIC US-006-014 |
| US-006-012 | Auto-Reorder Alerts | EPIC US-006-013 |
| US-006-013 | Turndown Service Scheduling | catalog-only — no EPIC body story |
| US-006-014 | Room Cleaning Priority Queue | catalog-only — no EPIC body story |
| US-006-015 | Deep Clean Scheduling | catalog-only — no EPIC body story |
| US-006-016 | Mobile App Integration | catalog-only — partially covered by EPIC US-006-004 mobile queue + US-006-006 mobile photos |
| EPIC US-006-016 | Auto-Task Generation on Checkout | EPIC-body-only — no catalog row |
This RTM follows the EPIC body numbering (16 stories) and surfaces the 4 catalog-only stories as [SME-PROPOSED] new EPIC-body sections. Settle before any downstream pilot.
- [SME-PROPOSED] Adopt the EPIC body numbering as canonical (it's the more granular split). Rewrite catalog rows US-006-005..US-006-016 to match. Add 4 catalog-only stories (
Turndown Service Scheduling,Room Cleaning Priority Queue,Deep Clean Scheduling,Mobile App Integration) as new EPIC-body sections US-006-NEW-A through US-006-NEW-D. Each catalog row keeps an> Previously titled "X" in catalog prior to 2026-05-25 reconciliationalias.
Catalog-only stories — proposed new EPIC sections
US-006-NEW-A — Turndown Service Scheduling (was catalog US-006-013)
[SME-PROPOSED] Indian-hospitality 5-star and 4-star hotels run an evening turndown service (chocolates on pillows, lights off/on, robe + slippers laid out). Today there's no dedicated scheduler — turndown tasks must be created manually or as a bulk operation at shift-change.
- [SME-PROPOSED] [Indian-hospitality SME] Add AC: "Per-property
turndownConfig: { enabled: bool, startTime: '17:00', taskType: 'TURNDOWN', applyToRoomTypes: string[] }. When enabled, a cron atstartTime ISTper property timezone creates one TURNDOWN task per occupied room of the listed roomTypes. Tasks dedup'd against any existing same-day TURNDOWN. Skip rooms with activeDO_NOT_DISTURBflag from reservation."
US-006-NEW-B — Room Cleaning Priority Queue (was catalog US-006-014)
[SME-PROPOSED] Today supervisor assigns tasks then mobile queue sorts by priority+floor proximity. Per EPIC body AC-1 of US-006-004 ("URGENT first") this is implicit. A canonical "Priority Queue" story should formalize the queue invariants.
- [SME-PROPOSED] Add AC: "
GET /tasks?assigneeId=meSHALL sort by(priority DESC, IF(nextArrival within 4h) DESC, scheduledFor ASC, floor ASC). The 'nextArrival within 4h' boost requires reservation-service cross-service read of next-arrival per room — gated behind a feature flag for the v1 rollout."
US-006-NEW-C — Deep Clean Scheduling (was catalog US-006-015)
[SME-PROPOSED] Indian-hospitality reality: deep-clean every 7 stayover nights AND post-checkout for long-stays (≥7 nights). Today only manual DEEP_CLEANING tasks exist (no recurring scheduler).
- [SME-PROPOSED] [Indian-hospitality SME] Add AC: "Per-property
deepCleanConfig: { stayoverInterval: 7, longStayThresholdNights: 7 }. Cron at 06:00 IST per property timezone creates one DEEP_CLEANING task per occupied room whose current stay night count is a multiple ofstayoverInterval. OnRESERVATION_CHECKED_OUTwithnights >= longStayThresholdNights, the auto-CHECKOUT_CLEANING SHALL be upgraded to DEEP_CLEANING."
US-006-NEW-D — Mobile App Integration (was catalog US-006-016)
[SME-PROPOSED] Catalog claims "Not Started" but per US-006-004 the React Native HK app exists. Catalog row is redundant — fold into US-006-004 or split into Push Notifications + Offline Sync + Camera + Photo Upload sub-stories.
- [SME-PROPOSED] Mark catalog row OBSOLETE; absorb scope into US-006-004 (queue + offline sync) and US-006-006 (photos).
Test-bug / orphaned-test notes
tasks/__tests__/task-cross-tenant-denial.spec.ts(12 tests) — ORPHANED-TEST for AC #3 (create-time tenant validation). The spec header documents the deviation: housekeeping'sTasksService.createdoes NOT callprisma.room.findUniquefor ownership. The 12 tests pass today because they only assert "no tenant context → BadRequestException", not "wrong tenant context → ForbiddenException at create". This is the same security gap D-009 calls out from Task #11 — pair the two fixes.inspection.service.spec.tsreferencesinspection.service.ts— implementation source[CITED-BUT-NOT-FOUND]in this pass; CLAUDE.md does not enumerate the file path. Likely lives insrc/tasks/and is wired via TasksModule but needs re-grep to confirm.
See also
./PLAYBOOK.md— reconciliation pattern./EP-002-rtm.md— template + cross-EPIC reference (D-006 room-status state-machine applies; D-007/D-011 catalog-vs-code numbering drift mirrored as D-001/D-006 here)../epics/EP-006-housekeeping.md— story bodies../reviews/EP-006/— 5 of 16 stories have review files (all 2026-03-07, partially stale)hmp-housekeeping-service/CLAUDE.md+src/tasks/CLAUDE.md+src/rooms/CLAUDE.md+src/maintenance/CLAUDE.md+src/sync/CLAUDE.md— code ground-truth (overrides review files on conflict)