- PRD: product identity, architecture (BFF + web), data model, 10 pages, 21 widgets, cross-product feed/search, security, resilience, telemetry - Roadmap: 8 phases (scaffold, auth, feed, widgets, search, billing, security, polish), dependency graph, ~21 days estimated - Builds on CROSS_PRODUCT_USER_DASHBOARD.md design + DASHBOARD_UI_GAP_ANALYSIS.md findings
30 KiB
Unified ByteLyst Portal — Roadmap & Task List
Date: 2026-03-21 · Companion to:
UNIFIED_BYTELYST_PORTAL_PRD.mdEstimated total: 8 phases · ~124 tasks · 6-8 weeks
Phase 0: Repo Scaffold & Foundations (2 days)
Goal: Repo exists, builds, deploys to dev. BFF starts and returns health check. Web renders shell.
| # | Task | Type | Est | Notes |
|---|---|---|---|---|
| 0.1 | Create learning_ai_portal repo with standard structure |
infra | 1h | backend/, web/, shared/, docs/, AGENTS.md |
| 0.2 | Create shared/product.json (id: portal, port: 4020/3010) |
config | 15m | Follow existing product.json pattern |
| 0.3 | Create products/portal/product.json in common-plat |
config | 15m | Register portal in product registry |
| 0.4 | Scaffold BFF with @bytelyst/fastify-core (port 4020) |
backend | 1h | createServiceApp() + health endpoint |
| 0.5 | Add lib/config.ts -- Zod-validated env (JWT_SECRET, PLATFORM_URL, product backend URLs) |
backend | 30m | |
| 0.6 | Add lib/product-config.ts -- load from shared/product.json |
backend | 15m | |
| 0.7 | Add lib/auth.ts -- JWT extraction + portal scope validation |
backend | 30m | |
| 0.8 | Add lib/request-context.ts -- getUserId(req), getUserMemberships(req) |
backend | 30m | |
| 0.9 | Add lib/product-registry.ts -- load all product.json files, build registry |
backend | 1h | Read from ../../products/*/product.json or env-injected |
| 0.10 | Add lib/product-client.ts -- service-to-service HTTP client for product backends |
backend | 1h | Timeout, circuit breaker, x-user-id header |
| 0.11 | Scaffold web with Next.js 16 + @bytelyst/dashboard-shell (port 3010) |
web | 1h | App Router, TailwindCSS v4, --portal-* CSS props |
| 0.12 | Add web/src/lib/product-config.ts -- product identity + API URLs |
web | 15m | |
| 0.13 | Add web/src/lib/auth.ts -- @bytelyst/react-auth provider |
web | 30m | Cookie-based auth |
| 0.14 | Add web/src/lib/api.ts -- typed fetch client for BFF |
web | 30m | |
| 0.15 | Add web/src/app/layout.tsx + providers.tsx -- shell + auth + telemetry |
web | 30m | |
| 0.16 | Create AGENTS.md with project conventions |
docs | 30m | |
| 0.17 | Create .github/workflows/ci.yml -- typecheck + test for backend + web |
infra | 30m | |
| 0.18 | Verify: cd backend && npm test && npm run typecheck passes |
test | 15m | |
| 0.19 | Verify: cd web && npm run typecheck && npm run build passes |
test | 15m |
Phase 0 deliverable: Repo builds, BFF returns GET /health, web renders empty shell with sidebar.
Phase 1: Product Launcher & Auth (3 days)
Goal: User can log in, see all their products, and launch any product dashboard.
| # | Task | Type | Est | Notes |
|---|---|---|---|---|
| 1.1 | BFF: GET /api/portal/products -- fetch user memberships from platform-service /auth/me, enrich with product.json metadata |
backend | 2h | Returns { products: [{ id, name, icon, plan, role, dashboardUrl, lastActiveAt }] } |
| 1.2 | BFF: POST /api/portal/products/:id/switch -- re-issue JWT with new active productId |
backend | 1h | |
| 1.3 | BFF: service-to-service auth setup -- API key registration for portal-to-product-backend calls | backend | 2h | Register portal as a service consumer in platform-service |
| 1.4 | Web: /login page -- email/password + OAuth buttons via @bytelyst/react-auth |
web | 2h | Redirect to / on success |
| 1.5 | Web: (app)/layout.tsx -- authenticated route group with DashboardShell |
web | 1h | Sidebar nav: Home, Products, Activity, Search, Billing, Settings, Security |
| 1.6 | Web: Sidebar.tsx -- portal sidebar with product icons + nav sections |
web | 1h | |
| 1.7 | Web: /products page -- product launcher grid |
web | 3h | Card per product: icon, name, plan badge, last active, "Open" button |
| 1.8 | Web: ProductCard.tsx component -- product card with theme colors from product.json |
web | 1h | |
| 1.9 | Web: / redirect to /products (temporary, until dashboard is built in Phase 3) |
web | 15m | |
| 1.10 | Web: product launch handler -- opens product dashboard in new tab with session context | web | 1h | Deep link: https://{domain}/?portal=true |
| 1.11 | Web: show "Get Started" CTA for products user hasn't signed up for | web | 1h | Links to product landing page |
| 1.12 | Test: BFF products endpoint returns correct memberships | test | 1h | Vitest |
| 1.13 | Test: Login flow to product grid to launch works E2E | test | 1h | Playwright |
Phase 1 deliverable: User logs in, sees grid of their products with status, can open any product in a new tab.
Phase 2: Activity Feed & Notifications (3 days)
Goal: User sees a chronological feed of recent actions across all products.
| # | Task | Type | Est | Notes |
|---|---|---|---|---|
| 2.1 | BFF: lib/feed-aggregator.ts -- parallel fan-out to product backends with timeout + circuit breaker |
backend | 3h | 3s timeout per product, fail-open |
| 2.2 | BFF: Define FeedEvent schema + product-specific event mappers |
backend | 2h | One mapper per product that normalizes response into FeedEvent |
| 2.3 | BFF: GET /api/portal/feed -- aggregated feed with pagination (?cursor=&limit=50) |
backend | 2h | Merge-sort by createdAt desc |
| 2.4 | BFF: In-memory cache (60s TTL per user) for feed data | backend | 1h | |
| 2.5 | BFF: GET /api/portal/notifications -- aggregate from platform-service notifications |
backend | 1h | |
| 2.6 | Web: /activity page -- full-page activity timeline |
web | 3h | Filter by product, date range, event type |
| 2.7 | Web: FeedCard.tsx -- event card with product icon, title, timestamp, deep link |
web | 1h | |
| 2.8 | Web: FeedFilters.tsx -- product multi-select, date range picker |
web | 1h | |
| 2.9 | Web: Activity feed widget on / home page (compact view, last 10 items) |
web | 1h | |
| 2.10 | Web: Notification bell in top bar -- badge count + dropdown | web | 2h | |
| 2.11 | Product backends: Verify each backend has a "recent items" list endpoint | backend | 2h | Add missing endpoints where needed |
| 2.12 | Test: Feed aggregation with 3 products up, 2 down | test | 1h | Circuit breaker test |
| 2.13 | Test: Feed pagination, sorting, filtering | test | 1h |
Phase 2 deliverable: /activity page shows chronological feed from all products. Home page shows compact feed.
Phase 3: Widget Dashboard (4 days)
Goal: Home page is a customizable widget grid. Users can add/remove/resize widgets.
| # | Task | Type | Est | Notes |
|---|---|---|---|---|
| 3.1 | BFF: modules/widgets/types.ts -- WidgetDefinition, WidgetLayout schemas |
backend | 1h | |
| 3.2 | BFF: modules/widgets/repository.ts -- CRUD for user widget layouts in Cosmos |
backend | 2h | Container: portal_widget_layouts, partition: /userId |
| 3.3 | BFF: modules/widgets/routes.ts -- GET/PUT layout, GET widget definitions |
backend | 2h | |
| 3.4 | BFF: GET /api/portal/widgets/:widgetId/data -- fetch widget data from product backend |
backend | 2h | Route to correct product backend based on widgetId prefix |
| 3.5 | BFF: Register 20+ widget definitions (from PRD section 5.4) | backend | 2h | Static definitions loaded at startup |
| 3.6 | BFF: Cosmos container registration for portal_widget_layouts |
backend | 30m | |
| 3.7 | Web: / home page with widget grid layout (CSS Grid) |
web | 3h | Fixed grid for v1, drag-and-drop in Phase 8 |
| 3.8 | Web: WidgetShell.tsx -- generic widget wrapper (header, loading, error, resize) |
web | 2h | |
| 3.9 | Web: WidgetPicker.tsx -- modal to add widgets from catalog |
web | 2h | Grouped by product |
| 3.10 | Web: Implement 5 priority widgets: ChronoMind next-timer, NomGap fast-progress, FlowMonk today-schedule, NoteLett recent-notes, Portal billing-summary | web | 4h | ~45m each |
| 3.11 | Web: Implement 5 more widgets: LysnrAI recent-transcripts, MindLyst recent-captures, JarvisJr coaching-streak, ActionTrail recent-actions, PeakPulse weekly-stats | web | 4h | |
| 3.12 | Web: Implement remaining 11 widgets | web | 5h | |
| 3.13 | Web: useWidgetData(widgetId) hook -- fetch + cache + loading/error states |
web | 1h | |
| 3.14 | Web: Default widget layout for new users (6 widgets: feed + 5 most-active products) | web | 1h | |
| 3.15 | Web: Save layout changes to BFF via PUT /api/portal/widgets/layout | web | 1h | Debounced save |
| 3.16 | Test: Widget CRUD, layout persistence | test | 1h | |
| 3.17 | Test: Widget data fetch with product backend down (graceful degradation) | test | 1h |
Phase 3 deliverable: Home page has customizable widget grid. Each widget fetches live data.
Phase 4: Cross-Product Search (2 days)
Goal: Cmd+K opens a global search bar that searches across all products.
| # | Task | Type | Est | Notes |
|---|---|---|---|---|
| 4.1 | BFF: lib/search-aggregator.ts -- parallel search fan-out to product backends |
backend | 2h | Same circuit-breaker pattern as feed |
| 4.2 | BFF: GET /api/portal/search?q=...&products=... -- cross-product search |
backend | 2h | Returns results grouped by product |
| 4.3 | BFF: Product search endpoint mapping -- which endpoint to call per product | backend | 1h | e.g., NoteLett: GET /api/notes?search=, LocalMemGPT: GET /api/search?q= |
| 4.4 | Web: /search page -- full search results grouped by product |
web | 3h | Product sections, result cards with deep links |
| 4.5 | Web: SearchBar.tsx -- Cmd+K global search overlay |
web | 2h | Debounced input, recent searches, keyboard nav |
| 4.6 | Web: SearchResultCard.tsx -- generic result card with product badge |
web | 1h | |
| 4.7 | Web: Add use-keyboard-shortcuts.ts -- Cmd+K to open search |
web | 30m | |
| 4.8 | Product backends: Verify search/filter endpoints exist for each product | backend | 2h | Add missing search endpoints |
| 4.9 | Test: Search across 3 products, results merge correctly | test | 1h | |
| 4.10 | Test: Search with one product down, others still return | test | 30m |
Phase 4 deliverable: Cmd+K opens cross-product search. Results grouped by product with deep links.
Phase 5: Billing & Settings (2 days)
Goal: User manages billing and global preferences from the portal.
| # | Task | Type | Est | Notes |
|---|---|---|---|---|
| 5.1 | BFF: GET /api/portal/billing/summary -- aggregate from platform-service subscriptions + stripe |
backend | 2h | Combined monthly cost, per-product breakdown |
| 5.2 | BFF: GET /api/portal/billing/invoices -- all invoices across products |
backend | 1h | |
| 5.3 | BFF: GET /api/portal/preferences -- user global preferences |
backend | 1h | From UserDoc.globalPreferences |
| 5.4 | BFF: PATCH /api/portal/preferences -- update preferences |
backend | 1h | Validate with Zod, save to platform-service |
| 5.5 | Platform-service: Add globalPreferences field to UserDoc schema |
backend | 1h | Optional, backward-compatible |
| 5.6 | Platform-service: Add PATCH /auth/preferences endpoint |
backend | 1h | |
| 5.7 | Web: /billing page -- subscription cards per product, combined total, invoice list |
web | 3h | |
| 5.8 | Web: /settings page -- theme toggle, timezone picker, locale, notification prefs |
web | 3h | |
| 5.9 | Web: Theme toggle (dark/light/system) applies to portal immediately | web | 1h | |
| 5.10 | Web: Notification preferences -- email/push/in-app toggles, digest frequency | web | 1h | |
| 5.11 | Test: Billing aggregation with mixed plan types | test | 1h | |
| 5.12 | Test: Preferences save and reload correctly | test | 1h |
Phase 5 deliverable: /billing shows combined costs. /settings manages cross-product preferences.
Phase 6: Security & Profile (2 days)
Goal: Full SmartAuth security management from the portal.
| # | Task | Type | Est | Notes |
|---|---|---|---|---|
| 6.1 | BFF: Proxy security endpoints to platform-service (MFA, passkeys, devices, login-events) | backend | 2h | Same catch-all proxy pattern |
| 6.2 | Web: /security page -- MFA status, TOTP setup/disable, recovery codes |
web | 3h | Reuse patterns from admin-web security page |
| 6.3 | Web: /security -- passkey management (register, list, remove) |
web | 2h | WebAuthn browser API integration |
| 6.4 | Web: /security -- device list with trust levels, session revocation |
web | 2h | |
| 6.5 | Web: /security -- login history timeline |
web | 1h | |
| 6.6 | Web: /profile page -- name, email, avatar, connected OAuth accounts |
web | 2h | |
| 6.7 | Web: Connected accounts section -- link/unlink Google, Microsoft, Apple | web | 2h | |
| 6.8 | Web: /support page -- create/track support tickets via platform-service support-cases |
web | 2h | |
| 6.9 | Test: MFA setup flow E2E | test | 1h | |
| 6.10 | Test: Device revocation works from portal | test | 30m |
Phase 6 deliverable: Full security management. Profile with OAuth link/unlink. Support ticket creation.
Phase 7: Telemetry, Polish & Launch (3 days)
Goal: Production-ready portal with telemetry, error handling, and polished UI.
| # | Task | Type | Est | Notes |
|---|---|---|---|---|
| 7.1 | BFF: Telemetry event buffer -- portal-specific events (product_launched, search_executed, etc.) | backend | 1h | |
| 7.2 | BFF: Feature flags integration -- @bytelyst/feature-flag-client |
backend | 30m | |
| 7.3 | BFF: Kill switch integration -- @bytelyst/kill-switch-client |
backend | 30m | |
| 7.4 | BFF: Rate limiting on search and feed endpoints | backend | 1h | |
| 7.5 | BFF: Request logging with structured context | backend | 30m | |
| 7.6 | Web: Telemetry client -- track portal events via @bytelyst/telemetry-client |
web | 1h | |
| 7.7 | Web: Error boundaries -- per-widget and per-page | web | 1h | |
| 7.8 | Web: Loading skeletons for all pages | web | 2h | |
| 7.9 | Web: Empty states for zero-product users (onboarding CTA) | web | 1h | |
| 7.10 | Web: Responsive design -- mobile-friendly sidebar collapse | web | 2h | |
| 7.11 | Web: PWA manifest + service worker for offline shell | web | 1h | |
| 7.12 | Web: --portal-* design tokens from @bytelyst/design-tokens |
web | 1h | |
| 7.13 | Web: Keyboard shortcuts -- Cmd+K (search), Cmd+1-9 (product launch) | web | 1h | |
| 7.14 | Docs: Update AGENTS.md with final architecture |
docs | 1h | |
| 7.15 | Docs: Write README.md with setup instructions |
docs | 30m | |
| 7.16 | Docker: Dockerfile for BFF + web |
infra | 1h | |
| 7.17 | Docker: Add to docker-compose.yml in common-plat |
infra | 30m | |
| 7.18 | CI: Playwright E2E test suite (10 core flows) | test | 3h | |
| 7.19 | CI: Add portal to run-local-all-services.sh |
infra | 15m | |
| 7.20 | Final: Typecheck + test + build green across BFF + web | test | 1h |
Phase 7 deliverable: Production-ready portal. All tests pass. Docker-deployable. Telemetry wired.
Phase 8: Post-Launch Enhancements (Ongoing)
Goal: Iterative improvements based on usage data.
| # | Task | Type | Est | Notes |
|---|---|---|---|---|
| 8.1 | Drag-and-drop widget reordering (react-dnd or dnd-kit) | web | 3h | |
| 8.2 | Embedded product views (iframe) with postMessage auth | web | 4h | v2 of product launcher |
| 8.3 | SSE-based real-time feed updates | backend | 3h | @bytelyst/fastify-sse |
| 8.4 | Cross-product quick actions (e.g., "Start Timer" from portal) | web+bff | 4h | Product backends expose action endpoints |
| 8.5 | Mobile-responsive PWA optimization | web | 2h | |
| 8.6 | Admin analytics -- portal engagement metrics in admin-web | web | 3h | Cross-product adoption charts |
| 8.7 | Smart widget suggestions based on user activity patterns | backend | 2h | "You use ChronoMind daily -- add the timer widget?" |
| 8.8 | Unified notification center with mark-read, filters | web | 3h | |
| 8.9 | Product health badges -- show backend status in product cards | bff | 1h | Poll health endpoints |
| 8.10 | Custom widget themes -- user sets accent color per widget | web | 1h |
Summary
| Phase | Name | Duration | Tasks | Key Deliverable |
|---|---|---|---|---|
| 0 | Repo Scaffold and Foundations | 2 days | 19 | Repo builds, shell renders |
| 1 | Product Launcher and Auth | 3 days | 13 | Login, product grid, launch |
| 2 | Activity Feed and Notifications | 3 days | 13 | Cross-product activity timeline |
| 3 | Widget Dashboard | 4 days | 17 | Customizable widget grid |
| 4 | Cross-Product Search | 2 days | 10 | Cmd+K global search |
| 5 | Billing and Settings | 2 days | 12 | Combined billing + global prefs |
| 6 | Security and Profile | 2 days | 10 | Full SmartAuth security from portal |
| 7 | Polish and Launch | 3 days | 20 | Production-ready, Docker, E2E |
| 8 | Post-Launch | Ongoing | 10 | Drag-drop, SSE, quick actions |
| Total | ~21 days | 124 |
Dependency Graph
Phase 0 (scaffold)
|
v
Phase 1 (auth + products)
|
+---> Phase 2 (feed) ---> Phase 3 (widgets)
|
+---> Phase 4 (search)
|
+---> Phase 5 (billing + settings)
|
+---> Phase 6 (security + profile)
|
v
Phase 7 (polish + launch) <-- all above complete
|
v
Phase 8 (post-launch enhancements)
Critical path: Phase 0 -> Phase 1 -> Phase 2 -> Phase 3 -> Phase 7
Phases 4, 5, 6 can be parallelized after Phase 1.
This roadmap should be reviewed after each phase. Estimates are in ideal developer-hours.