housekeeping-service — AI_INTEGRATION
All AI calls go through
ai-orchestrator-service. This service exposes one application port —RoutingSuggestionPort— and consumes one event —melmastoon.ai_orchestrator.suggestion.housekeeping_routing.v1. No model is loaded in this service. HITL gate is the default. Aligned withdocs/08-ai-architecture.md.
1. Capabilities
| # | Capability | Mode | Purpose |
|---|---|---|---|
| AI-HK-1 | Shift routing — assign N pending tasks to M active housekeepers minimising total walking time + balancing load + matching language/skill | sync (REST) and async (event) | Replace ad-hoc supervisor allocation; respect priority and ETA |
| AI-HK-2 | ETA estimation — predicted time-to-complete per task | sync | Inputs to routing & to the front-desk arrivals projection |
| AI-HK-3 | Linen runway forecast — minutes of cleaning sustainable given current stock + forecast turnover | scheduled (hourly) | Drives linen.low_stock_alert.v1 smartness |
| AI-HK-4 | Anomaly flag — task taking abnormally long → suggest escalation | sync (background scoring) | Proactive escalation hint on the board |
All capabilities are suggestions. They never bypass the domain layer.
2. Port
export interface RoutingSuggestionPort {
suggestAssignments(input: RoutingInput): Promise<RoutingSuggestion>;
}
export interface RoutingInput {
tenantId: TenantId;
propertyId: PropertyId;
shiftId: string;
generatedAt: Date;
openTasks: Array<{
taskId: HousekeepingTaskId;
roomId: RoomId;
floor?: number;
wing?: string;
kind: TaskKind;
priority: TaskPriority;
scheduledFor?: Date;
estimatedMinutes?: number;
requiredSkills?: string[];
}>;
activeStaff: Array<{
staffId: StaffId;
languages: string[];
skills: string[];
capacityMinutes: number;
loadMinutes: number;
activeTaskCount: number;
lastRoomId?: RoomId; // for proximity heuristics
}>;
constraints?: { maxRoomsPerStaff?: number; preserveExistingAssignments?: boolean };
}
export interface RoutingSuggestion {
suggestionId: string;
modelVersion: string;
generatedAt: string;
rows: Array<{ taskId: HousekeepingTaskId; staffId: StaffId; score: number; reasoning: string }>;
unassignableTasks: Array<{ taskId: HousekeepingTaskId; reason: string }>;
}
The infrastructure adapter (infrastructure/ai/routing.adapter.ts) calls ai-orchestrator-service over REST (POST /api/v1/orchestrator/housekeeping/route-suggest). Timeout: 1.5 s p99 budget; 2.5 s hard cap.
3. Async ingestion (background optimization)
ai-orchestrator-service may publish improved suggestions for an active shift on melmastoon.ai_orchestrator.suggestion.housekeeping_routing.v1. The RoutingSuggestionReceivedHandler:
- Validates that the shift is still active and the suggestion is fresh (< 5 min).
- Persists the suggestion to
routing_suggestions(transient table; 24 h retention). - If the tenant gate is
auto_applyand no manual changes were made sincegeneratedAt, callsAssignTaskUseCaseper row and emitstask.reassigned.v1withreason="router_suggestion". - Otherwise, surfaces the suggestion on the desktop board (banner: "AI suggests reassignments — review").
4. HITL gate
| Tenant setting | Default | Behaviour |
|---|---|---|
housekeeping.routing.hitlMode | supervisor_approval | Suggestion shown on the board; supervisor approves all/some/none |
auto_apply | Server applies suggestion if no manual edits since generatedAt | |
disabled | Suggestions ignored; manual-only |
Stored on tenant-service and refreshed via tenant.settings.changed.v1.
5. Inputs we collect / outputs we emit
We read:
- Open tasks (this DB)
- Active shifts (
StaffShiftAssignment, projected fromstaff-service) - Room metadata: floor, wing (cached read-through to
property-service) - Last completed-task room per staff (proximity heuristic)
- Linen on-hand (this DB)
We emit to the orchestrator (only when needed): the RoutingInput snapshot. No PII (no guest data, no phone numbers, no payment data). Staff IDs are pseudonymous within the tenant. The reasoning field returned must be <= 240 chars and is stored on audit_events for explainability.
6. Privacy and safety
- Suggestions are never auto-applied without HITL gate set to
auto_applyby an explicit tenant decision. - Every applied suggestion writes an audit row with
cause="router_suggestion", thesuggestionId, the model version, and the rejected alternatives (if any). - The orchestrator is not allowed to call us back — it publishes to the event topic; the push subscription is what activates the suggestion.
- We never expose model raw logits, prompts, or training-data references to clients.
7. Fallback
If the routing port is unavailable (timeout, 5xx, circuit open):
BuildRoutingSnapshotUseCasereturns an empty suggestion (rows: []).- The board surfaces "Routing assistant unavailable — manual mode".
- Manual drag-and-drop continues working with no degradation.
The circuit breaker (per RoutingSuggestionPort) trips after 5 consecutive failures or > 50% errors over 60 s, half-opens after 30 s.
8. Performance and cost
- p99 latency budget for sync
suggestAssignments: 1.5 s (well within board interactivity). - We cap input size: max 200 open tasks, max 30 active staff. Larger shifts are paged.
- Suggestion frequency in
auto_applymode is rate-limited to 1/min per(tenant, property, shift).
9. Observability
- Metric
melmastoon.housekeeping.ai.routing.requests(labels: outcome, hitl_mode). - Metric
melmastoon.housekeeping.ai.routing.applied_rows(labels: tenant_id_hashed). - Metric
melmastoon.housekeeping.ai.routing.fallback(counter). - Trace span
housekeeping.ai.routing.suggestwith attributes(tenant_id_hashed, property_id, shift_id, task_count, staff_count, model_version). - Dashboard "AI Routing Health" pinned in Grafana folder
housekeeping.
10. Future capabilities (not implemented in Phase 0)
- Voice-driven task updates in Pashto/Dari/Persian (intercept on the desktop, transcribe via on-device ASR, post via REST).
- Photo-based completeness scoring (model evaluates inspection photos for cleanliness signals).
- Cross-property load balancing for multi-property tenants in the same compound.
11. Cross-link
- Platform AI architecture:
docs/08-ai-architecture.md. - Application port wiring:
APPLICATION_LOGIC.md§9. - Event schema for incoming suggestions:
EVENT_SCHEMAS.md§3.