Sprint 4 Plan — Production Readiness Demo Blockers
Source inputs:
Total raw findings: 134. After dedup: ~110 unique. Top 30 in Sprint 4.
Sprint structure
| Sprint | Theme | Days | Items |
|---|
| 4 | Demo blockers (silent failures, dead handlers, contract gaps, state-machine fixes, UUID leaks, hardcoded mocks) | 5-7 | ~30 |
| 5 | Missing cross-module flows (POS↔folio, checkout payment collection, tax invoice for non-GST, key encoder, tape chart drag-drop, mobile app testing) | 7-10 | ~12 |
| 6 | UX polish (alignment, time-precision tape chart, transition-history tabs, confirm dialog standardisation) | 3-4 | ~10 |
Sprint 4 — ranked execution
Block A — Silent-failure / fake-success patterns (Day 1, ~6 items)
Highest priority because users see "success" toasts when nothing actually happened. Operationally dangerous.
| # | Item | Files | Closes |
|---|
| 4-1 | Replace 5× catch { toast.success(...) } with real error toast | control-center/exceptions/page.tsx:201,213,527, control-center/actions/page.tsx:204,223 | Audit A-7 |
| 4-2 | key-cards/page.tsx replaceMutation.onError → real error toast | front-desk/key-cards/page.tsx:189-194 | A-8 |
| 4-3 | key-cards/page.tsx Encode New Card → real form dialog (not empty payload) | front-desk/key-cards/page.tsx:215-218 | A-8 |
| 4-4 | keynova.tsx arrival cards onClick={() => {}} → router.push | (dashboard)/keynova.tsx:320 | A-12 |
| 4-5 | "View full profile" Button onClick → Link href=/crm/guests/{id} | reservations/[id]/keynova.tsx:1050 | Issue #4, A-13 |
| 4-6 | "View all →" span → Link to /housekeeping/staff | housekeeping/keynova.tsx:544 | A-15 |
| 4-7 | revenue-manager/strategy handleSave → real useUpdateRevenueStrategy() mutation OR hide page behind FLAGS.revenueManagerEnabled (default OFF) | revenue-manager/strategy/page.tsx:117 | A-2 |
Block B — State machine post-action redirects (Day 1-2, ~7 items)
| # | Item | Files | Closes |
|---|
| 4-8 | Check-in success → router.replace('/front-desk/folio/' + id) | front-desk/check-in/[id]/keynova.tsx:933 | Issue #1, C-1 |
| 4-9 | Folio detail page: read folio.status, gate Settle/Post Charge/Record Payment buttons; show SETTLED badge | front-desk/folio/[id]/page.tsx | C-6 |
| 4-10 | Settle Folio onSuccess → router.push to /front-desk/check-out/${reservationId} if reservation is CHECKED_IN | front-desk/folio/[id]/page.tsx:365 | C-7 |
| 4-11 | Add "Mark No-Show" action to /reservations/[id] overflow menu | reservations/[id]/keynova.tsx | C-2 |
| 4-12 | Add "Confirm" button for PENDING reservations | reservations/[id]/keynova.tsx | C-3 |
| 4-13 | Add "Cancel Order" / "Void Order" action to POS detail panel | pos/orders/keynova.tsx:685 | C-12 |
| 4-14 | Universal guard-page improvement: every "wrong status" guard gets router.push CTA | check-in :1026, check-out :325 | C-23 |
Block C — DTO contract critical fixes (Day 2-3, ~9 items)
| # | Item | Closes |
|---|
| 4-15 | Frontdesk Check-In DTO: required idDocumentType/Number, send reservationId in body, rename signature → signatureImageUrl | B-10 |
| 4-16 | Settings Add User: add tenantId from session, type role against backend enum, validate password ≥12 chars | B-3, B-5 |
| 4-17 | CRM Create Campaign + Drip Step: rebuild DTO with propertyId/channel/segmentRules/createdBy, uppercase all enums | B-6, B-19, Issue #3 |
| 4-18 | Guest Create: inject propertyId from store, uppercase Gender enum | B-7 |
| 4-19 | Settings Create Rate Plan / Cancellation Policy: add propertyId, uppercase enums | B-8, B-9 |
| 4-20 | Folio Post Charge / Payment / Void: include folioId in body, uppercase enums, sanitize transactionCode regex, require reason ≥ 3 chars on void | B-11, B-12, B-13, Issue #17 path |
| 4-21 | Housekeeping Create Task assignee: replace <Input> with user picker bound to housekeepingStaffApi.list() | B-2, Issue #8 |
| 4-22 | Room Update Status: fix path to PATCH /rooms/:id/status; reconcile HousekeepingStatus enum (drop OUT_OF_ORDER/OUT_OF_SERVICE — those belong on RoomStatus) | B-14, C-9, Issue #6 |
| 4-23 | DSAR enum: reconcile 5 backend ↔ 4 frontend states, add explicit translation layer | C-19 |
Block D — Stub data hidden behind flags (Day 3-4, ~5 items)
| # | Item | Closes |
|---|
| 4-24 | Whole /revenue-manager section behind FLAGS.revenueManagerEnabled (default OFF). 5 pages of 100% mock data. | A-1 to A-5 |
| 4-25 | Spa therapists/treatments/packages: replace placeholders with real EmptyState components | A-9 |
| 4-26 | Housekeeping VIP_CHECKLIST hardcoded → load from existing /housekeeping/checklists endpoint per room type | A-14, Issue #7b |
| 4-27 | Check-in keynova members hardcoded + handleEncodeKey fake toast → hide section OR wire to /front-desk/key-cards/encode | A-16, Issue #5 part |
| 4-28 | POS receipt propertyName: 'Hotel' hardcoded → read from useAppStore.currentProperty.name | A-36 |
Block E — UUID rendering fixes (Day 4, batch ~6)
| # | Item | Closes |
|---|
| 4-29 | Backend addition + frontend rendering for performedByName/createdByName/assignedToName in 6 places: cash-management/safe, front-desk/phone, crm/dsar audit, housekeeping/inventory/reorder, pos/hardware, housekeeping/maintenance | A-30 to A-35, Issue #8 part, Issue #12 |
| 4-30 | Tape chart "(empty note) UUID" → check why note.text is empty for those rows + suppress display when empty | Issue #12 |
Sprint 5 — Missing cross-module flows (separate plan, 7-10 days)
| # | Item | Issue |
|---|
| 5-1 | POS → folio room charge end-to-end: pos/orders Charge to Room → reservation lookup → folio.postCharge → folio shows line | Issue #10 |
| 5-2 | Collect payment from checkout: Outstanding-balance error → "Settle now" button → opens folio settle dialog → returns to checkout | Issue #17 |
| 5-3 | Tax invoice for non-GST properties: e-invoice GST-regime gate → fall-through to plain Tax Invoice PDF | Issue #16 |
| 5-4 | Tape chart drag-drop wiring: move room, extend stay, assign unassigned guest — all 3 silent today | Issues #13, #14, #15 |
| 5-5 | Move Key Setup panel from Folio tab to its own tab on /reservations/[id] | Issue #5a |
| 5-6 | Wire real key encoder hardware (already has env config; need driver + UI) | Issue #5b |
| 5-7 | Mobile app testing scaffold for housekeeping (currently hmp-mobile-apps exists but no test path) | Issue #7d |
Sprint 6 — UX polish + remaining (3-4 days)
| # | Item | Issue |
|---|
| 6-1 | Activity panel text/timeline overlap | Issue #2 |
| 6-2 | Tape chart bar use clock-time positioning (12 noon → 11 AM proportional) instead of whole-day cells | Issue #11 |
| 6-3 | Adopt banquet's STATUS_TRANSITIONS declarative pattern for reservation, folio, POS order, HK task, DSAR | C-6 |
| 6-4 | Add transition history tab to all status-bearing entities | C-24 |
| 6-5 | Standardise confirm dialogs (AlertDialog for any irreversible transition) | C-27 |
Execution rhythm
Same as Sprint 1/2/3:
- Per item: edit → typecheck → commit → push
- Every 5 items: rebuild dashboard, restart local container, smoke check
- End of each block: deploy to QA, do manual smoke on the affected surface
- Tasks tracked via TaskCreate/TaskUpdate
Approval gate
This plan covers 30 Sprint 4 items. If approved, I'll execute in order — Block A → B → C → D → E. Each block ends with a commit batch + QA deploy. ETA 5-7 working days at the same pace as Sprint 1/2/3.
If you want to compress: I can skip Block C frontend-only fixes for items where the bug is a backend response issue you'd rather fix on the backend (like POS Add Menu Item isAvailable → adding to backend DTO instead of removing from form).
Otherwise, proceeding with execution starting from item 4-1.