Skip to main content

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 with docs/08-ai-architecture.md.


1. Capabilities

#CapabilityModePurpose
AI-HK-1Shift routing — assign N pending tasks to M active housekeepers minimising total walking time + balancing load + matching language/skillsync (REST) and async (event)Replace ad-hoc supervisor allocation; respect priority and ETA
AI-HK-2ETA estimation — predicted time-to-complete per tasksyncInputs to routing & to the front-desk arrivals projection
AI-HK-3Linen runway forecast — minutes of cleaning sustainable given current stock + forecast turnoverscheduled (hourly)Drives linen.low_stock_alert.v1 smartness
AI-HK-4Anomaly flag — task taking abnormally long → suggest escalationsync (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:

  1. Validates that the shift is still active and the suggestion is fresh (< 5 min).
  2. Persists the suggestion to routing_suggestions (transient table; 24 h retention).
  3. If the tenant gate is auto_apply and no manual changes were made since generatedAt, calls AssignTaskUseCase per row and emits task.reassigned.v1 with reason="router_suggestion".
  4. Otherwise, surfaces the suggestion on the desktop board (banner: "AI suggests reassignments — review").

4. HITL gate

Tenant settingDefaultBehaviour
housekeeping.routing.hitlModesupervisor_approvalSuggestion shown on the board; supervisor approves all/some/none
auto_applyServer applies suggestion if no manual edits since generatedAt
disabledSuggestions 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 from staff-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_apply by an explicit tenant decision.
  • Every applied suggestion writes an audit row with cause="router_suggestion", the suggestionId, 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):

  • BuildRoutingSnapshotUseCase returns 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_apply mode 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.suggest with 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.