Skip to main content

EP-006 — Housekeeping Management RTM

EPIC: EP-006 Housekeeping Management Reconciled: 2026-05-25 Wave 1 / 21 parallel agents — see ./PLAYBOOK.md for the pattern; ./EP-002-rtm.md is 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​

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 (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-Built7 (US-006-001, -002, -003, -005, -008, -009, -010)
Built-Untested2 (US-006-012 lost & found, US-006-015 KPI dashboard)
Partial5 (US-006-004, -006, -007, -011, -014)
Stub0
Not Started2 (US-006-013, US-006-016)
Drift findings12 (D-001..D-012; 5 P0 / 5 P1 / 2 P2)
[SME-PROPOSED] refinements24
[CITED-BUT-NOT-FOUND] markers4

Catalog vs EPIC-body title drift — the catalog row US-006-005 — Linen Inventory & Movement Tracking collapses two EPIC-body stories (US-006-009 Linen Inventory Management + US-006-010 Linen Movement Tracking); the catalog row US-006-006 — Minibar Par Management maps to EPIC US-006-011; catalog US-006-007 — Maintenance maps to EPIC US-006-008; catalog US-006-008 — Supervisor Inspection maps to EPIC US-006-007; catalog US-006-009 — Lost & Found maps to EPIC US-006-012; catalog US-006-010 — KPI Dashboard maps to EPIC US-006-015; catalog US-006-011 — Discrepancy Report maps to EPIC US-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 Scheduling in 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 via getRequestContext()?.tenantId
  • hmp-housekeeping-service/src/rooms/rooms.controller.ts — PATCH /rooms/:id/status, POST /rooms/:id/inspection, POST /rooms/:id/request-inspection, GET /rooms/discrepancy
  • hmp-housekeeping-service/src/rooms/rooms.service.ts — getRoomStatusBoard(), updateRoomStatus(), inspectRoom(), getDiscrepancies()
  • hmp-housekeeping-service/src/gateway/housekeeping.gateway.ts — WebSocket /housekeeping namespace; task:created/task:updated/task:assigned/task:inspected server-to-client
  • hmp-housekeeping-service/src/events/events.service.ts — publishes ROOM_STATUS_CHANGED/ROOM_INSPECTED Kafka events
  • hmp-web-dashboard/src/components/modules/housekeeping/room-status-board.tsx [CITED-BUT-NOT-FOUND] — review file cites; not re-grep'd this pass
  • hmp-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 coverage
  • hmp-housekeeping-service/src/rooms/rooms-enhanced.service.spec.ts — 4 supplemental tests
  • hmp-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.ts
  • hmp-web-dashboard/e2e/tests/housekeeping-room-status.spec.ts
  • hmp-web-dashboard/e2e/tests/04-housekeeping.spec.ts
  • hmp-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 Prisma HousekeepingStatus enum implements only 4 (CLEAN, DIRTY, INSPECTED, PICKUP) per the US-006-001 review file Gap 4. IN_PROGRESS lives in the frontend display layer (derived from task state). OUT_OF_ORDER/OUT_OF_SERVICE/DO_NOT_DISTURB are on the parent Room table's RoomStatus/frontOfficeStatus column 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/status lands on property-service (the canonical Room owner). To reach the HK-side state machine the dashboard must hit PATCH /api/housekeeping/rooms/:id/status. Easy to miss; covered by rooms.controller.spec.ts.
  • Real-time path is Kafka → analytics; the WebSocket /housekeeping namespace + 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-side RoomStatus/frontOfficeStatus (OUT_OF_ORDER/OUT_OF_SERVICE/OCCUPIED/VACANT) and the per-room dnd flag (today on Reservation not Room). 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, publishes HOUSEKEEPING_TASK_CREATED, WebSocket task: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 specs
  • hmp-housekeeping-service/src/tasks/tasks.controller.spec.ts — 17 specs
  • hmp-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 in create() is shipped — see D-009)
  • hmp-housekeeping-service/src/events/events.service.spec.ts — 26 specs (covers RESERVATION_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 PASS
  • hmp-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 uses DAILY_CLEANING, CHECKOUT_CLEANING, DEEP_CLEANING, TURNDOWN, INSPECTION, SPECIAL_REQUEST per src/tasks/CLAUDE.md. The EPIC body story-text says CLEANING, DEEP_CLEAN, TURNDOWN, INSPECTION, MAINTENANCE, CUSTOM. Two of six values disagree (CLEANING vs DAILY_CLEANING+CHECKOUT_CLEANING; MAINTENANCE vs SPECIAL_REQUEST; CUSTOM not present). The dashboard task-list filter has its own third copy. See D-003.
  • AP-T05-001 dedup: EventsService skips creating a second CHECKOUT_CLEANING for the same reservationId+roomId within the day. 3 tests in events.service.spec.ts.
  • Cross-tenant denial spec is ORPHANED: 12 tests in task-cross-tenant-denial.spec.ts exist but their #3 (create-time tenant validation) is documented in the spec header as a [Deviation from canonical Pick D — documented]. The service's create() does NOT call prisma.room.findUnique to 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 /tasks SHALL accept taskType from the canonical 6-enum and 400 with code=TASK_TYPE_INVALID for any other value, including the deprecated CLEANING / MAINTENANCE / CUSTOM values."
  • [SME-PROPOSED] Add AC: "MAINTENANCE intent SHALL be expressed via POST /maintenance (separate domain — see US-006-008) and explicit taskType=INSPECTION for post-fix verification; do not overload HousekeepingTask for 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: "HousekeepingTask SHALL carry an optional shift enum (MORNING/EVENING/NIGHT) populated either explicitly or auto-computed from scheduledFor against 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-id tenant header)
  • hmp-housekeeping-service/src/tasks/tasks.service.ts — assign() (auto-transitions PENDING → IN_PROGRESS, publishes HOUSEKEEPING_TASK_ASSIGNED, WebSocket task:assigned)
  • Workload safeguard — SafeguardService.HK_STAFF_OVERLOADED enforced in assign() per src/tasks/CLAUDE.md. Blocks if staff already has >= maxTasksPerStaff active tasks.
  • hmp-housekeeping-service/src/staff/staff.service.ts — staff lookups for assignee validation
  • No auto-assign endpoint shipped — POST /tasks/auto-assign does 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 suite
  • hmp-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_ASSIGNED event is published per CLAUDE.md "On Assign" side effects — notification-service consumes via the standard topic-routing. The WebSocket task:assigned is 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 StaffShift or ClockEvent table. 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 a StaffShift row with shift, clockInAt, clockOutAt?, floor[]). POST /tasks/auto-assign SHALL distribute pending tasks to staff whose StaffShift.clockOutAt IS NULL AND shift matches the task's scheduledFor window AND whose floor[] 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 screen
  • hmp-mobile-apps/apps/housekeeping/app/(tabs)/tasks.tsx — full task list
  • hmp-mobile-apps/apps/housekeeping/app/(tabs)/rooms.tsx — room view
  • hmp-mobile-apps/apps/housekeeping/app/(tabs)/maintenance.tsx — maintenance entry
  • hmp-mobile-apps/apps/housekeeping/app/room/[id].tsx — per-room detail
  • hmp-mobile-apps/packages/shared-realtime/src/notifications-store.ts + useNotifications.ts — push/FCM ingest + WS reconnect with auth.token from store
  • hmp-mobile-apps/packages/shared-realtime/src/fcm-foreground-suppressor.ts — foreground FCM suppression
  • hmp-housekeeping-service/src/sync/sync.controller.ts + sync.service.ts — offline batch endpoint POST /api/sync (last-write-wins, idempotency via mutationId)
  • 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 — store
  • hmp-mobile-apps/packages/shared-realtime/src/useNotifications.test.ts — hook
  • hmp-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 + mutationId idempotency, 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 @IsIn on UpdateTaskStatusDto)
  • hmp-housekeeping-service/src/tasks/tasks.service.ts:39-45 — TASK_STATUS_TRANSITIONS exported 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 publishes HOUSEKEEPING_TASK_COMPLETED carrying assignedTo + cleanTimeMinutes + completedAt + targetHousekeepingStatus (FU-55, see gotcha 5)
  • HK_INSPECTION_PHOTO_REQUIRED safeguard (per src/tasks/CLAUDE.md) — if requireInspectionPhoto is 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_TRANSITIONS at tasks.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_productivity depends on the COMPLETED event carrying assignedTo. Regression test locked at tasks.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/:referenceId
  • hmp-housekeeping-service/src/photos/photos.service.ts — presigned URL generation + photo listing
  • hmp-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.md per EPIC numbering) verdict NOT_IMPL is stale — PhotosService is 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 on HousekeepingPhoto, no fileSizeBytes enforcement at S3 presigned-URL content-length-range time.
  • Base64-in-DB anti-pattern from the original review IS resolved — HousekeepingPhoto.photoUrl is 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: "HousekeepingPhoto SHALL carry a photoType: BEFORE | AFTER | INSPECTION_PASS | INSPECTION_FAIL enum. POST presigned-URL request SHALL accept photoType and 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 and content-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, marks inspectedBy/inspectedAt, publishes ROOM_INSPECTED)
  • No InspectionTemplate/InspectionChecklist Prisma 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 items checklist, 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 InspectionTemplate Prisma model linked to RoomType (or property-default) with sections[] (Section { name, weight, items[] }) and Item { 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 < passingThreshold default 85%) SHALL atomically (a) mark current task INSPECTED-FAIL, (b) create a new CLEANING task with priority=HIGH and notes auto-populated with failed item labels, (c) revert housekeepingStatus to DIRTY, (d) publish HOUSEKEEPING_INSPECTION_FAILED carrying 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/:id
  • hmp-housekeeping-service/src/maintenance/maintenance.service.ts — full lifecycle (status: OPEN/IN_PROGRESS/WAITING_PARTS/COMPLETED/CANCELLED) + Kafka events
  • hmp-housekeeping-service/src/tasks/escalation.service.ts — POST /tasks/:id/escalate creates engineering Work Order via asset-service, sets HousekeepingTask.workOrderId (idempotent: 400 if already linked)
  • hmp-housekeeping-service/src/maintenance/CLAUDE.md — module-level documentation
  • hmp-web-dashboard/src/app/(dashboard)/housekeeping/maintenance/page.tsx — maintenance dashboard

