J-09 — Mid-Stay Issue: Lock Battery Dies
One-liner: Lock battery dies; receptionist issues a fallback key and a maintenance ticket without disrupting the guest.
1. Purpose
A guest reports that their door lock won't unlock. The receptionist diagnoses (battery dead, vendor reachability), issues a fallback credential, raises a maintenance ticket, and arranges replacement, all in minutes. Outcome: guest gets back in their room within 5 min; maintenance ticket auto-created with priority; lock service updated; audit log entry.
2. Persona Context
- Persona: Receptionist (primary); Maintenance (downstream); Guest (subject).
- Surface: Electron Desktop.
- Primary BFF:
bff-backoffice-service. - Backing services:
lock-and-key-service,maintenance-service,notification-service,audit-service. - Preconditions: Lock has known recent telemetry; backup card pool available at front desk.
- Trigger: Guest reports lock issue (call, SMS, in-person); receptionist clicks "Lock issue" on the room tile.
3. Entry Points
| # | Entry | Notes |
|---|---|---|
| 1 | "Lock issue" button on room tile | Default |
| 2 | Lock telemetry alert (battery low) | Pre-emptive (P2) |
| 3 | Maintenance "scheduled lock service" task | Planned |
4. Screen-by-Screen Flow
4.1 LockDiagnosticsModal
- Layout: Lock vendor, last unlock timestamp, battery telemetry (if available), connectivity status, "Issue fallback key" CTA, "Create maintenance ticket" CTA.
- Components:
LockDiagnosticsCard,Button(variants). - Offline: Reads from SQLite snapshot; can issue fallback via offline-cert.
- AI: None in P1; P2 — anomaly detector for unlock patterns.
- Errors: Vendor unreachable -> banner; recommend fallback path.
- Loading: Sub-200 ms.
- A11y: Diagnostics fields announced; CTAs labelled.
- RTL: Mirror.
- Perf: Mount <= 200 ms.
- Telemetry:
frontend.lock.diagnostics_viewed { roomId }.
4.2 IssueFallbackKeyPanel
- Layout: Backup card pool selector or mobile-key resend; encoder progress; "Done" CTA.
- Components:
BackupCardSelector,MobileKeyResendButton,LockEncoderPanel. - Offline: Offline-cert path; backup card encoded locally.
- AI: None.
- Errors: Encoder USB disconnect -> retry UX.
- Loading: Encoder cycle <= 8 s.
- A11y: Encoder progress announced.
- RTL: Mirror.
- Perf: <= 8 s p95.
- Telemetry:
frontend.lock.fallback_key_issued { method }.
4.3 CreateMaintenanceTicketForm
- Layout: Pre-filled with room, lock id, symptom (battery suspected), priority (high), photo upload (optional), "Create ticket" CTA.
- Components:
Form,PrioritySelect,PhotoUpload. - Offline: Ticket queued.
- AI: None.
- Errors: Validation per field.
- Loading: Submit <= 200 ms local / 500 ms online.
- A11y: Field labels.
- RTL: Mirror.
- Perf: Submit <= 500 ms.
- Telemetry:
frontend.maintenance.ticket_created { type: lock, priority }.
5. State Machine
6. Data Requirements
6.1 Server state
GET /api/v1/lock/:roomId/diagnosticsPOST /api/v1/lock/key/issue(idempotent; withpurpose=fallback)POST /api/v1/maintenance/tickets(idempotent)
6.2 Local persistence
- SQLite
lock_diagnostics_local,maintenance_tickets_local,outbox.
6.3 Idempotency
- All mutations carry
X-Idempotency-Key.
7. AI Behavior
n/a in P1; P2: lock-anomaly detector.
8. Offline Behavior
- Diagnostics read from SQLite (last known telemetry).
- Fallback key issued via offline-cert.
- Maintenance ticket queued.
9. Error States
| Error | Trigger | UX shown | Recovery | Telemetry |
|---|---|---|---|---|
LOCK_VENDOR_UNREACHABLE | Vendor 5xx | Banner; offline-cert fallback offered | Use fallback | error.surfaced { code } |
BACKUP_CARD_POOL_EMPTY | Pool depleted | Modal: "Restock pool - meanwhile use mobile key" | Use mobile-key path | frontend.lock.backup_pool_empty |
ENCODER_DISCONNECTED | USB unplug | Retry UX | Replug + retry | frontend.lock.encoder_disconnected |
MAINTENANCE_TICKET_CREATE_FAILED | 5xx | Queued in outbox; banner | Auto-retry on reconnect | error.surfaced { code } |
10. E2E Test Gates
- Composite gate
G-DESK-OPS-1: report -> diagnostics -> fallback key -> ticket -> resolved. - Vendor unreachable variant.
- Backup pool empty variant.
11. Performance Requirements
| Metric | Target |
|---|---|
| Diagnostics mount | <= 200 ms |
| Fallback key issue | <= 8 s p95 |
| Ticket create | <= 500 ms p95 |
| Total flow | <= 5 min wall-clock (incl. encoder + walk) |
12. Accessibility Requirements
- All keyboard-completable.
- Encoder progress + diagnostics announced.
- Photo upload has accessible label and progress.
13. Telemetry
Frontend events
frontend.lock.diagnostics_viewed { roomId }frontend.lock.fallback_key_issued { method }frontend.maintenance.ticket_created { type: lock, priority }
Domain events emitted
melmastoon.lock_and_key.diagnostics.viewed.v1(sampled)melmastoon.lock_and_key.credential.issued.v1(withpurpose=fallback)melmastoon.maintenance.ticket.created.v1melmastoon.audit.recorded.v1
14. Success Criteria
- Guest back in room <= 5 min wall-clock.
- Maintenance ticket created with correct priority and pre-filled context.
- Lock service updated; audit log entry.
- Offline path proven (offline-cert + queued ticket).