# Unified ByteLyst Portal — Product Requirements Document

> **Date:** 2026-03-21 · **Status:** Draft · **Author:** AI-assisted  
> **Depends on:** SmartAuth OneAuth (Phase 5), `@bytelyst/dashboard-shell`, platform-service  
> **See also:** [`CROSS_PRODUCT_USER_DASHBOARD.md`](design/CROSS_PRODUCT_USER_DASHBOARD.md), [`DASHBOARD_UI_GAP_ANALYSIS.md`](DASHBOARD_UI_GAP_ANALYSIS.md)

---

## Review Errata (2026-03-21 self-audit)

The following bugs/inaccuracies were found during systematic post-generation review and corrected in-place:

|  #  | Bug                                                                                                                                                                                            | Severity | Fix Applied                                                                                             |
| :-: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | ------------------------------------------------------------------------------------------------------- |
| F1  | **Product count wrong:** Said "11 products" but ByteLyst Auth has no backend -- only 10 products have backends the portal can query                                                            |   HIGH   | Changed to "10 products with backends" + noted Auth as companion app                                    |
| F2  | **ByteLyst Auth listed as product with backend** -- it's a companion app consuming platform-service, no data to aggregate                                                                      |   HIGH   | Removed from product count, clarified in problem statement                                              |
| F3  | **product.json count wrong:** Said "7 exist, 4 need creation" but only 3 are missing (FlowMonk, ActionTrail, LocalMemGPT)                                                                      |  MEDIUM  | Corrected to "7 exist, 3 need creation" with specific names                                             |
| F4  | **Architecture diagram spacing:** "ChronoMind4011" and "ActionTrail4018" missing spaces                                                                                                        |   LOW    | Fixed spacing                                                                                           |
| F5  | **GlobalPreferences contained portalLayout:** Widget layouts were inside GlobalPreferences AND in a separate Cosmos container -- contradictory                                                 |  MEDIUM  | Removed `portalLayout` from GlobalPreferences; layouts stored in `portal_widget_layouts` container only |
| F6  | **Widget endpoint mismatch:** PRD API table said `/widgets/:widgetId`, roadmap said `/widgets/:widgetId/data`                                                                                  |  MEDIUM  | Unified to `/widgets/:widgetId/data`                                                                    |
| F7  | **Missing widget layout endpoints in API table:** GET/PUT for layout management were in roadmap but not documented in PRD                                                                      |  MEDIUM  | Added `GET/PUT /api/portal/widgets/layout` + `GET /api/portal/widgets/definitions` to API table         |
| F8  | **Feed event source endpoints unverified:** Several assumed endpoints were wrong (ChronoMind `/timers/recent`, JarvisJr `/jarvis-sessions`, NomGap `/fasting-sessions`, PeakPulse `/sessions`) |   HIGH   | Verified all 10 endpoints against actual backend route files; corrected paths                           |
| F9  | **Missing `@bytelyst/datastore` in BFF tech stack** -- required for Cosmos container access                                                                                                    |  MEDIUM  | Added to BFF tech stack row                                                                             |
| F10 | **No Cosmos containers section** -- widget layouts need explicit container documentation                                                                                                       |  MEDIUM  | Added section 4.6 with `portal_widget_layouts` container                                                |
| F11 | **Dependency table: global prefs marked "Phase 2 blocker"** -- actually used in Phase 5 (Settings)                                                                                             |   LOW    | Changed to "Phase 5 blocker"                                                                            |
| F12 | **Search example used emojis** -- violates "no emojis unless explicitly asked" rule                                                                                                            |   LOW    | Removed emojis from search example                                                                      |
| F13 | **Missing quick-actions in roadmap** -- PRD listed endpoint but no roadmap task built it                                                                                                       |   LOW    | Added task 1.13 to roadmap Phase 1                                                                      |
| F14 | **Roadmap missing standard BFF scaffold files** -- `lib/errors.ts`, `lib/datastore.ts`, `lib/cosmos-init.ts`, `vitest.config.ts` not listed                                                    |  MEDIUM  | Added 4 tasks (0.11-0.14) to Phase 0                                                                    |
| F15 | **Roadmap task 7.19 wrong script location** -- `run-local-all-services.sh` is in `learning_voice_ai_agent/`, not common-plat                                                                   |   LOW    | Corrected with "(in learning_voice_ai_agent repo)" note                                                 |

