# Unified ByteLyst Portal — Roadmap & Task List > **Date:** 2026-03-21 · **Companion to:** [`UNIFIED_BYTELYST_PORTAL_PRD.md`](UNIFIED_BYTELYST_PORTAL_PRD.md) > **Estimated total:** 8 phases · ~129 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 | Add `lib/errors.ts` -- re-export from `@bytelyst/errors` | backend | 10m | Standard pattern across all product backends | | 0.12 | Add `lib/datastore.ts` -- `@bytelyst/datastore` provider (Cosmos or memory via DB_PROVIDER) | backend | 30m | Required for widget layout persistence | | 0.13 | Add `lib/cosmos-init.ts` -- register `portal_widget_layouts` container on startup | backend | 30m | Partition: `/userId` | | 0.14 | Add `vitest.config.ts` + `test-helpers.ts` -- Vitest config + `buildTestApp()` helper | backend | 30m | Standard test scaffold | | 0.15 | Scaffold web with Next.js 16 + `@bytelyst/dashboard-shell` (port 3010) | web | 1h | App Router, TailwindCSS v4, `--portal-*` CSS props | | 0.16 | Add `web/src/lib/product-config.ts` -- product identity + API URLs | web | 15m | | | 0.17 | Add `web/src/lib/auth.ts` -- `@bytelyst/react-auth` provider | web | 30m | Cookie-based auth | | 0.18 | Add `web/src/lib/api.ts` -- typed fetch client for BFF | web | 30m | | | 0.19 | Add `web/src/app/layout.tsx` + `providers.tsx` -- shell + auth + telemetry | web | 30m | | | 0.20 | Create `AGENTS.md` with project conventions | docs | 30m | | | 0.21 | Create `.github/workflows/ci.yml` -- typecheck + test for backend + web | infra | 30m | | | 0.22 | Verify: `cd backend && npm test && npm run typecheck` passes | test | 15m | | | 0.23 | 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. (23 tasks) --- ## 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 | BFF: `GET /api/portal/quick-actions` -- available quick actions per product from product registrations | backend | 1h | Static config in product.json + optional dynamic from backends | | 1.14 | 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` (in learning_voice_ai_agent repo) | 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 | 23 | Repo builds, shell renders | | **1** | Product Launcher and Auth | 3 days | 14 | 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** | **129** | | --- ## 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._