Session Report — Operations Hub Phase 2 + Launch Cleanup
Date: 2026-05-02 (full day, single session)
Mode: Autonomous from "resume Operations Hub Phase 2" prompt
Spec: 2026-05-02-operations-hub-progressive-density-design.md
Plans:
2026-05-02-operations-hub-phase-2-tile-library.md(executed)2026-05-02-operations-hub-master-plan.md
Outcome at a glance
| Bundle | Scope | Status | Repos touched |
|---|---|---|---|
| Prereq 0a | Property interface gains type + roomCount typed fields | DONE | hmp-web-dashboard |
| Prereq 0b | tenant: { id, propertyCount } slice on app-store (derived from properties.length) | DONE | hmp-web-dashboard |
| Prereq 0c | DUTY_MGR persona deleted (no JWT source role) | DONE | hmp-web-dashboard, hmp-shared-packages |
| Prereq 0d | DashboardPrefsService.get/patch tenantId scoping (defense-in-depth) | DONE | hmp-auth-service |
| Task 1 | <TileSlot> dispatcher + tileRegistry registry pattern | DONE | hmp-web-dashboard |
| Task 2 | 21 KPI tiles (Pulse/Card/Deep variants) | DONE | hmp-web-dashboard |
| Task 3 | 24 report card tiles wrapping <ReportTable compact> | DONE | hmp-web-dashboard |
| Task 4 | <FrontDeskPulse> HOTEL today.live (4 sub-blocks) | DONE | hmp-web-dashboard |
| Task 5 | <ResortPulse> RESORT today.live (3 sub-blocks) | DONE | hmp-web-dashboard |
| Task 6 | <ServicePulse> BOUTIQUE today.live (3 stacked rows) | DONE | hmp-web-dashboard |
| Task 7 | <TodayLive> slot picker dispatches by propertyType | DONE | hmp-web-dashboard |
| Task 8 | 6 role configs populated with tile catalogs from spec §3 | DONE | hmp-web-dashboard |
| Task 9 | <KpiStrip> + <InsightsGrid> wired with applyPrefs (hide/reorder) | DONE | hmp-web-dashboard |
| Task 10 | <AttentionRailSection> + <ActionsQueueSection> (action-card moved to operations-hub/widgets) | DONE | hmp-web-dashboard |
| Task 11 | 3 new v2 reports: crm-feedback-pulse, pre-arrival-pulse, linen-consumption | DONE | hmp-analytics-service |
| Task 12 | GET /api/dashboard/portfolio resolver (heatmap + leaderboard, 5-min cache) | DONE | hmp-analytics-service, hmp-api-gateway |
| Task 13 | <PortfolioStrip> frontend (heatmap + leaderboard + watchlist chips) | DONE | hmp-web-dashboard |
| Task 14 | <TilePinMenu> kebab — Hide / Move up / Move down (persists per user) | DONE | hmp-web-dashboard |
| Regression fix | PortfolioStrip data-testid stable across loading state | DONE | hmp-web-dashboard |
| Launch cleanup | Flag default ON + /control-center/* deleted + sidebar trimmed + AttentionRail rewired to /anomalies + /control-center 308 redirects | DONE | hmp-web-dashboard |
Aggregate: 22 commits across 5 repos pushed to origin/master.
Final repo HEADs
| Repo | master HEAD | Latest commit |
|---|---|---|
hmp-shared-packages | 450cff8 | DUTY_MGR removed from EffectiveRole union |
hmp-auth-service | 5512770 | DashboardPrefsService.get tenantId scoping |
hmp-analytics-service | de019ba | Portfolio resolver (heatmap + leaderboard) |
hmp-api-gateway | 337ba28 | /dashboard/portfolio route registered |
hmp-web-dashboard | b4f627f | Merge feat/operations-hub-launch-cleanup |
What's now live
FLAG_OPERATIONS_HUB_HOME defaults ON post-2026-05-02 (opt out via
NEXT_PUBLIC_FLAG_OPERATIONS_HUB_HOME=false). When ON, the dashboard root (/)
renders <RoleHome /> — a 5-section role-pivoted home:
- PortfolioStrip (renders only when
tenantScale !== 'single') — property heatmap + anomaly leaderboard + watchlist chips for OWNER role. - KpiStrip — 6-tile horizontal row of role-specific KPIs (Pulse fidelity on single tenant, Card on small-multi, Card on large-multi).
- TodayLive — property-type-aware live block. HOTEL gets
FrontDeskPulse(arrivals timeline + queue pressure + VIP rail + room readiness). RESORT getsResortPulse(cross-dept timeline + occupancy gauge + activity rail). BOUTIQUE getsServicePulse(combined arrivals + issues + HK strip). - InsightsGrid — 4-card grid of role-specific report cards (channel mix, pickup, segment mix, etc.).
- AttentionRail + ActionsQueue sidebar — top critical anomalies
(sourced from real
/anomaliesendpoint) + assigned actions.
Per-tile kebab menu persists hide/reorder to UserDashboardPrefs. Role
switcher chip in the header lets users with multiple eligible roles toggle the
home variant; selection persists across sessions.
Legacy /control-center deleted
The user requested deletion of "older duplicate/half-baked pages" before deploy. Cleanup removed:
- 8 page files:
/control-center/{,today,revenue,labor,exceptions,actions}/page.tsx+keynova.tsx+error.tsx - 8 component files in
src/components/modules/control-center/: department-timeline, exception-card, kpi-card, kpi-drill-down, labor-cost-entry, playbook-activator, review-briefing-view, sop-execution-view (all unused after the route group went) - 7 corresponding test files
- Sidebar "Control Center" parent + 5 children
- ~5650 lines of code
308 redirects added in next.config.ts: /control-center and
/control-center/:path* → /. Old bookmarks land gracefully on the new home.
AttentionRailSection was rewired: it had been calling a non-existent
/issues endpoint with a /issues#<id> CTA that 404'd. Now it calls real
Plan 3.1 /anomalies endpoint and CTA opens /anomalies.
ActionsQueueSection's "View all → /actions" link was dropped pending Phase 3.
New backend endpoints
| Method | Route | Service | Purpose |
|---|---|---|---|
| GET | /api/dashboard/portfolio | analytics-service | Heatmap + anomaly leaderboard for the user's accessible properties (5-min cache) |
| POST | /api/reports/v2/crm-feedback-pulse/run | analytics-service | Guest sentiment 24h (warming-up until MvGuestFeedback wired) |
| POST | /api/reports/v2/pre-arrival-pulse/run | analytics-service | Pre-arrival eligibility (warming-up until eligibility join wired) |
| POST | /api/reports/v2/linen-consumption/run | analytics-service | Linen issued vs returned (warming-up until MvLinenConsumption wired) |
All 3 new reports register correctly in the catalog (/api/reports/v2/catalog)
and return totals.warmingUp: true until their data sources land in Phase 3.
Test counts
- Dashboard
operations-hubsuite: 137 tests across 22 files green - Analytics: 658 reporting tests + 19 portfolio tests green
- Auth: 14 dashboard-prefs tests green
- Total: 828 tests verified green during this session
Design choices worth noting
-
Tile registry is a mutable singleton populated by import side-effects. Each tile module ends with
tileRegistry['<id>'] = MyTileat top level. Trade-off: simpler than aregister()helper; requires consumers to importtile-registryso the registrations run. -
<ReportTable>doesn't accept ametricfilter — the plan's snippet was forward-looking. KPI tiles use<KpiCard reportId extract>for Pulse (which extracts a specific totals key) and<ReportTable compact>for Card (which renders the full table). Per-metric filtering on ReportTable is a Phase 3 capability. -
Heatmap revpar is placeholder for Phase 2 v1 —
PortfolioService.buildHeatmapreadsMvDailyKpi.revparfor the current UTC date but returnsrevparLy=nullandratio=nullalways. Phase 3 wires the LY comparison + property-name resolution. The leaderboard works correctly today. -
6 spec §3 tiles deferred to Phase 3 —
pre-arrival-pulse-card, F&B KPI tiles (avgCoverValue, fnbRevenue, serverProductivityKpi),kot-board-pulse,room-board-pulse,portfolio-anomaly-map. Documented inline as comments in the role configs that reference them;tile-registrysmoke test refuses to register placeholder strings pointing at non-existent tiles. -
Auth-service DUTY_MGR backward-compat lag — 4 inline arrays in auth-service still carry DUTY_MGR (schema comment + VALID_ROLES + DTO @IsIn). Old dashboard builds during rollout can still PATCH
lastViewedRole: 'DUTY_MGR'without 400-ing. Cleanup tracked as follow-up post-rollout.
Phase 3 follow-ups (priority-ordered)
| # | Item | Why |
|---|---|---|
| 1 | Issues page (/issues route) — anomalies + exceptions merged | Spec §5; AttentionRail CTA currently goes to /anomalies (Plan 3.1 page) — Issues page would be the canonical merge of anomalies + exceptions |
| 2 | Wire real revpar / revparLy aggregation for portfolio heatmap | Phase 2 v1 returns placeholders; OWNER home looks empty without it |
| 3 | Wire MvGuestFeedback / eligibility join / MvLinenConsumption for the 3 new v2 reports | All return warming-up today |
| 4 | /api/tenants/me endpoint | Drives tenant.propertyCount correctly (currently derived from properties.length) |
| 5 | Build 6 missing spec §3 tiles | Unlocks RM/HK/FNB role configs to add their today.live + missing KPIs |
| 6 | DashboardPrefsClient → portfolio controller | Resolves watchlistPropertyIds server-side instead of hardcoded [] |
| 7 | /actions route + canonical endpoint | ActionsQueueSection's "View all" link was dropped pending this |
| 8 | Phase 4 Cutover — remove KeyNovaDashboard legacy fallback path | After 2-week soak with flag ON |
| 9 | Phase 5 Deletion — remove any remaining control-center component imports | Final cleanup |
| 10 | Auth-service DUTY_MGR scrub (4 inline arrays) | Post-rollout window |
Files of interest
- Master plan:
hmp-docs/docs/superpowers/plans/2026-05-02-operations-hub-master-plan.md - Phase 2 plan (executed):
.../plans/2026-05-02-operations-hub-phase-2-tile-library.md - Spec:
.../specs/2026-05-02-operations-hub-progressive-density-design.md - Use case YAML:
_data/use-cases/uc-operations-hub-phase-2.yaml - Phase 1 ship report: prior session memory
project_2026-05-02-operations-hub-phase-1-shipped.md - Phase 2 ship report: prior session memory
project_2026-05-02-operations-hub-phase-2-shipped.md