# Personal Dashboard Framework PRS **Workplan:** IHUB-WP-0020 **Date:** 2026-06-16 **Status:** Product requirements for follow-on FDD and implementation planning **Research input:** `docs/research/personal-dashboard-current-state.md` ## 1. Problem Statement Authenticated inter-hub users currently land on the Hubs list after login. That list is a useful management table, but it does not answer the daily operating questions users bring to inter-hub: - What changed recently? - Which candidates or governance items need attention? - Which hubs are unhealthy or blocked? - Which learning or institutional knowledge signals should I notice today? - Where should I go next? Inter-hub already has many specialized dashboards, but they are scattered across hub-level and platform-level routes. Users must know which surface to visit and manually stitch together recent activity, open work, health, governance, and learning signals. The personal dashboard should become the authenticated landing surface that summarizes the most relevant existing signals and links users to the source dashboards for detail. It should be persistent, configurable, server-rendered, and governed by the same IHF widget, annotation, and interaction-event model as the rest of the application. ## 2. Users and Personas ### Hub Operator Owns or watches one or more hubs. Needs quick visibility into recent events, open requirement candidates, hub health, active bottlenecks, and regressions. Primary questions: - Which hubs need attention now? - What happened since the last session? - Which candidates are still open? - Are any bottlenecks or health drops visible? ### Governance Reviewer Triages candidates, reviews decisions, checks policy coverage, and follows traceability from observations to implementation outcomes. Primary questions: - Which candidates are waiting for triage? - Which decisions or requirements changed recently? - Which panels need annotation or review? - Are governance signals visible without visiting every hub? ### AI Orchestrator Monitors agent proposals, review outcomes, learning signals, and institutional knowledge that may affect future AI-assisted work. Primary questions: - Which agent proposals or reviews need attention? - What learning insights were generated recently? - Which knowledge entries should inform the next work session? - Are there patterns of repeated friction or successful reuse? ### Platform Admin Watches the inter-hub platform itself: API consumers, hub registry health, manifests, policy overlays, marketplace activity, and cross-hub propagation. Primary questions: - Are API consumers active and healthy? - Are hub manifests and registry views coherent? - Are cross-hub governance or propagation signals emerging? - Which operational panels should be promoted into a shared view later? ## 3. Product Goals - Replace the authenticated post-login Hubs table as the daily landing surface. - Provide a compact, configurable overview of existing inter-hub signals. - Preserve the existing specialized dashboards as source-of-truth detail views. - Make every saved panel a governed IHF interaction artifact. - Keep the first implementation simple enough to deliver without a broad dashboard refactor. ## 4. Non-Goals - Do not build a drag-and-drop report builder. - Do not add external datasource connectors. - Do not introduce a client-side data fetching framework. - Do not refactor all existing dashboards into reusable panel modules in the first slice. - Do not add a full role/permission system. - Do not make shared/team dashboards part of the first implementation. - Do not change public root, tutorial, capabilities, or extension-guide routes. ## 5. Core Requirements ### Must - Provide an authenticated personal dashboard route. - Redirect successful login to the personal dashboard instead of `HubsAction`. - Preserve public root behavior for unauthenticated and documentation users. - Persist at least one dashboard per user. - Seed a default dashboard on first visit. - Persist panel instances, layout position, and panel config. - Render all first-slice panels server-side through IHP/HSX. - Use a server-side panel catalogue with stable panel keys. - Bound every panel query by limit and, where relevant, hub/status/time filters. - Decode JSONB panel config into explicit Haskell config types before querying. - Create or reference stable `Widget` records for saved panel instances. - Wrap rendered panels with `widgetEnvelope`. - Preserve annotation and interaction-event identity across sessions. - Link each panel to its existing source dashboard or source entity list. - Provide a simple edit mode for adding, removing, and reordering panels through normal IHP forms. ### Should - Support hub filters for panels backed by hub-owned data. - Support simple time-range filters where the underlying table has timestamps. - Support limit/display-mode settings for panels with list content. - Refresh recent activity, triage, health, and learning panels using the existing `autoRefresh` style. - Keep forms keyboard accessible and understandable without custom JavaScript. - Render a neutral empty state when a panel has no data. - Provide safe fallback behavior for invalid panel config. - Keep first paint sub-second for a seeded dashboard with default panels. - Use existing Tailwind/card/table visual conventions. ### Could - Add saved watched-hub sets. - Add multiple named dashboards per user. - Add dashboard templates. - Add shared/team dashboards. - Add more panel types after the first framework slice is proven. - Add finer-grained panel refresh routes later. - Add user-selected default landing dashboard later. ### Won't for First Implementation - Drag-and-drop layout editing. - Mobile-native layout editor. - Client-side data fetching. - External dashboard or BI embedding. - External datasource credentials. - Role-based access control beyond existing authenticated controller guards. - Complex visual charting library integration. ## 6. First-Slice Panel Catalogue The first implementation should prove the framework with a small panel set. | Panel key | Label | Purpose | Default behavior | |---|---|---|---| | `watched-hubs` | Watched Hubs | Show hub list plus latest health hint | All hubs, limit 12 | | `recent-interactions` | Recent Activity | Show latest interaction events with hub/widget context | Last 24h, limit 25 | | `triage-queue` | Triage Queue | Show open requirement candidates | Open status, oldest first, limit 10 | | `recent-decisions` | Recent Decisions | Show recent governance decisions | Last 30 days, newest first, limit 10 | | `hub-health` | Hub Health | Show latest health snapshot and active blockers | Latest per hub, limit 12 | | `learning-digest` | Learning Digest | Show latest insights and knowledge highlights | Latest insights and entries, limit 5 each | Deferred panel catalogue candidates: - `agent-proposals` - `api-usage` - `marketplace-trending` - `my-annotations` - `policy-compliance` - `adapter-compatibility` - `cross-hub-propagations` ## 7. Functional Requirements ### Dashboard Route - Add a `PersonalDashboardsController` or similarly named controller. - Add a show action for the current user's default dashboard. - Add edit/update/add/remove actions for panel management. - Register routes in `Web.Routes` and `Web.FrontController`. - Add a sidebar link labelled `Dashboard`. ### Dashboard Persistence - Store dashboard ownership by `users.id`. - Support at least one default dashboard per user. - Store panel order and grid layout. - Store panel config in JSONB. - Store a linked panel widget id for governance. - Keep panel deletion non-destructive with respect to historical events and annotations. ### Default Seeding - On first visit, create a default dashboard for the authenticated user. - Seed the first-slice panels with safe default config. - Do not require a `users.role` column. - Best-effort hub relevance may use active `stewardship_roles.assigned_to` matching user email or name, but the neutral default must work without it. ### Panel Rendering - Dispatch panels by stable catalogue key. - Decode and validate config before querying. - Use bounded queries. - Render empty states and config warnings without crashing the whole dashboard. - Wrap every rendered panel in `widgetEnvelope`. - Link to source dashboards for deeper work. ### Edit Mode - List current panels in layout order. - Allow adding a panel from active panel types. - Allow removing a panel from the dashboard. - Allow editing column, row, span, limit, hub filter, time range, and display mode where supported. - Validate layout spans and config values server-side. - Keep forms usable without custom JavaScript. ### Governance and Event Capture - Saved panels must use stable `Widget` rows. - Panel widgets should use the existing `panel` widget type. - Panel widget `view_context` must be non-empty. - Panel annotations must attach to the panel widget id. - Panel interaction capture should use existing event types where possible. - Adding/removing/reconfiguring panels should not mutate historical interaction events. ## 8. Non-Functional Requirements ### Performance - Default dashboard first paint target: under 1 second in local development with seeded fixtures. - Each panel query should have a default row limit. - Any aggregate query decoded as `Int` must cast `COUNT(*)` to integer or decode as `Int64`. - Avoid N+1 patterns where a simple join or batched fetch is practical. - Use existing indexes where available; document new index needs in the FDD. ### Reliability - Invalid panel config should not break the whole dashboard. - Missing source data should render an empty state. - Missing linked widget should be repaired or reported by the controller before rendering. - Dashboard seeding should be idempotent. - Login redirect should fall back gracefully if dashboard seeding fails. ### Security and Privacy - Dashboard routes require authenticated users. - Users can view and edit only their own personal dashboard in the first slice. - No secrets, API keys, or token values may be shown in dashboard panels. - API usage panels must show only non-secret consumer metadata. - Panel config must not become an arbitrary SQL or route execution surface. ### Accessibility and Usability - Use semantic headings for panels. - Use tables/lists for scan-heavy operational data. - Use form labels for all edit inputs. - Keep navigation links clear and predictable. - Do not rely on hover-only controls for essential edits. ### Maintainability - Put renderer dispatch and config decoding in a focused helper/module. - Keep panel renderer functions small and testable. - Avoid moving existing dashboard code unless required. - Prefer additive schema migrations. - Keep first implementation tasks small enough for separate Codex sessions. ## 9. Acceptance Criteria The product design is acceptable when the FDD can specify: - Exact schema tables and fields. - Exact controller/action names. - Exact default panel seed set. - Exact panel config types and defaults. - Exact first-slice panel query shapes. - Exact governance identity lifecycle for panel widgets. - Exact smoke tests for login, dashboard seeding, editing, annotation, and source dashboard link-outs. The implementation will be acceptable when: - A new authenticated user lands on a seeded personal dashboard after login. - The seeded dashboard renders all first-slice panels. - The user can add, remove, and adjust panels through server-rendered forms. - Panel layout persists across sessions. - Each panel is wrapped in `widgetEnvelope`. - Annotating a panel opens the existing widget annotation flow. - Existing Hubs and specialized dashboard routes still load. - Focused tests or smoke checks cover seeding, config validation, route access, and bounded query behavior. ## 10. Risks and Mitigations | Risk | Mitigation | |---|---| | Scope grows into report builder | Limit first slice to six panel types and server-rendered forms | | Existing dashboards are hard-coded | Extract only needed query/render fragments into new panel renderers | | Panel config becomes unsafe JSON | Decode into Haskell ADTs and validate before querying | | Role-aware defaults require missing schema | Use neutral default first; only best-effort stewardship hints | | AutoRefresh refreshes too much | Bound all queries; defer per-panel refresh unless needed | | Panel annotations lack stable identity | Require `dashboard_panels.widget_id` and `widgetEnvelope` | | COUNT decode errors recur | Cast aggregate counts or decode as `Int64` in implementation | ## 11. Open Questions for FDD 1. Should table names use `personal_dashboards`/`dashboard_panels`, or a more explicit `user_dashboards` prefix? 2. Should removed panels archive their linked widgets or mark them deprecated? 3. Should panel widgets be owned by the framework hub, or by the source hub when a panel is hub-specific? 4. Should the first implementation allow multiple dashboards per user, or only one default dashboard with schema ready for multiples? 5. Should `autoRefresh` wrap the whole dashboard action initially, or should live panel fragments get their own actions? 6. Should watched hubs be a separate table in the first slice, or represented as dashboard panel config only? ## 12. Recommendation Proceed to FDD with a small, governed, server-rendered personal dashboard: - One default dashboard per user, schema ready for multiples. - Six first-slice panel types. - `dashboard_panels.widget_id` as the governance anchor. - Existing `panel` widget type for saved panel widgets. - Whole-page `autoRefresh` initially, with bounded queries. - Simple edit forms and no custom client runtime.