housekeeping-service
Bounded Context: Housekeeping (Core) · Owner: Operations · Phase: 0 · Storage: Cloud SQL Postgres (shared schema + RLS, monthly partitioning on
housekeeping_tasks) + transactional outbox · Bundle: services/housekeeping-service/
housekeeping-service owns the room-status lifecycle and the housekeeping task lifecycle for Ghasi Melmastoon — the multi-tenant hotel SaaS with an Electron offline-first backoffice and GCP-hosted cloud. It is the single authority for whether a room is clean, dirty, cleaning, inspected, ready, out_of_order, or out_of_service, for which staff member is responsible for cleaning it on a given shift, and for the operational hand-off with reservation-service (every checkout creates a turnover task) and property-service (which retains the structural definition of the room and its long-term OOO state).
The service is a Core context in the platform's domain classification (02 §3) because room-readiness is a strict gate on every check-in — a guest cannot be moved into a room that the cleaning board has not flipped to ready, and a missed flip cascades into front-desk delays, lobby congestion, and (eventually) cancellations. Hotels in our target markets (Afghanistan, Tajikistan, Iran) typically run with 2–6 housekeepers per shift, often without per-staff mobile devices, which means the shared desktop must be the source of truth during a shift and the sync engine must be forgiving about offline operation in basement laundry rooms and floor-end linen closets that lose Wi-Fi.
Purpose
- Be the single authoritative aggregate for room status and housekeeping task lifecycle across every property a tenant operates.
- Drive the turnover saga automatically:
reservation.checked_out.v1arrives → a turnoverHousekeepingTaskis created with priority derived from the next arrival's ETA → the task is auto-assigned to the housekeeper with the best score (proximity, current workload, language, skill) using the AI routing port → the task is worked on the desktop board untiltask.completed.v1→ anInspectionis scheduled (or skipped per tenant config) → on pass,room.status_changed.v1flips toreadyand the front-desk arrivals board lights up. - Keep the housekeeping board on the Electron desktop responsive to drag-and-drop reassignment, partial-task pause/resume, and mid-stay cleaning requests captured at the front desk.
- Let cleaners report a maintenance issue mid-task (broken faucet, stained mattress) without leaving the cleaning flow — the service emits
room.maintenance_required.v1formaintenance-serviceto open a work order, and the task either pauses (light issue) or is converted to arequires_maintenanceoutcome (heavy issue). - Provide linen inventory light-stock tracking with low-watermark alerts so the housekeeping supervisor knows when towels and sheets are running short before a 6-room turnover hits the linen closet.
Key responsibilities
- Auto-create turnover tasks on
melmastoon.reservation.checked_out.v1with priority computed from next-arrival ETA, room type, and currentrequires_maintenanceflags. - Manual + AI-assisted task assignment — staff can drag-drop on the board; the AI routing port (calls
ai-orchestrator-service) suggests an optimal allocation per shift considering proximity (group rooms by floor/wing), staff workload, ETA, language match, and skill (deep clean vs turnover). - Task progress lifecycle —
pending → assigned → in_progress → (paused ↔ in_progress) → completed | failed | cancelled | requires_maintenance, with append-only audit on every transition. - Room-status state machine —
clean → dirty → cleaning → cleaned → inspected → readywith side branchesout_of_order(transient maintenance) andout_of_service(longer-term, owned byproperty-servicebut echoed here for the board). - Mid-stay cleaning — staff or guest can request a stay-in clean; for stays > 1 night, daily mid-stay tasks are auto-scheduled per tenant config (default opt-in for properties marked "full-service").
- Inspection — optional per tenant; if enabled, a senior housekeeper or supervisor must run a checklist before the room flips to
ready. - Lost & found — recorded against the room (and the recently checked-out reservation if within 7 days), matched against guest claims, disposed per tenant policy after retention window.
- Linen inventory light tracking — issued counts per task, returned counts on completion, low-stock alerts at tenant-configured watermarks.
- Shift staffing-gap detection — monitors active shifts vs scheduled tasks; if a staff member calls in sick mid-shift, fires
shift.staffing_gap_detected.v1so the manager can re-route or call in a relief. - Checklist templates — fully tenant-configurable (turnover, deep clean, maintenance check, post-renovation, …); versioned and bound to tasks at assignment time so a checklist update never silently changes an in-flight task.
- Offline-first desktop board — drag-and-drop reassignment, status flips, and task completion all work against the local SQLite store and sync back; conflict policy declared in SYNC_CONTRACT.
- Pashto/Dari/Persian UI — the housekeeping board ships with first-class support for staff who do not read English; locale is per user, not per tenant.
Aggregates owned
| Aggregate | Cardinality | Purpose | Identity prefix |
|---|---|---|---|
HousekeepingTask | root, 1 per cleaning unit | Lifecycle, assignment, checklist binding, linen issuance, outcome | hkt_ |
RoomStatus | 1 per room (singleton per (tenant, property, room)) | Live status flag and last-flip audit | (singleton key) |
CleaningChecklist | 0..N per tenant | Versioned template; bound at assignment time | chl_ |
Inspection | 0..1 per task | Pass/fail with photo evidence | ins_ |
LinenInventory | 1 per linen line per property | Light stock tracking, low-watermark alerts | lin_ |
LostAndFound | 0..N per room/reservation | Capture, match, dispose | laf_ |
StaffShiftAssignment | 0..N per shift | Housekeeping-scoped projection of staff-service shift; carries capacity and active task count | (composite, ULID) |
RoomBlock | 0..N per room | Shared notion with maintenance-service — block reasons (cleaning, inspection, maintenance, oos); we own the cleaning/inspection rows, maintenance-service owns the maintenance rows | (composite, ULID) |
Key APIs (REST, /api/v1/housekeeping)
| Method | Path | Purpose |
|---|---|---|
POST | /tasks | Create a task (auto-called by event handlers; manual create via backoffice) |
GET | /tasks | List tasks for a property/shift (filter by status, assignee, priority) |
GET | /tasks/:id | Read one task |
POST | /tasks/:id/assign | Assign or reassign to a staff member |
POST | /tasks/:id/start | Staff starts working |
POST | /tasks/:id/pause | Pause (with reason: break, awaiting linen, awaiting maintenance) |
POST | /tasks/:id/resume | Resume from pause |
POST | /tasks/:id/complete | Mark complete (with linen returned, time spent, photos) |
POST | /tasks/:id/fail | Mark failed (with reason; triggers re-route or escalation) |
POST | /tasks/:id/escalate | Escalate to supervisor (e.g., room not vacated) |
POST | /tasks/:id/maintenance-required | Convert task or pause + emit room.maintenance_required.v1 |
POST | /tasks/:id/inspections | Run inspection (pass/fail + checklist results) |
GET | /rooms/:roomId/status | Read current room status |
POST | /rooms/:roomId/status | Manual flip (supervisor override; audit-flagged) |
POST | /rooms/:roomId/block | Block a room (cleaning / inspection / maintenance / oos) |
DELETE | /rooms/:roomId/block/:blockId | Clear a block |
POST | /checklists | Create or version a checklist template |
GET | /checklists | List active checklist templates |
POST | /lost-and-found | Record a found item |
POST | /lost-and-found/:id/match | Match against a guest claim |
POST | /lost-and-found/:id/dispose | Dispose per policy |
GET | /linen | List linen lines + current stock |
POST | /linen/:lineId/issue | Issue linen against a task |
POST | /linen/:lineId/return | Return linen on completion |
GET | /board | Composite read for the desktop board (tasks + room statuses + active staff) |
GET | /stats/turnover | Turnover statistics for a property + date range |
Direct callers are bff-backoffice-service (Electron desktop, the dominant traffic) and bff-tenant-booking-service (mid-stay cleaning request from in-stay guest). Internal event handlers are exposed under /internal/events/<topic-short> and authenticated by Pub/Sub OIDC.
Key events published
| Event | Trigger |
|---|---|
melmastoon.housekeeping.task.created.v1 | Task created (auto from checkout, or manual) |
melmastoon.housekeeping.task.assigned.v1 | Task assigned to staff |
melmastoon.housekeeping.task.reassigned.v1 | Assignment changed |
melmastoon.housekeeping.task.started.v1 | Staff pressed Start |
melmastoon.housekeeping.task.paused.v1 | Pause with reason |
melmastoon.housekeeping.task.resumed.v1 | Resumed after pause |
melmastoon.housekeeping.task.completed.v1 | Task complete (cleaning + checklist done) |
melmastoon.housekeeping.task.failed.v1 | Task failed with reason |
melmastoon.housekeeping.task.cancelled.v1 | Cancelled (reservation modified, room reassigned, …) |
melmastoon.housekeeping.task.escalated.v1 | Escalated to supervisor |
melmastoon.housekeeping.room.status_changed.v1 | Room status flipped |
melmastoon.housekeeping.room.maintenance_required.v1 | Cleaning revealed an issue → triggers maintenance-service |
melmastoon.housekeeping.inspection.passed.v1 | Inspection passed |
melmastoon.housekeeping.inspection.failed.v1 | Inspection failed (room re-cleaned) |
melmastoon.housekeeping.checklist.template_updated.v1 | Tenant changed a checklist template |
melmastoon.housekeeping.lost_item.recorded.v1 | Item logged in lost & found |
melmastoon.housekeeping.lost_item.matched.v1 | Item matched to a guest claim |
melmastoon.housekeeping.lost_item.disposed.v1 | Item disposed per policy |
melmastoon.housekeeping.linen.low_stock_alert.v1 | Stock fell below the low-watermark |
melmastoon.housekeeping.shift.staffing_gap_detected.v1 | Shift capacity insufficient for queued tasks |
Key events consumed
| Event | Effect |
|---|---|
melmastoon.reservation.checked_out.v1 | Auto-create turnover HousekeepingTask with priority by next-arrival ETA |
melmastoon.reservation.early_checkout.v1 | Priority bump on existing turnover task; create one if not yet created |
melmastoon.reservation.modification.requested.v1 | Mid-stay cleaning request → create mid_stay_clean task |
melmastoon.maintenance.work_order.completed.v1 | Return room from out_of_order to cleaning queue (recreate task if needed) |
melmastoon.staff.shift.started.v1 | Project a StaffShiftAssignment; refresh router capacity |
melmastoon.staff.shift.ended.v1 | Tear down StaffShiftAssignment; reassign open tasks |
melmastoon.ai_orchestrator.suggestion.housekeeping_routing.v1 | Apply (with HITL gate) the AI routing suggestion to pending tasks |
melmastoon.property.room.archived.v1 | Cancel pending tasks; clear RoomStatus row |
melmastoon.tenant.settings.changed.v1 | Refresh in-memory cache of inspection-required flag, mid-stay cadence, low-watermarks |
Upstream / downstream
Upstream (we consume): reservation-service, property-service, staff-service, maintenance-service, tenant-service, ai-orchestrator-service.
Downstream (we publish for): maintenance-service, notification-service (low-stock alerts, escalations), analytics-service (turnover stats, staff performance), audit-service, search-aggregation-service (room-readiness facet for tenant booking surface), sync-service, bff-backoffice-service, bff-tenant-booking-service (mid-stay request acknowledgement).
Non-functional requirements
| NFR | Target |
|---|---|
| Turnover task auto-create latency (event in → task visible) | < 2 s p95 |
Board read p99 (GET /board) | < 250 ms with 200 active rooms |
| Board write (drag-drop reassign) p99 | < 400 ms end-to-end including outbox publish |
| API availability | 99.9% monthly |
| Tenant isolation | RLS-enforced; tenant-isolation.spec.ts mandatory in CI |
| Sync footprint | Tasks of last 30 days + room statuses + active checklists + linen + open lost-and-found replicated to desktop SQLite |
| Replicas | Min 2 Cloud Run instances (hot path); separate shift-staffing-gap worker as Cloud Run cron job (every 60 s) |
| Partitioning | housekeeping_tasks partitioned monthly; partition-pruning enforced on hot reads |
Where to go next
- Implementation-grade detail:
services/housekeeping-service/SERVICE_OVERVIEW.mdand the rest of the 17-doc bundle. - Turnover saga and event taxonomy:
docs/04-event-driven-architecture.md. - Conflict policy table for the desktop sync engine:
docs/02-enterprise-architecture.md§8.2. - Front-desk operational flow that intersects housekeeping:
docs/journeys/01-core-user-journeys.md. - AI routing capability:
docs/08-ai-architecture.md.