diff --git a/docs/UNIFIED_BYTELYST_PORTAL_PRD.md b/docs/UNIFIED_BYTELYST_PORTAL_PRD.md index c64ea50b..549fdbe8 100644 --- a/docs/UNIFIED_BYTELYST_PORTAL_PRD.md +++ b/docs/UNIFIED_BYTELYST_PORTAL_PRD.md @@ -6,6 +6,30 @@ --- +## 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 | @@ -14,7 +38,7 @@ | **Product ID** | `portal` | | **Domain** | portal.bytelyst.com | | **Repo** | `learning_ai_portal` (new) | -| **Ecosystem** | ByteLyst (consumes platform-service + all 11 product backends) | +| **Ecosystem** | ByteLyst (consumes platform-service + all 10 product backends) | | **Port (web)** | 3010 | | **Port (BFF)** | 4020 | @@ -22,7 +46,7 @@ ## 2. Problem Statement -The ByteLyst ecosystem has grown to **11 products** (LysnrAI, MindLyst, ChronoMind, JarvisJr, NomGap, PeakPulse, FlowMonk, NoteLett, ActionTrail, LocalMemGPT, ByteLyst Auth), each with its own web dashboard, login, and settings. Users who use multiple products face: +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" @@ -40,10 +64,10 @@ Meanwhile, the **admin dashboard** has grown to 43+ backend modules with 511+ en | # | Goal | Success Metric | | --- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------ | -| G1 | **Single login, all products** — one URL, one session, access everything | 100% of products accessible from portal | +| 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 11 products launchable with deep links | +| 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 | @@ -100,7 +124,7 @@ Meanwhile, the **admin dashboard** has grown to 43+ backend modules with 511+ en ┌──────────┐ ┌───────────────┐ ┌───────────────┐ │ auth │ │ LysnrAI 4015 │ │ FlowMonk 4017 │ │ billing │ │ MindLyst 4014 │ │ NoteLett 4016 │ - │ flags │ │ ChronoMind4011│ │ ActionTrail4018│ + │ flags │ │ ChronoMind 4011│ │ ActionTrail 4018│ │ telemetry│ │ JarvisJr 4012 │ │ LocalMem 4019 │ │ usage │ │ NomGap 4013 │ │ PeakPulse 4010 │ └──────────┘ └───────────────┘ └───────────────┘ @@ -138,7 +162,9 @@ interface GlobalPreferences { inApp: boolean; digest: 'none' | 'daily' | 'weekly'; }; - portalLayout: WidgetLayout[]; + // NOTE: Widget layouts are stored separately in Cosmos container + // `portal_widget_layouts` (partition: /userId), not here. + // This keeps GlobalPreferences lightweight on UserDoc. } ``` @@ -176,19 +202,22 @@ The BFF reads all `products/*/product.json` at startup to build the product regi ### 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/:widgetId` | 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 | +| 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 @@ -222,12 +251,22 @@ Portal JWT adds a `scope: "portal"` claim so product backends know the request c | 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` | +| **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 @@ -277,18 +316,18 @@ The home page has three zones: Global search bar in the top bar (Cmd+K). Results grouped by product: ``` -🔍 "meeting notes" +"meeting notes" -📝 NoteLett (3 results) - - Meeting Notes — Project Alpha (2h ago) +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 +LysnrAI (2 results) + - Meeting transcript -- 03/21 (today) + - Meeting transcript -- 03/18 -🧠 MindLyst (1 result) +MindLyst (1 result) - Memory: "Meeting with design team" in Work brain ``` @@ -344,18 +383,18 @@ interface FeedEvent { ### 6.2 Event Sources per Product -| Product | Event Types | Source | -| ----------- | ------------------------------------------------------ | ------------------------------------ | -| 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/recent` | -| 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/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?limit=10` | +| 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 @@ -461,16 +500,16 @@ The portal BFF can provide admin-web with: ## 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 11 products | ✅ 7 exist, 4 need creation | Non-blocking | -| Service-to-service auth (portal BFF → product backends) | ❌ Needs implementation | Phase 1 blocker | -| Global preferences on UserDoc | ❌ Needs implementation | Phase 2 blocker | +| 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 | --- diff --git a/docs/UNIFIED_BYTELYST_PORTAL_ROADMAP.md b/docs/UNIFIED_BYTELYST_PORTAL_ROADMAP.md index 79eb1112..ef372c3f 100644 --- a/docs/UNIFIED_BYTELYST_PORTAL_ROADMAP.md +++ b/docs/UNIFIED_BYTELYST_PORTAL_ROADMAP.md @@ -1,7 +1,7 @@ # 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 · ~124 tasks · 6-8 weeks +> **Estimated total:** 8 phases · ~129 tasks · 6-8 weeks --- @@ -9,29 +9,33 @@ > **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 | | +| # | 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. +**Phase 0 deliverable:** Repo builds, BFF returns `GET /health`, web renders empty shell with sidebar. (23 tasks) --- @@ -53,7 +57,8 @@ | 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 | +| 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. @@ -200,7 +205,7 @@ | 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.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. @@ -230,8 +235,8 @@ | 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 | +| **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 | @@ -239,7 +244,7 @@ | **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** | | +| | **Total** | **~21 days** | **129** | | ---