---

## 1. Product Identity

| Key            | Value                                                          |
| -------------- | -------------------------------------------------------------- |
| **Product**    | ByteLyst Portal                                                |
| **Product ID** | `portal`                                                       |
| **Domain**     | portal.bytelyst.com                                            |
| **Repo**       | `learning_ai_portal` (new)                                     |
| **Ecosystem**  | ByteLyst (consumes platform-service + all 10 product backends) |
| **Port (web)** | 3010                                                           |
| **Port (BFF)** | 4020                                                           |

---

## 2. Problem Statement

The ByteLyst ecosystem has grown to **10 products with backends** (LysnrAI, MindLyst, ChronoMind, JarvisJr, NomGap, PeakPulse, FlowMonk, NoteLett, ActionTrail, LocalMemGPT) plus ByteLyst Auth (a companion app with no separate backend). Each product has its own web dashboard, login, and settings. Users who use multiple products face:

1. **Separate logins** — even though OneAuth shares identity, users still open different URLs per product
2. **No unified view** — no way to see "what's happening across all my ByteLyst apps"
3. **Fragmented settings** — notification preferences, theme, timezone set per-product
4. **No cross-product search** — can't search across notes, timers, sessions, tasks from one place
5. **No unified billing** — subscriptions managed per-product with no combined view

Meanwhile, the **admin dashboard** has grown to 43+ backend modules with 511+ endpoints, but the gap analysis shows **55 features are hidden or underexposed** in the UI. The admin console is becoming unmanageable — it needs to evolve alongside the user-facing portal.

---

## 3. Goals

### 3.1 User-Facing Portal Goals