Unit + integration tests:

  • hmp-housekeeping-service/src/maintenance/maintenance.service.spec.ts — 6 specs
  • hmp-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 via POST /tasks/:id/escalate. The escalation path is owned by hmp-asset-service (port 3016) per platform CLAUDE.md — HK records the workOrderId but Engineering owns the WO. Double-escalation guarded (BadRequestException if workOrderId already 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/status to 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, when category=ELECTRICAL|GAS|PLUMBING AND priority=URGENT, atomically (transaction) call PropertyServiceClient.updateRoomStatus(roomId, 'OUT_OF_ORDER', { reason: 'auto-OOO from URGENT maintenance ' + requestId }) and publish MAINTENANCE_AUTO_OOO for 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/movement
  • hmp-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: LinenInventory carries totalQuantity, inRooms, inLaundry, inStock, damaged, reorderLevel. The US-006-005 (catalog numbering) review file flagged that createLinenMovement() did NOT update buckets — needs re-verification (see D-012)
  • hmp-web-dashboard/src/app/(dashboard)/housekeeping/inventory/page.tsx — inventory dashboard
  • hmp-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 itemType string. 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.ts for prisma.$transaction around the movement create + bucket update. If absent, ship the atomic transaction now. Add 6 unit tests, one per LinenMovementType, asserting BOTH the movement row IS written AND the correct LinenInventory bucket pair IS adjusted.
  • [SME-PROPOSED] [Indian-hospitality SME] Add AC: "linen-categories SHALL 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 as rooms × multiplier (towels 3×, sheets 2× to account for inLaundry over 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/movement
  • hmp-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_OFF into DAMAGED (stained/torn — still pending decision) and DISCARDED (final write-off). Adding DAMAGED gives accounts team a 30-day window to recover or discard. Monthly loss-rate calculation SHALL exclude DAMAGED rows 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/charge
  • hmp-housekeeping-service/src/minibar/minibar.service.ts — getByRoom, updateConsumption, replenishAll, chargeToFolio
  • hmp-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-templates for the receipt). The minibar sellingPrice is 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/:roomId per room.