| #   | Goal                                                                              | Success Metric                                                     |
| --- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| G1  | **Single login, all products** — one URL, one session, access everything          | 100% of 10 products accessible from portal                         |
| G2  | **Cross-product activity feed** — timeline of recent actions across all products  | Feed renders within 2s for users with 5+ products                  |
| G3  | **Unified search** — search notes, timers, tasks, sessions, memories from one bar | Cross-product search returns results in <1s                        |
| G4  | **Product launcher** — app-switcher grid (like Google's waffle menu)              | All 10 products launchable with deep links                         |
| G5  | **Combined billing** — single view of all subscriptions, invoices, usage          | Zero billing queries to support about "which product charges what" |
| G6  | **Global settings** — theme, timezone, notification prefs apply cross-product     | Settings changes propagate to all products within 30s              |
| G7  | **Cross-product widgets** — composable dashboard with pinnable product widgets    | Users can customize their portal home with ≥10 widget types        |

### 3.2 Platform Goals

| #   | Goal                                                                                                 | Success Metric                                      |
| --- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| P1  | **Reusable shell** — portal built on `@bytelyst/dashboard-shell`                                     | Shell package consumed without forking              |
| P2  | **BFF pattern** — portal has its own Backend-For-Frontend that aggregates data from product backends | No direct browser→product-backend calls             |
| P3  | **Progressive rollout** — portal works even if some product backends are down                        | Graceful degradation for unavailable products       |
| P4  | **Extensible product cards** — each product registers a "portal card" config                         | New products auto-appear in portal via product.json |

### 3.3 Non-Goals (Explicit)

- **Not replacing product dashboards** — portal is a hub, not a replacement. Deep actions still happen in product UIs.
- **Not an admin console** — portal is user-facing. Admin features stay in admin-web.
- **Not a mobile app** — web-only for v1. Native portal is a future consideration.
- **Not real-time collaboration** — this is a personal dashboard, not a shared workspace.

---

## 4. Architecture

### 4.1 High-Level Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                    ByteLyst Portal (web)                      │
│                     Next.js 16 · port 3010                   │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐   │
│  │ Product   │ │ Activity │ │ Search   │ │ Settings     │   │
│  │ Launcher  │ │ Feed     │ │ Bar      │ │ & Billing    │   │
│  └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────┬───────┘   │
│       │             │            │               │           │
│       └─────────────┴────────────┴───────────────┘           │
│                          │                                    │
│              Portal BFF (Fastify 5 · port 4020)              │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ /api/portal/products    — product registry            │   │
│  │ /api/portal/feed        — aggregated activity         │   │
│  │ /api/portal/search      — cross-product search        │   │
│  │ /api/portal/widgets     — widget data aggregation     │   │
│  │ /api/portal/preferences — global user preferences     │   │
│  │ /api/portal/billing     — combined billing summary    │   │
│  └──────────────────────────────────────────────────────┘   │
│                          │                                    │
└──────────────────────────┼──────────────────────────────────┘
                           │
        ┌──────────────────┼──────────────────────┐
        ▼                  ▼                       ▼
   platform-service   product backends        product backends
     (port 4003)      (ports 4010-4019)      (ports 4010-4019)
   ┌──────────┐      ┌───────────────┐      ┌───────────────┐
   │ auth     │      │ LysnrAI 4015  │      │ FlowMonk 4017 │
   │ billing  │      │ MindLyst 4014 │      │ NoteLett 4016  │
   │ flags    │      │ ChronoMind 4011│      │ ActionTrail 4018│
   │ telemetry│      │ JarvisJr 4012 │      │ LocalMem  4019 │
   │ usage    │      │ NomGap   4013 │      │ PeakPulse 4010 │
   └──────────┘      └───────────────┘      └───────────────┘
```

### 4.2 Data Model

#### 4.2.1 UserDoc Enhancement (already in place via OneAuth)

```typescript
interface UserDoc {
  id: string;
  email: string;
  primaryProductId: string;
  memberships: ProductMembership[]; // ← from SmartAuth Phase 5
  globalPreferences?: GlobalPreferences; // ← NEW for portal
}

interface ProductMembership {
  productId: string;
  role: 'user' | 'admin';
  plan: 'free' | 'pro' | 'enterprise';
  firstAccessAt: string;
  subscriptionId?: string;
  licenseId?: string;
}

interface GlobalPreferences {
  theme: 'light' | 'dark' | 'system';
  timezone: string;
  locale: string;
  notifications: {
    email: boolean;
    push: boolean;
    inApp: boolean;
    digest: 'none' | 'daily' | 'weekly';
  };
  // NOTE: Widget layouts are stored separately in Cosmos container
  // `portal_widget_layouts` (partition: /userId), not here.
  // This keeps GlobalPreferences lightweight on UserDoc.
}
```

#### 4.2.2 Portal Widget Config

```typescript
interface WidgetLayout {
  widgetId: string; // e.g. 'lysnrai:recent-transcripts'
  productId: string;
  position: { row: number; col: number; width: number; height: number };
  config?: Record<string, unknown>;
}

interface PortalWidgetDefinition {
  id: string;
  productId: string;
  name: string;
  description: string;
  icon: string;
  sizes: ('small' | 'medium' | 'large')[];
  dataEndpoint: string; // BFF endpoint that fetches widget data
}
```

#### 4.2.3 Product Registry (from existing product.json files)

Each product already has a `product.json` with:

- `id`, `name`, `displayName`, `domain`, `description`
- `theme` (colors)
- `features` (capability flags)
- `ports` (service + dashboard)

The BFF reads all `products/*/product.json` at startup to build the product registry. No new schema needed.

### 4.3 BFF API Endpoints

| Method | Path                                 | Description                           | Source                              |
| ------ | ------------------------------------ | ------------------------------------- | ----------------------------------- |
| GET    | `/api/portal/products`               | List products user has access to      | platform-service `/auth/me`         |
| GET    | `/api/portal/feed`                   | Cross-product activity feed (last 7d) | Fan-out to product backends         |
| GET    | `/api/portal/search?q=...`           | Cross-product search                  | Fan-out to product search endpoints |
| GET    | `/api/portal/widgets/definitions`    | List available widget definitions     | Static registry in BFF              |
| GET    | `/api/portal/widgets/layout`         | Get user's widget layout              | Cosmos `portal_widget_layouts`      |
| PUT    | `/api/portal/widgets/layout`         | Save user's widget layout             | Cosmos `portal_widget_layouts`      |
| GET    | `/api/portal/widgets/:widgetId/data` | Fetch data for a specific widget      | Product backend endpoint            |
| GET    | `/api/portal/preferences`            | Get global preferences                | platform-service                    |
| PATCH  | `/api/portal/preferences`            | Update global preferences             | platform-service                    |
| GET    | `/api/portal/billing/summary`        | Combined billing across products      | platform-service subscriptions      |
| GET    | `/api/portal/billing/invoices`       | All invoices across products          | platform-service stripe             |
| POST   | `/api/portal/products/:id/switch`    | Switch active product context         | JWT re-issue                        |
| GET    | `/api/portal/notifications`          | Aggregated notifications              | platform-service                    |
| GET    | `/api/portal/quick-actions`          | Available quick actions per product   | Product registrations               |

### 4.4 JWT Enhancement

Current JWT (already includes memberships via OneAuth):

```json
{
  "sub": "user-123",
  "productId": "lysnrai",
  "primaryProductId": "lysnrai",
  "memberships": [
    { "productId": "lysnrai", "role": "user" },
    { "productId": "mindlyst", "role": "admin" }
  ]
}
```

Portal JWT adds a `scope: "portal"` claim so product backends know the request comes from the portal BFF:

```json
{
  "sub": "user-123",
  "scope": "portal",
  "primaryProductId": "lysnrai",
  "memberships": [...]
}
```

### 4.5 Tech Stack

| Layer               | Technology                                                                                                                                                                           |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Web**             | Next.js 16 (App Router), React 19, TailwindCSS v4, `@bytelyst/dashboard-shell`, Zustand, Recharts, Lucide icons                                                                      |
| **BFF**             | Fastify 5, TypeScript ESM, `@bytelyst/fastify-core`, `@bytelyst/api-client`, `@bytelyst/auth`, `@bytelyst/datastore`                                                                 |
| **Auth**            | JWT via `jose`, cookie-based session, `@bytelyst/react-auth`                                                                                                                         |
| **Shared packages** | `@bytelyst/config`, `@bytelyst/errors`, `@bytelyst/logger`, `@bytelyst/design-tokens`, `@bytelyst/telemetry-client`, `@bytelyst/feature-flag-client`, `@bytelyst/kill-switch-client` |
| **Database**        | Azure Cosmos DB (`productId: "portal"`) for portal-specific data (widget layouts, preferences)                                                                                       |
| **Tests**           | Vitest (BFF), Playwright (E2E)                                                                                                                                                       |

### 4.6 Cosmos Containers

Portal-specific data stored in Azure Cosmos DB with `productId: "portal"`:

| Container               | Partition Key | Purpose                         |
| ----------------------- | ------------- | ------------------------------- |
| `portal_widget_layouts` | `/userId`     | User widget grid configurations |

Global user preferences (`GlobalPreferences`) are stored on the existing `UserDoc` in platform-service's `users` container -- no new container needed for preferences.

---

## 5. Pages & UI

### 5.1 Page Map

| Page                 | Route            | Description                                             |
| -------------------- | ---------------- | ------------------------------------------------------- |
| **Home / Dashboard** | `/`              | Customizable widget grid + activity feed                |
| **Products**         | `/products`      | Product launcher grid with status badges                |
| **Activity**         | `/activity`      | Full cross-product activity timeline with filters       |
| **Search**           | `/search`        | Cross-product search results grouped by product         |
| **Billing**          | `/billing`       | Combined subscriptions, invoices, usage                 |
| **Settings**         | `/settings`      | Global preferences (theme, timezone, notifications)     |
| **Security**         | `/security`      | MFA, passkeys, devices, sessions (from SmartAuth)       |
| **Profile**          | `/profile`       | User profile, connected accounts (OAuth link/unlink)    |
| **Product Detail**   | `/products/[id]` | Deep link into product with embedded iframe or redirect |
| **Support**          | `/support`       | Cross-product support ticket creation and tracking      |

### 5.2 Product Launcher (Home)

The home page has three zones:

1. **Product Grid** (top) — 3×4 grid of product cards. Each card shows:
   - Product icon + name
   - Status badge (active/trial/free)
   - Last activity timestamp
   - Quick action button (e.g., "New Timer" for ChronoMind, "Dictate" for LysnrAI)
   - Click → opens product dashboard in new tab or embedded view

2. **Activity Feed** (left 60%) — chronological feed of recent events:
   - "You completed a 25-min Pomodoro in ChronoMind"
   - "New note created in NoteLett: 'Meeting Notes'"
   - "JarvisJr coaching session: 3 insights extracted"
   - Each item has product icon, timestamp, and deep link

3. **Widgets** (right 40%) — user-configurable widget stack:
   - ChronoMind: next timer countdown
   - NomGap: current fast progress
   - FlowMonk: today's schedule
   - MindLyst: brain activity heatmap
   - LysnrAI: recent transcripts
   - PeakPulse: weekly stats

### 5.3 Cross-Product Search

Global search bar in the top bar (Cmd+K). Results grouped by product:

```
"meeting notes"

NoteLett (3 results)
  - Meeting Notes -- Project Alpha (2h ago)
  - Weekly Standup Notes (yesterday)
  - Client Meeting Follow-up (3d ago)

LysnrAI (2 results)
  - Meeting transcript -- 03/21 (today)
  - Meeting transcript -- 03/18

MindLyst (1 result)
  - Memory: "Meeting with design team" in Work brain
```

### 5.4 Widget System

Widgets are the portal's core differentiator. Each product registers widget definitions:

| Widget ID                    | Product     | Size   | Description                        |
| ---------------------------- | ----------- | ------ | ---------------------------------- |
| `lysnrai:recent-transcripts` | LysnrAI     | Medium | Last 5 transcripts with duration   |
| `lysnrai:usage-chart`        | LysnrAI     | Large  | Weekly transcription minutes chart |
| `chronomind:next-timer`      | ChronoMind  | Small  | Countdown to next timer/alarm      |
| `chronomind:today-schedule`  | ChronoMind  | Medium | Today's timers and routines        |
| `nomgap:fast-progress`       | NomGap      | Small  | Current fast elapsed/remaining     |
| `nomgap:streak`              | NomGap      | Small  | Fasting streak count               |
| `mindlyst:brain-activity`    | MindLyst    | Medium | Brain captures heatmap (7d)        |
| `mindlyst:recent-captures`   | MindLyst    | Medium | Last 5 captures with brain tags    |
| `jarvisjr:coaching-streak`   | JarvisJr    | Small  | Coaching session streak            |
| `jarvisjr:recent-sessions`   | JarvisJr    | Medium | Last 3 agent sessions              |
| `flowmonk:today-schedule`    | FlowMonk    | Medium | Today's planned flow slots         |
| `flowmonk:task-summary`      | FlowMonk    | Small  | Tasks: pending/done/overdue        |
| `notelett:recent-notes`      | NoteLett    | Medium | Last 5 notes with tags             |
| `notelett:workspace-summary` | NoteLett    | Small  | Workspace note counts              |
| `actiontrail:recent-actions` | ActionTrail | Medium | Last 10 agent actions              |
| `actiontrail:risk-summary`   | ActionTrail | Small  | Risk level distribution            |
| `peakpulse:weekly-stats`     | PeakPulse   | Medium | Distance, elevation, sessions      |
| `peakpulse:recent-sessions`  | PeakPulse   | Medium | Last 3 adventure sessions          |
| `localmemgpt:recent-chats`   | LocalMemGPT | Medium | Last 5 conversations               |
| `portal:billing-summary`     | Portal      | Small  | Combined monthly cost              |
| `portal:security-status`     | Portal      | Small  | MFA status, device count           |

---

## 6. Cross-Product Activity Feed

### 6.1 Feed Event Schema

```typescript
interface FeedEvent {
  id: string;
  productId: string;
  productName: string;
  productIcon: string;
  userId: string;
  type: string; // product-specific event type
  title: string; // human-readable summary
  description?: string;
  deepLink?: string; // URL to the item in the product dashboard
  metadata?: Record<string, unknown>;
  createdAt: string;
}
```

### 6.2 Event Sources per Product

| Product     | Event Types                                            | Verified Backend Endpoint                 |
| ----------- | ------------------------------------------------------ | ----------------------------------------- |
| LysnrAI     | `transcript.created`, `session.completed`              | `GET /api/transcripts?limit=10`           |
| MindLyst    | `capture.created`, `triage.completed`                  | `GET /api/memory-items?limit=10`          |
| ChronoMind  | `timer.completed`, `routine.finished`, `pomodoro.done` | `GET /api/timers?limit=10&sort=updatedAt` |
| JarvisJr    | `session.completed`, `memory.created`                  | `GET /api/jarvis/sessions?limit=10`       |
| NomGap      | `fast.started`, `fast.completed`, `milestone.reached`  | `GET /api/fasting/sessions?limit=10`      |
| PeakPulse   | `session.recorded`, `goal.achieved`, `badge.unlocked`  | `GET /api/peak/sessions?limit=10`         |
| FlowMonk    | `task.completed`, `schedule.generated`, `entry.done`   | `GET /api/schedule-entries?limit=10`      |
| NoteLett    | `note.created`, `note.updated`, `task.extracted`       | `GET /api/notes?limit=10`                 |
| ActionTrail | `action.ingested`, `alert.fired`, `approval.decided`   | `GET /api/actions?limit=10`               |
| LocalMemGPT | `conversation.created`, `document.uploaded`            | `GET /api/conversations` (no limit param) |

### 6.3 Aggregation Strategy

The BFF fetches from all product backends in parallel with:

- **Timeout:** 3s per product (fail-open — missing products show "temporarily unavailable")
- **Caching:** 60s TTL in-memory cache per user
- **Pagination:** Feed returns 50 items max, sorted by `createdAt` desc
- **Circuit breaker:** After 3 consecutive failures, skip product for 5 minutes

---

## 7. Security & Auth

### 7.1 Authentication Flow

1. User visits `portal.bytelyst.com`
2. If no session, redirect to `/login` (uses platform-service `/auth/login`)
3. On success, BFF issues a portal-scoped JWT with all memberships
4. JWT stored as httpOnly cookie (not localStorage)
5. BFF proxies requests to product backends using service-to-service auth

### 7.2 Authorization

- Portal BFF has a **service API key** for each product backend
- When fetching product data, BFF sends `x-user-id` + `x-portal-token` headers
- Product backends validate the portal service key and scope data to the user
- Users can only see data for products they have memberships in

### 7.3 Security Page Features

Leverages existing SmartAuth infrastructure:

- **MFA management** — TOTP setup/disable, recovery codes
- **Passkey management** — register/remove WebAuthn credentials
- **Device management** — view trusted devices, revoke sessions
- **Login history** — recent login events with IP/location
- **Connected accounts** — link/unlink Google, Microsoft, Apple OAuth

---

## 8. Offline & Resilience

### 8.1 Graceful Degradation

The portal must work even when some product backends are down:

| Scenario                   | Behavior                                                     |
| -------------------------- | ------------------------------------------------------------ |
| Product backend down       | Widget shows "Temporarily unavailable" with retry button     |
| Platform-service down      | Portal shows cached data, auth redirects to maintenance page |
| BFF down                   | Static Next.js pages with "Service connecting..." message    |
| Slow product backend (>3s) | Widget shows skeleton, loads async when available            |

### 8.2 Caching Strategy

| Data               | Cache TTL  | Storage               |
| ------------------ | ---------- | --------------------- |
| Product registry   | 1 hour     | BFF memory            |
| User memberships   | 5 minutes  | BFF memory            |
| Activity feed      | 60 seconds | BFF memory per user   |
| Widget data        | 30 seconds | BFF memory per widget |
| Global preferences | 10 minutes | BFF memory per user   |
| Search results     | No cache   | Real-time fan-out     |

---

## 9. Telemetry & Analytics

### 9.1 Portal-Specific Events

| Event                       | Properties                                      |
| --------------------------- | ----------------------------------------------- |
| `portal.product_launched`   | `productId`, `source` (grid/feed/search/widget) |
| `portal.search_executed`    | `query`, `resultCount`, `productHits[]`         |
| `portal.widget_interacted`  | `widgetId`, `action`                            |
| `portal.layout_customized`  | `widgetCount`, `widgetIds[]`                    |
| `portal.preference_changed` | `key`, `oldValue`, `newValue`                   |
| `portal.feed_scrolled`      | `depth`, `productsVisible[]`                    |

### 9.2 Cross-Product Insights (Admin)

The portal BFF can provide admin-web with:

- **Cross-product adoption:** How many users use 1, 2, 3+ products
- **Product affinity:** Which products are commonly used together
- **Portal engagement:** Time spent in portal vs. direct product access
- **Widget popularity:** Most/least configured widgets

---

## 10. Open Questions

| #   | Question                                                   | Options                                                      | Recommendation                                                            |
| --- | ---------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------- |
| Q1  | Standalone repo or inside common-plat?                     | Standalone `learning_ai_portal` vs. `dashboards/portal-web/` | **Standalone repo** — it has its own BFF, making it more than a dashboard |
| Q2  | Embedded product views or redirect?                        | iframe embed vs. open in new tab                             | **New tab** for v1, embedded for v2                                       |
| Q3  | Should BFF own its own Cosmos containers?                  | Yes (portal-specific) vs. use platform-service only          | **Yes** — for widget layouts, portal preferences, feed cache              |
| Q4  | Widget data: pull or push?                                 | BFF polls products vs. products push events                  | **Pull** for v1 (simpler), SSE push for v2                                |
| Q5  | How does portal handle products user hasn't signed up for? | Hide vs. show with "Get Started" CTA                         | **Show with CTA** — drives cross-product adoption                         |

---

## 11. Dependencies

| Dependency                                              | Status                                                           | Blocker?                            |
| ------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------- |
| OneAuth `memberships[]` on UserDoc                      | ✅ Implemented                                                   | No                                  |
| `/auth/me` returns product list                         | ✅ Implemented                                                   | No                                  |
| `@bytelyst/dashboard-shell`                             | ✅ Published                                                     | No                                  |
| `@bytelyst/react-auth`                                  | ✅ Published                                                     | No                                  |
| Product backends expose recent-items APIs               | ⚠️ Partial — most have list endpoints                            | Non-blocking (graceful degradation) |
| `product.json` files for all 10 products                | ✅ 7 exist, 3 need creation (FlowMonk, ActionTrail, LocalMemGPT) | Non-blocking                        |
| Service-to-service auth (portal BFF → product backends) | ❌ Needs implementation                                          | Phase 1 blocker                     |
| Global preferences on UserDoc                           | ❌ Needs implementation                                          | Phase 5 blocker                     |

---

_This PRD should be reviewed and updated as implementation progresses._