Drift findings: (none net new beyond cross-cutting D-012 enum concerns)

  • [SME-PROPOSED] [Indian-hospitality SME] Add AC: "MinibarItem SHALL carry gstSlab: '5'|'12'|'18'|'28' and pricingMode: 'GST_INCLUSIVE'|'GST_EXCLUSIVE' (default GST_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=X returning 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/dispose
  • hmp-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 InventoryController at /api/lost-found per CLAUDE.md gotcha 1

Unit + integration tests:

  • lost-found.service.spec.ts — 16 specs
  • reporting-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) AND LostFoundController:/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, and Minibar.consume SHALL call PropertyServiceClient.assertRoomOwnership(roomId, tenantId) (or use local Room read-model lookup) BEFORE writing the row. Reject forged roomId with ForbiddenException('ROOM_NOT_OWNED_BY_TENANT'). Add 4 cross-tenant .spec.ts cases 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/receive
  • hmp-housekeeping-service/src/inventory/auto-reorder.service.ts — auto-reorder evaluation + PO drafting
  • hmp-housekeeping-service/src/inventory/dto/reorder-rule.dto.ts — ReorderRule DTO
  • 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.drafted event; 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 (or Partial until notification-service Kafka consumption is verified). Add a regression test asserting auto-reorder.service.ts publishes housekeeping.inventory.below_reorder on bucket drop below reorderPoint.

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/discrepancy
  • hmp-housekeeping-service/src/rooms/rooms.service.ts — getDiscrepancies() (queries OR: [{ frontOfficeStatus: OCCUPIED, housekeepingStatus: CLEAN }, { frontOfficeStatus: VACANT, housekeepingStatus: DIRTY }], classifies as POSSIBLE_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 suite
  • reporting-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 discrepancy read joins FO and HK status, both on the local Room read-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/resolve body { 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) write DiscrepancyResolution audit row with (roomId, detectedAt, resolvedAt, resolverUserId, action, reason), (c) emit HOUSEKEEPING_DISCREPANCY_RESOLVED."
  • [SME-PROPOSED] Add AC: "Discrepancy detected SHALL stamp a firstDetectedAt on the Room row (or sidecar table). Resolution payload computes durationMinutes. Daily summary at night-audit time (already wired via night-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_productivity per CLAUDE.md gotcha 5 + FU-55): populated by HOUSEKEEPING_TASK_COMPLETED Kafka events when assignedTo is truthy
  • hmp-web-dashboard/src/app/(dashboard)/housekeeping/kpis/page.tsx — KPI dashboard
  • No service-local GET /api/kpis shipped 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 assignedTo into the completed event; the MV's composite-unique key depends on it. Without assignedTo, 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 to RESERVATION_CHECKED_OUT, dedup via housekeepingTask.findFirst (W1.B.9 / AP-T05-001)
  • EventTasksContext interface in tasks.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 tests
  • night-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 loyaltyTier to bump priority to 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 same roomId within next 4 hours has guest.loyaltyTier IN ('PLATINUM','DIAMOND') OR a VIP reservation tag, the auto-created CHECKOUT_CLEANING task SHALL be set to priority=HIGH and notes populated 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 IDCatalog titleEPIC body match
US-006-005Linen Inventory & Movement TrackingEPIC US-006-009 + US-006-010 (collapsed)
US-006-006Minibar Par ManagementEPIC US-006-011
US-006-007Maintenance RequestEPIC US-006-008
US-006-008Supervisor InspectionEPIC US-006-007
US-006-009Lost & FoundEPIC US-006-012
US-006-010KPI DashboardEPIC US-006-015
US-006-011Discrepancy ReportEPIC US-006-014
US-006-012Auto-Reorder AlertsEPIC US-006-013
US-006-013Turndown Service Schedulingcatalog-only — no EPIC body story
US-006-014Room Cleaning Priority Queuecatalog-only — no EPIC body story
US-006-015Deep Clean Schedulingcatalog-only — no EPIC body story
US-006-016Mobile App Integrationcatalog-only — partially covered by EPIC US-006-004 mobile queue + US-006-006 mobile photos
EPIC US-006-016Auto-Task Generation on CheckoutEPIC-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 reconciliation alias.

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 at startTime IST per 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 active DO_NOT_DISTURB flag 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=me SHALL 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 of stayoverInterval. On RESERVATION_CHECKED_OUT with nights >= 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's TasksService.create does NOT call prisma.room.findUnique for 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.ts references inspection.service.ts — implementation source [CITED-BUT-NOT-FOUND] in this pass; CLAUDE.md does not enumerate the file path. Likely lives in src/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)