This commit is contained in:
269
docs/ux-refactor/gap-cards/S00-appshell-ia.md
Normal file
269
docs/ux-refactor/gap-cards/S00-appshell-ia.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# Gap Card — S00 · AppShell + Information Architecture + Navigation
|
||||
|
||||
> Screen ID: **S00** · UX-refactor planning track · grounded in live `apps/web/src` + `packages/*`.
|
||||
> Execution model is **LOCKED**: in-place incremental refactor of the existing shell, not a rebuild.
|
||||
> PRD = `docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`.
|
||||
> Disposition: **rework** (the shell exists and is strong; it is reframed + extended, not recreated).
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
S00 is the **application chrome and navigation spine** that every other screen mounts inside — it is
|
||||
not a "screen" the user opens, it is the frame they always see. PRD §1 names the spine: Home Cockpit,
|
||||
Workspace Desktop, Command Center (Ctrl+K), visible Memory, Extend layer, Team. S00 is the layer that
|
||||
makes that spine reachable.
|
||||
|
||||
Per the blueprint Developer Handoff (`_blueprint_extracted.txt:498`), **AppShell** = "Global route
|
||||
layout, sidebar, active workspace, top status, command palette provider." Its responsibilities:
|
||||
|
||||
- Persistent top status bar (workspace, model, memory trophy, trial, search, notifications, clock).
|
||||
- Primary navigation expressing the six IA layers (PRD §10: Global / Work / Intelligence / Extend /
|
||||
Team / System) — blueprint `_blueprint_extracted.txt:68-81` + page-3 `06_board_all_in_one.png`.
|
||||
- Global, always-available command layer (Ctrl+K) — PRD §12.3, §6.3.
|
||||
- Active-workspace context held in a global store and threaded to every surface
|
||||
(`_blueprint_extracted.txt:512`).
|
||||
- The container for empty / loading / error / offline / permission-denied states (PRD §14.1).
|
||||
|
||||
**Purpose:** collapse the current 27-app "app-launcher" mental model into the IA spine
|
||||
(PRD §3.2, §3.3, §20.4 "do not add more top-level apps without fitting IA"), without rebuilding
|
||||
the windowed runtime that already works.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
S00 itself is mostly stateless chrome, but it **hosts** the global states and must render them:
|
||||
|
||||
- **Global states (PRD §14.1, blueprint AC `:604`):** Loading, Empty, Populated, Error,
|
||||
Offline/local-only, Syncing, Permission denied, Partial data, Approval required — every screen
|
||||
inside the shell must support these; the shell provides the offline indicator + error boundary +
|
||||
approval modal mounts.
|
||||
- **Navigation states:** active layer/route highlighted; active workspace shown in status; open vs
|
||||
minimized window indicators (current dock dots).
|
||||
- **Command Center states (PRD §12.3):** Idle, Query active, Grouped results, No results, Permission
|
||||
prompt, Command success, Command failure.
|
||||
- **Offline / local-only (PRD §6.7, §14.1; blueprint `:516`):** shell degrades connectors/MCPs
|
||||
gracefully while keeping local workspace + memory reachable; offline badge in status bar.
|
||||
- **First-run vs returning:** onboarding wizard replaces the shell entirely on first launch
|
||||
(current behavior, see §3); returning users land in the populated shell.
|
||||
- **Accessibility (PRD §19.3, blueprint `:489`):** keyboard-first, Ctrl+K + tab nav, visible focus,
|
||||
non-color status, text labels on all badges.
|
||||
|
||||
Layout direction (blueprint `:483`, directional only): "Desktop-first, **left navigation**, central
|
||||
workspace canvas, optional right context rail, Ctrl+K overlay." Note this conflicts with the current
|
||||
**bottom-dock** shell — see Open Questions Q1 (the PRD acceptance criteria, not the mock, win:
|
||||
PRD §24 risk row "Visual mocks overfit implementation").
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo (disposition: **rework**)
|
||||
|
||||
**There is no `AppShell` / `AppLayout` / `GlobalLayout` component** — grep over `apps/web/src` for
|
||||
`AppShell|GlobalLayout|AppLayout` returns **zero matches**. The shell's responsibilities are spread
|
||||
across these real files:
|
||||
|
||||
| File | What it does today |
|
||||
|---|---|
|
||||
| `apps/web/src/components/os/Desktop.tsx` | **The de-facto AppShell.** Root OS shell: wires all domain hooks, the window manager, `appConfig` (per-`AppId` title/icon/pos/size, `:77-102`), `renderAppContent` (the `switch(win.appId)` app router, `:276-363`), all overlays, theme observer, trial/tier fetch, onboarding early-return (`:262-273`), the `waggle:open-app` CustomEvent bus (`:162-169`). ~550 LOC. |
|
||||
| `apps/web/src/components/os/StatusBar.tsx` | Top bar (PRD "top status"): logo, workspace name, focused-window label, model, memory-frame trophy, dev tokens/cost, trial badge, **Search button (Ctrl+K)**, notifications bell, offline indicator, clock. |
|
||||
| `apps/web/src/components/os/Dock.tsx` + `lib/dock-tiers.ts` + `DockTray.tsx` | **The de-facto primary navigation.** Bottom dock, tier-filtered `DockEntry[]`, `zone-parent` flyouts (Ops, Extend) via portal-to-body `DockTray`, open/minimized dots, Spawn Agent, Waggle badge. `dock-tiers.ts` is the canonical `AppId` union + `getDockForTier()`. |
|
||||
| `apps/web/src/components/os/AppWindow.tsx` | Draggable/resizable/snappable window chrome (one per open app). The "windowed runtime" — orthogonal to IA but the thing the IA must keep. |
|
||||
| `apps/web/src/hooks/useWindowManager.ts` | Window state machine (`WindowState[]`, persisted to `localStorage waggle-window-state-v1`), `openApp(AppId)` / `openChatForWorkspace(...)`, focus/minimize/cycle. **Navigation == window ops, keyed by `AppId`, NOT by URL.** |
|
||||
| `apps/web/src/hooks/useOverlayState.ts` | All overlay open/close flags (global search, switchers, notifications, etc.) — the global "command palette state / overlay state" store. |
|
||||
| `apps/web/src/components/os/overlays/GlobalSearch.tsx` | **The de-facto Command Center (Ctrl+K).** Ctrl+K palette, 5 categories (`command\|workspace\|memory\|session\|skill`, `:15`), but built on a **static `COMMANDS[]` array (`:40-64`)** that is hand-synced to `appConfig` (drift warning in-file `:33-39`) — no Create/Run/Extend categories, no backend command index. |
|
||||
| `apps/web/src/hooks/useKeyboardShortcuts.ts` | Binds global hotkeys (Ctrl+K search, persona/workspace switchers, new chat, window cycle). |
|
||||
| `apps/web/src/lib/types.ts` | Holds the **stale** `AppView` union (8 ids, `:3-11`). |
|
||||
| `apps/web/src/pages/Index.tsx` (route `/`) + `App.tsx` | Single-route app: `BrowserRouter` → `/` → `BootScreen` → `Desktop`. **No per-app routes exist** (frontend inventory `frontend.md:9-13`). |
|
||||
|
||||
**Key structural facts driving the rework (not a rebuild):**
|
||||
|
||||
1. **Single-route windowed desktop, not a navigable app.** "Navigation" is window management by
|
||||
`AppId`. The blueprint's `/home,/workspaces,/memory,…` route groups (`:81`) and "left navigation"
|
||||
(`:483`) are **directional**; the in-place model expresses the six IA layers through the existing
|
||||
**dock-zone model** (`dock-tiers.ts` `zone-parent`) + window manager — NOT by introducing
|
||||
react-router routes (PRD §20.2 "App surfaces -> Work/Intelligence/Extend categories";
|
||||
§20.4 "Do not add more top-level apps without fitting IA").
|
||||
2. **Dual app-id union drift (cleanup target).** `AppId` (27 ids, canonical, `dock-tiers.ts:7-13`)
|
||||
vs `AppView` (8 ids, stale, `types.ts:3-11`). `AppView` is unused by the window manager. Dead ids
|
||||
`terminal` / `calculator` / `notes` are declared in `AppId` but have **no component / no
|
||||
`appConfig` entry** (Desktop `:77-102`) — dead.
|
||||
3. **Three overlapping tier vocabularies** the IA gating must reconcile (frontend inventory
|
||||
`frontend.md:376`): `UserTier` (UI density `simple\|professional\|power\|admin`, `dock-tiers.ts:15`),
|
||||
`BillingTier` (`FREE\|TRIAL\|PRO\|TEAMS\|ENTERPRISE`, `:17`), and `PlanTier`
|
||||
(`solo\|teams\|business\|enterprise` in `lib/feature-gates.ts`). Dock filters on
|
||||
`UserTier` × `BillingTier` (`getDockForTier`, `:127`).
|
||||
4. **Marketplace is doubly represented** (standalone `MarketplaceApp`, no dock entry, + a section
|
||||
inside `CapabilitiesApp`) — an IA-consolidation point (frontend inventory `frontend.md:374`).
|
||||
5. **IA→existing-app mapping already drafted** in `frontend.md:357-364` (Global/Work/Intelligence/
|
||||
Extend/Team/System buckets → current apps). S00 hardens that mapping into dock zones + labels.
|
||||
|
||||
**Why rework, not create-new:** Desktop/Dock/StatusBar/AppWindow/window-manager are functional,
|
||||
test-covered, and PRD §20.1 explicitly says keep+promote the substrate. The deliverable is an IA
|
||||
**reframe** (zone labels, consolidation, union dedup, command-index wiring), not a new shell.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
> Principle (CLAUDE.md §3.3 surgical changes): extend the existing shell. Do not introduce
|
||||
> react-router app routes. Express IA through dock zones + a shared app catalog.
|
||||
|
||||
### 4a. Consolidate to one app catalog (kills the drift)
|
||||
- **Rework `lib/dock-tiers.ts` into / beside a `lib/app-catalog.ts`** that is the single source for:
|
||||
`AppId`, per-app metadata (title, icon, default pos/size — currently siloed in `Desktop.appConfig`
|
||||
`:77-102`), IA-layer membership (`Work|Intelligence|Extend|Team|System|Global`), and tier gating.
|
||||
Derive **both** `appConfig` (Desktop) and `COMMANDS[]` (GlobalSearch `:40-64`) and `DockEntry[]`
|
||||
from it. Fixes FR #13 drift (called out in `GlobalSearch.tsx:33-39`).
|
||||
- **Delete the stale `AppView` union** (`types.ts:3-11`) and the dead `terminal`/`calculator`/`notes`
|
||||
ids from `AppId` (exhaustive grep first per CLAUDE.md §3.5 — they appear in `dock-tiers.ts`,
|
||||
possibly tests).
|
||||
|
||||
### 4b. IA reframe of the dock (the "primary navigation")
|
||||
- **Rework `TIER_DOCK_CONFIG`** (`dock-tiers.ts:82-105`) so the power-tier dock zones map 1:1 to the
|
||||
PRD IA layers: today there are 2 zone-parents (`Ops`, `Extend`); the target is the six-layer IA
|
||||
(`frontend.md:357-364` mapping). Likely outcome: rename/regroup into **Work** (Home, Chat, Memory,
|
||||
Files/Artifacts, Sessions), **Intelligence** (Personas/Agents, Skills, Automations, Room/Mission
|
||||
Control), **Extend** (Connectors, MCPs, Marketplace, AI Tools), **Team**, **System** (Settings,
|
||||
Vault, Profile, Backup, Telemetry). Keep the existing `zone-parent` + `DockTray` mechanism.
|
||||
- Resolve **Marketplace double-representation** — one canonical surface (Extend), remove the orphan.
|
||||
- Keep tier filtering (`filterByBillingTier`, `:111`); reconcile the three tier vocabularies into one
|
||||
gating helper (or document the mapping) as part of this card so downstream cards inherit it.
|
||||
|
||||
### 4c. Extract an explicit `AppShell` boundary (optional, low-risk)
|
||||
- `Desktop.tsx` is ~550 LOC and conflates shell + router + state wiring (CLAUDE.md §3.2/§4 favor
|
||||
smaller files). Optionally extract a thin `AppShell` that owns: status bar + dock + overlay mounts +
|
||||
error boundary + offline state, leaving `Desktop` to own only the window manager + `renderAppContent`.
|
||||
This is the component the blueprint names (`:498`). **Not required for behavior** — flag as a clean-up
|
||||
the plan can sequence late.
|
||||
|
||||
### 4d. Command Center upgrade (Ctrl+K) — depends on backend §16.3 (see §5)
|
||||
- **Rework `GlobalSearch.tsx`** from static-list + client fuzzy-match into a backend-fed command index:
|
||||
add the PRD §12.3 category sections **Search / Launch / Create / Run / Navigate / Extend** (today only
|
||||
command/workspace/memory/session/skill exist), federated over `/api/command/search` when present.
|
||||
Reuse `fuzzyMatch` (`lib/fuzzy-match.ts`) as the offline fallback. Add the Command Center states
|
||||
(§2). Keep the `onNavigate(type,id)` → `Desktop.handleSearchNavigate` (`Desktop.tsx:217-225`)
|
||||
wiring; extend it for Create/Run/Extend dispatch.
|
||||
|
||||
### 4e. Global store for shell state (blueprint `:512`)
|
||||
- Today shell state is scattered (`useWorkspaces`, `useOverlayState`, ad-hoc `Desktop` `useState` for
|
||||
tier/trial/theme). Consolidate the **global** slice the blueprint names — `{ activeWorkspaceId,
|
||||
command palette state, user profile, connection/offline status, feature flags }` — behind a single
|
||||
provider/hook so Home/Workspace/Command surfaces read one source. Reuse `useOverlayState`,
|
||||
`useWorkspaces`, `useOfflineStatus`, `useBilling`/`useFeatureGate` rather than replacing them.
|
||||
|
||||
**Reuse targets:** `Desktop.tsx`, `Dock.tsx`/`DockTray.tsx`/`dock-tiers.ts`, `StatusBar.tsx`,
|
||||
`AppWindow.tsx`, `useWindowManager`, `useOverlayState`, `GlobalSearch.tsx`, `useKeyboardShortcuts`,
|
||||
`ErrorBoundary.tsx`, `useOfflineStatus`, `useBilling`/`useFeatureGate`.
|
||||
|
||||
**Props/state:** `AppShell` (if extracted) takes no props (reads global store); the app catalog is a
|
||||
pure module; `Dock` keeps its current props (`tier`, `billingTier`, `onOpenApp`, `openApps`,
|
||||
`minimizedApps`, `onSpawnAgent`, `waggleBadgeCount`); `GlobalSearch` keeps `{open,onClose,onNavigate}`
|
||||
plus internal async result groups.
|
||||
|
||||
**Adapter methods/hooks:** S00 chrome needs none beyond what exists. The Command Center upgrade
|
||||
(4d) needs new adapter methods `commandSearch(q)` / `commandExecute(...)` / `commandRecent()` /
|
||||
`commandSuggestions()` added to `lib/adapter.ts` (the one contract surface, `frontend.md:380`) once
|
||||
the backend routes (§5) land.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work
|
||||
|
||||
**S00 as pure shell/IA/navigation needs NO new backend** — it is frontend chrome + client-side
|
||||
window routing. The dependency is the **Command Center (4d)**, which the IA spine requires
|
||||
(PRD §6.3 "Ctrl+K always available"; blueprint `:515` "command index should unify workspaces, memory,
|
||||
artifacts, sessions, agents, skills, connectors, MCPs, actions and recent commands").
|
||||
|
||||
PRD §16.3 Command Center endpoints (cross-ref `backend-routes.md:444-451`):
|
||||
|
||||
| PRD §16 endpoint | Status | EXTEND vs NET-NEW · substrate · note |
|
||||
|---|---|---|
|
||||
| `GET /api/command/search?q=` | **MISSING** | **NET-NEW** federating route. No `/api/command/*` exists; `commands.ts` is `/api/commands/execute` (slash-exec, different shape). Touches no new store — fans out over `/api/memory/search` (`memory.ts`), `WorkspaceManager.list()` (`workspaces.ts`), `/api/skills`, `/api/workspaces/:id/sessions/search` (`sessions.ts`). New `command.ts` route under `packages/server/src/local/routes/`. |
|
||||
| `POST /api/command/execute` | **PARTIAL** | **EXTEND** `POST /api/commands/execute` (note **plural** `commands`, `commands.ts`). Current runs slash commands with a subset CommandContext; PRD's generic palette execute is broader (navigate/create/run/extend dispatch). Extend or add a thin singular alias. |
|
||||
| `GET /api/command/recent` | **MISSING** | **NET-NEW** (or derive client-side from session/window history — defer to client first). |
|
||||
| `GET /api/command/suggestions` | **MISSING** | **NET-NEW**. Closest analog `GET /api/skills/suggestions` (`skills.ts`, different domain) is a pattern to copy, not reuse. |
|
||||
|
||||
**No `.mind` migration for S00.** None of the Command Center routes add a data store
|
||||
(`backend-routes.md:604-607`: every MISSING endpoint is buildable over existing substrates). The
|
||||
status bar's memory trophy already uses `GET /api/memory/stats` (`StatusBar.tsx:40`), and trial/tier
|
||||
uses `GET /api/tier` (`Desktop.tsx:136`) — both EXIST.
|
||||
|
||||
> Note: a command-index that searches "artifacts" (PRD §12.3) is blocked on the Artifacts domain
|
||||
> (PRD §16.6, entirely MISSING — `backend-routes.md:477-484`). S00's Command Center can ship the
|
||||
> existing object types first and add Artifacts/Agents/Automations groups as those screens land.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
S00 is mostly literal-union + nav typing. PRD §15.2 target unions — **none currently exist** in
|
||||
`apps/web/src/lib/types.ts` (substrate-types `substrate-types.md:218-229`):
|
||||
|
||||
- **Reframe the IA into types:** introduce a `IALayer = 'global'|'work'|'intelligence'|'extend'|
|
||||
'team'|'system'` union and attach it to the app-catalog entry type (new, in `lib/app-catalog.ts`
|
||||
or `dock-tiers.ts`). Not a PRD §15 type but the type that makes the IA explicit.
|
||||
- **Delete `AppView`** (`types.ts:3-11`) — superseded by `AppId` (§4a). Consolidate exports so
|
||||
`AppId`/`UserTier`/`BillingTier`/`DockEntry` (currently in `dock-tiers.ts`) and the catalog type
|
||||
live in one place.
|
||||
- **Command Center types (for 4d):** extend `SearchCategory` (`GlobalSearch.tsx:15`) beyond
|
||||
the 5 current values to PRD §12.3's Search/Launch/Create/Run/Navigate/Extend; add a `Command`
|
||||
result type (PRD §11 glossary "Command" object). Add a `CommandResult` interface used by both the
|
||||
adapter method return and the palette.
|
||||
- **Tier reconciliation type:** a single helper type mapping `UserTier`↔`BillingTier`↔`PlanTier`
|
||||
(currently three vocabularies across `dock-tiers.ts` + `feature-gates.ts`). Document or unify so
|
||||
every downstream gap card inherits one gating contract.
|
||||
|
||||
PRD §15.2 entity unions (`WorkspaceType`, `Scope`, `Confidence`, `MemoryKind`, `ArtifactKind`,
|
||||
`AgentType`, `AutonomyLevel`, `ExtensionType`) are **owned by their respective screens**, not S00 —
|
||||
S00 only needs the nav/command/tier types above. Establishing the shared-types module location is an
|
||||
S00 responsibility (blueprint Phase 0 "Define shared frontend types", PRD §8 Phase 0).
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **Blocks everything.** S00 is PRD Phase 0 / blueprint Phase 0 ("Freeze UX spine: route names,
|
||||
screen inventory, navigation, naming, data scopes", `_blueprint_extracted.txt:581`) and PRD §21
|
||||
Sprint 1 ("AppShell navigation, route map, shared types, command provider skeleton"). Every other
|
||||
gap card (Home Cockpit S0x, Workspace Desktop, Memory Center, Extend, Team) mounts inside this shell
|
||||
and consumes the app catalog + IA layering + global store defined here.
|
||||
- **Internal ordering:** 4a (app catalog / union dedup) → 4b (dock IA reframe) → 4e (global store)
|
||||
can all ship **frontend-only, no backend**. 4d (Command Center upgrade) is gated on backend §5
|
||||
`/api/command/*` (PRD §21 Sprint 3) — ship the Ctrl+K **skeleton + static/offline fallback** in
|
||||
Phase 0/Sprint 1 (matches "command provider skeleton", PRD §21 Sprint 1), then wire the backend
|
||||
index in Sprint 3.
|
||||
- **Depends on no other screen.** Consumes only existing substrate (`GET /api/tier`,
|
||||
`GET /api/memory/stats`, `GET /api/workspaces`) which all EXIST.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **L**
|
||||
|
||||
Frontend-heavy reframe touching the highest-traffic, highest-blast-radius files in the app
|
||||
(`Desktop.tsx` ~550 LOC, `Dock.tsx`, `dock-tiers.ts`, `GlobalSearch.tsx`, window manager) plus a
|
||||
union/tier-vocabulary consolidation that requires exhaustive grep (CLAUDE.md §3.5) and the net-new
|
||||
`/api/command/*` federating route. Not XL because it reuses the working windowed runtime wholesale
|
||||
(no rebuild) and adds no data store / migration. The Command Center backend index pushes it from M→L.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Bottom dock vs left navigation.** Blueprint `:483` + mock `06_board_all_in_one.png` show
|
||||
**left navigation**; the live shell is a **bottom dock** (`Dock.tsx`). Mocks are directional
|
||||
(PRD §24); does the founder want the dock reframed-in-place (cheaper, keeps the OS feel) or
|
||||
migrated to a left rail (closer to mock, larger blast radius)? Recommend in-place dock reframe
|
||||
unless the left rail is a hard requirement.
|
||||
2. **Routes vs window-IDs.** Blueprint `:81` asks for `/home,/workspaces,…` route groups; the app
|
||||
is single-route windowed. Confirm we keep `AppId`-keyed window navigation (recommended, in-place)
|
||||
rather than introducing react-router app routes (larger change, conflicts with multi-window).
|
||||
Deep-linking/back-button behavior is the only thing real routes would buy.
|
||||
3. **Tier-vocabulary unification.** Three tiers (`UserTier`/`BillingTier`/`PlanTier`) gate the dock,
|
||||
billing, and features independently. Should S00 unify them into one model now (clean but
|
||||
cross-cutting), or just document the mapping and defer? PRD §17 RBAC roles add a 4th axis.
|
||||
4. **Command Center scope for v1 (PRD §23 Q-implied).** Which object types ship in the Ctrl+K index
|
||||
first? Artifacts/Agents/Automations are blocked on their domains (§5 note). Propose:
|
||||
workspaces + memory + sessions + skills + commands at launch, add the rest as screens land.
|
||||
5. **AppShell extraction now or later?** Pull the shell out of `Desktop.tsx` (4c) up front for a
|
||||
clean boundary, or defer to a polish pass to minimize churn during the high-velocity refactor?
|
||||
197
docs/ux-refactor/gap-cards/S01-home-cockpit.md
Normal file
197
docs/ux-refactor/gap-cards/S01-home-cockpit.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# Gap Card — S01 Home Cockpit
|
||||
|
||||
> Execution model: **in-place incremental refactor** of `apps/web` + targeted backend extensions.
|
||||
> PRD source of truth: §12.1 (lines 373-410), §15 (data model), §16.1 (API), §20.1 (keep/promote).
|
||||
> Mockup `screen_01_home_cockpit.png` is **directional** — acceptance criteria win over pixels.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
Home Cockpit is the **daily executive briefing and launch surface** — the first thing the user
|
||||
sees after boot, before opening any workspace. PRD §12.1: "Give the user a useful daily briefing
|
||||
and immediate next actions." Acceptance bar (§12.1): user understands the day in <30 s, continues a
|
||||
workspace in one click, opens Ctrl+K from the keyboard, and captures a note/task/link/file from Home.
|
||||
|
||||
This is a **new top-level surface** in the IA spine (PRD §1: spine item #1). Today there is no Home
|
||||
Cockpit; the closest things are the per-workspace `WorkspaceBriefing` (chat home screen) and the
|
||||
modal `LoginBriefing` (one-shot catch-up overlay). The mockup shows six regions: greeting + date,
|
||||
"You were working on" (recent workspaces with Continue), "Overnight" (consolidated / artifacts /
|
||||
failures counters), "Up next" (events/tasks), "Suggested next actions", "Quick capture", and an
|
||||
"Active models" tile.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD §12.1 + Blueprint p.265/447)
|
||||
|
||||
Functional requirements (§12.1):
|
||||
- Greeting with user name + date/time.
|
||||
- Active/recent workspaces ranked by recency and priority, each with one-click Continue.
|
||||
- Overnight summary: memories consolidated, artifacts created, automations completed, failures.
|
||||
- Upcoming meetings/events/tasks ("Up next").
|
||||
- Suggested next actions (memory/session/task/schedule-derived).
|
||||
- Quick capture for note/task/link/file.
|
||||
- Active models + current mode, only if relevant (do not clutter).
|
||||
- Ctrl+K hint/access.
|
||||
|
||||
States to implement (§12.1 "States" + Blueprint line 447):
|
||||
`Loading` · `First-run empty` · `Normal populated` · `Attention required` · `Offline/local-only` ·
|
||||
`Overnight failure` · `Permission denied for shared/team data`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo — disposition: **partial (keep-promote + create-new)**
|
||||
|
||||
PRD §20.1 explicitly: `WorkspaceBriefing.tsx` → "seed for Home Cockpit widgets" (keep and promote).
|
||||
There is **no `HomeCockpit` component today** — it is a §20.3 "Create" item. So the work is: extract
|
||||
reusable widget logic from the two existing briefing surfaces, build a new full-screen `HomeCockpit`,
|
||||
and wire it to a new cross-workspace aggregation endpoint.
|
||||
|
||||
Existing files that feed this screen:
|
||||
|
||||
| File | What it does | Reuse role |
|
||||
|---|---|---|
|
||||
| `apps/web/src/components/os/WorkspaceBriefing.tsx` (283 LOC) | Per-workspace "home screen" inside ChatApp when a chat has no messages. Fetches `GET /api/workspaces/:id/context`; renders greeting, stats bar, pending tasks, recent decisions, "I Remember" memories, recent threads, cross-workspace hints, persona skill chips, suggested prompts, upcoming schedules. Collapse state persisted per-workspace via `lib/workspace-briefing-state`. | **Keep-promote.** Its section components (memory list, pending tasks, decisions, suggested-prompt chips, upcoming schedules) are the widget vocabulary the Cockpit reuses. It is **per-workspace**, so it can't be the Cockpit as-is. |
|
||||
| `apps/web/src/components/os/overlays/LoginBriefing.tsx` (363 LOC) | Modal "I remember…" catch-up shown once after boot. Already does **cross-workspace aggregation** client-side: `getWorkspaces()` → per-workspace `getWorkspaceContext()` (N calls) + `searchMemory('…','global')` + `getMemoryStats()`. Builds workspace summaries (memoryCount/sessionCount/lastActive/summary/pendingTasks), ranked memory highlights (`lib/briefing-highlights`), brag header (`lib/login-briefing-brag`), time-aware greeting + identity name. Has first-run empty hook (3 demo bubbles). Filters E2E/test workspaces. | **Keep-promote (heaviest reuse).** Its data-gathering and ranking is exactly the Cockpit's "You were working on" + greeting + first-run-empty logic — but it does the N+1 fan-out **on the client**, which §20.4 forbids ("do not duplicate backend state calculation in frontend"). Promote this logic **into the new `GET /api/home/briefing`** server route. |
|
||||
| `apps/web/src/components/os/apps/DashboardApp.tsx` | Dock app `home` — Workspaces grid (select/create/open chat), brain-health tier. Currently the thing the `home` dock key opens. | **Rework/keep.** Becomes the workspaces-grid sub-view; Home Cockpit becomes the new default `home` surface. Not the briefing itself. |
|
||||
| `apps/web/src/components/os/apps/CockpitApp.tsx` (315 LOC) | "Cockpit" = system/ops dashboard (health, cost, crons, connectors, weaver, event stats, compliance). Dock id `cockpit`. **Name collision only** — this is the ops Command-Center surface, NOT the PRD Home Cockpit. | **Out of scope / do not conflate.** Maps to PRD §12.3-ish ops, not §12.1. Flag the naming clash. |
|
||||
| `apps/web/src/components/os/cockpit/ComplianceDashboard.tsx` | EU AI Act compliance tiles, hosted inside `CockpitApp`. | Not relevant to S01. |
|
||||
|
||||
Supporting lib (frontend, reuse as-is):
|
||||
`lib/briefing-highlights.ts` (`selectBriefingHighlights` — importance-then-recency ranking),
|
||||
`lib/login-briefing-brag.ts` (`computeBragSummary`/`formatBragLine`/`timeAgo`),
|
||||
`lib/workspace-briefing-state.ts` (collapse persistence), `lib/persona-display.ts`,
|
||||
`lib/skill-recommendations.ts` (persona skill chips). **Note:** `lib/suggested-actions.ts` is for
|
||||
**chat follow-up chips** (regex over the last assistant message) — it is NOT the Home "suggested next
|
||||
actions" generator; do not reuse it for the Cockpit. Suggested actions for Home come from
|
||||
`buildWorkspaceState().nextActions` (backend) aggregated cross-workspace.
|
||||
|
||||
Backend builder already present (keep-promote, §20.1): `packages/server/src/local/workspace-state.ts`
|
||||
`buildWorkspaceState()` → typed `WorkspaceState` (active/openQuestions/pending/blocked/completed/stale/
|
||||
recentDecisions/`nextActions`) with freshness classification. This is the per-workspace next-action
|
||||
engine the Home briefing aggregates over.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Create** `apps/web/src/components/os/apps/HomeCockpitApp.tsx` (new top-level surface; register in
|
||||
`Desktop.tsx` `appConfig` + `renderAppContent`, and make the `home` dock key open it instead of
|
||||
`DashboardApp` — `DashboardApp` demotes to a "Workspaces" grid reachable from a Cockpit tile/Ctrl+K).
|
||||
|
||||
Widget components (extract from the two briefing files so logic is shared, not copy-pasted — CLAUDE.md
|
||||
"many small files"):
|
||||
- `home/GreetingHeader.tsx` — greeting + name + date/time (promote from `LoginBriefing` lines 84-95 +
|
||||
`WorkspaceBriefing` greeting). Reuse `buildTimeAwareGreeting` semantics from backend; client just renders.
|
||||
- `home/RecentWorkspacesPanel.tsx` — "You were working on": ranked workspace cards w/ Continue button →
|
||||
`openChatForWorkspace(id)` (via `useWindowManager`). Promote `LoginBriefing` summaries list (lines 277-334).
|
||||
- `home/OvernightPanel.tsx` — counters (consolidated / artifacts / automations done / failures); failure
|
||||
rows expandable → Automation Center. **New** (no current equivalent).
|
||||
- `home/UpNextPanel.tsx` — upcoming events/tasks/schedules. Reuse `upcomingSchedules` + `pendingTasks`
|
||||
shapes; aggregate across workspaces.
|
||||
- `home/SuggestedActionsPanel.tsx` — top N `nextActions` aggregated from `buildWorkspaceState`, each
|
||||
routing to its workspace. **Not** `lib/suggested-actions.ts`.
|
||||
- `home/QuickCapturePanel.tsx` — note/task/link/file input → `POST /api/quick-capture`. **New.**
|
||||
- `home/ActiveModelsTile.tsx` — promote `ModelPilotCard` / `useProviders`; render only when relevant.
|
||||
|
||||
Data layer:
|
||||
- New hook `hooks/useHomeBriefing.ts` → `adapter.getHomeBriefing()` + `adapter.getOvernight()`, with
|
||||
the §12.1 state machine (loading/empty/populated/attention/offline/overnight-failure/permission-denied).
|
||||
- Extend `lib/adapter.ts` (the single sidecar gateway) with `getHomeBriefing()`, `getOvernight()`,
|
||||
`quickCapture(payload)`. Per the frontend inventory, all PRD §16 endpoints get added here.
|
||||
- First-run empty: reuse `LoginBriefing`'s demo-bubble hook (lines 222-247).
|
||||
- Offline/local-only: reuse `useOfflineStatus`; degrade overnight/team tiles, keep local workspaces.
|
||||
- Ctrl+K hint: surface the existing `GlobalSearch` (Ctrl+K) — already wired in `useKeyboardShortcuts`.
|
||||
|
||||
**Decision required (Open Q):** does `LoginBriefing` (modal) survive alongside Home Cockpit, or does the
|
||||
Cockpit absorb it? They overlap ~80%. Recommend: collapse `LoginBriefing` into the Cockpit's first paint
|
||||
and retire the modal (avoid two catch-up surfaces).
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.1)
|
||||
|
||||
| PRD endpoint | Status | Plan |
|
||||
|---|---|---|
|
||||
| `GET /api/home/briefing` | **MISSING** | **NET-NEW** route (new `routes/home.ts` registered in `local/index.ts`). Server-side aggregation that promotes `LoginBriefing`'s client N+1 fan-out: iterate `workspaceManager` workspaces → reuse per-workspace logic already in `routes/workspaces.ts` `/context` handler (greeting/summary/recentMemories/pendingTasks/upcomingSchedules) + `buildWorkspaceState()` (`workspace-state.ts`) for `nextActions`, ranked by recency/priority. Reuse `briefing-highlights` ranking server-side. **Substrate touched:** `memory_frames` (per-workspace `.mind`), session JSONL, `awareness`, `cron-store`. No new store. **Refactor over EXTEND:** factor the `/context` body into a shared builder so `/context` and `/home/briefing` don't duplicate SQL. |
|
||||
| `POST /api/quick-capture` | **PARTIAL** | **EXTEND** `routes/memory.ts` (or thin new handler delegating to it). Closest existing write is `POST /api/memory/frames`. Quick-capture = thin wrapper: default to **personal** mind, stamp `source: 'quick-capture'`, accept `kind ∈ note|task|link|file`. For `task` also write an `awareness` row so it surfaces in `nextActions`; for `file` route through `POST /api/ingest`. **Substrate:** `memory_frames` (personal `.mind`), `awareness`. No migration. |
|
||||
| `GET /api/home/overnight` | **MISSING** | **NET-NEW** route (same `routes/home.ts`). Aggregate from existing substrates — **no new store**: `cron`/automation runs via `GET /api/cron/:id/history` (`notifications.ts`) + cron-store; consolidation/artifact counts from `events.ts` audit events (`GET /api/events?since=`) and `weaver` status (`/api/weaver/status`); failures from cron history error rows + notifications. Returns `{ consolidated, artifactsCreated, automationsCompleted, failures[] }`. The `Overnight failure` state is driven by `failures.length > 0`. |
|
||||
|
||||
Notes for the implementer:
|
||||
- **Reuse, don't reinvent (§20.4):** the per-workspace catch-up math lives in `routes/workspaces.ts`
|
||||
lines 311-592 and `workspace-state.ts`. Extract the shared body before adding the cross-workspace loop.
|
||||
- **Privacy gate (CRITICAL — already bit us):** cross-workspace content aggregation was previously a
|
||||
privacy leak. `routes/workspaces.ts` lines 553-564 show `crossWorkspaceHints` is **deliberately
|
||||
DISABLED** (returns `[]`) because the original iterated every workspace MindDB and returned content
|
||||
snippets with no grant check (security review: `cowork/Code-Review_MultiMind_April-2026.md` Critical #1).
|
||||
`GET /api/home/briefing` is the SAME pattern (read every workspace) — but for Home it is the user's
|
||||
**own** workspaces, so it is legitimate for personal scope. **Team/shared workspace rows must respect
|
||||
`approvalGrantStore` / team RBAC** (PRD §12.1 `Permission denied for shared/team data` state). Do not
|
||||
leak team-workspace content the caller can't access.
|
||||
- Tier: Home briefing must work on FREE/TRIAL (it's the daily landing surface). Team-overnight rows are
|
||||
TEAMS-gated; gate the team slice, not the whole endpoint.
|
||||
|
||||
**No `.mind` schema migration required.** All three endpoints read existing tables (`memory_frames`,
|
||||
`awareness`, audit `events`) and write only via existing frame/awareness paths. (PRD §15.4 confidence/
|
||||
provenance fields are a *separate, later* migration — not needed for S01.)
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
Today `lib/types.ts` has `WorkspaceContext` (lines 66-85) — per-workspace, already carries
|
||||
greeting/recentMemories/pendingTasks/upcomingSchedules/crossWorkspaceHints. **Reuse its sub-shapes.**
|
||||
|
||||
Add new interfaces (in `lib/types.ts`, mirrored server-side):
|
||||
- `HomeBriefing { greeting; date; recentWorkspaces: RecentWorkspaceCard[]; suggestedActions: SuggestedAction[]; upNext: UpNextItem[]; activeModels?; isFirstRun: boolean }`
|
||||
- `RecentWorkspaceCard { id; name; group; summary?; lastActive; pendingCount; continueSessionId? }`
|
||||
- `OvernightSummary { consolidated: number; artifactsCreated: number; automationsCompleted: number; failures: OvernightFailure[] }`
|
||||
- `OvernightFailure { id; label; automationId?; error; at }`
|
||||
- `QuickCaptureInput { kind: 'note'|'task'|'link'|'file'; content; workspaceId? }`
|
||||
- `SuggestedAction { label; workspaceId; sessionId?; kind }`
|
||||
|
||||
Align kinds with PRD §15.2 (`MemoryKind`, `ArtifactKind`) where they overlap. These are net-new vs the
|
||||
current `lib/types.ts` (which has no Home/Overnight/QuickCapture types).
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **AppShell / IA (Sprint 1, PRD §21):** Home Cockpit is the default landing surface — needs the dock
|
||||
`home` key repointed and the §10 IA buckets settled first.
|
||||
- **Command Center (Ctrl+K) (S03):** §12.1 acceptance "open Ctrl+K from the keyboard" — `GlobalSearch`
|
||||
already exists, so this is a soft dep (hint only).
|
||||
- **Workspace Desktop (S02):** "Continue" routes into the per-workspace runtime; needs `openChatForWorkspace`
|
||||
(already in `useWindowManager`) — soft dep.
|
||||
- **Automation Center (S11):** overnight failure rows link there; can ship Home with the count + a stub link.
|
||||
- Belongs to **Sprint 2 ("Home and workspace state")** per PRD §21 — after shell, alongside the
|
||||
workspace-context/state API extension.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **L**
|
||||
|
||||
Frontend is mostly **promotion** of two existing briefing surfaces into shared widgets (medium), but the
|
||||
backend adds **two net-new aggregation routes** (`/home/briefing`, `/home/overnight`) that must (a)
|
||||
refactor the per-workspace `/context` body into a shared builder to avoid duplication and (b) re-implement
|
||||
the cross-workspace aggregation **safely** behind the grant/RBAC gate that previously caused a privacy
|
||||
leak. The security-sensitive cross-workspace read is what pushes this from M to L. Not XL — no new data
|
||||
store, no schema migration, and the ranking/greeting logic already exists.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **LoginBriefing fate:** retire the modal and absorb its catch-up into Home Cockpit's first paint, or
|
||||
keep both? (~80% overlap; two catch-up surfaces is confusing.)
|
||||
2. **"Overnight" semantics:** time-window = since last app close? since midnight local? last 12 h? Affects
|
||||
the `since=` query for events/cron history.
|
||||
3. **Cross-workspace personal read:** confirm reading the user's *own* workspaces server-side (for the
|
||||
briefing) is acceptable now that it's same-user (the prior leak was content snippets without grant
|
||||
checks). Team/shared rows still gate through `approvalGrantStore`/RBAC — confirm the gate boundary.
|
||||
4. **`home` vs `cockpit` naming:** `CockpitApp` already owns "Cockpit" (ops). PRD calls S01 "Home Cockpit".
|
||||
Final dock/app naming to avoid the collision (proposal: S01 = `home`/"Home"; keep ops as `cockpit`).
|
||||
5. **DashboardApp role:** does the Workspaces grid live as a Cockpit tab, a Ctrl+K destination, or stay a
|
||||
separate dock app? (Affects whether `home` dock key fully repoints to HomeCockpit.)
|
||||
6. **Quick-capture `file` flow:** does a file capture upload into a default/personal store, or prompt for a
|
||||
target workspace? `POST /api/ingest` needs a destination.
|
||||
219
docs/ux-refactor/gap-cards/S02-workspace-desktop.md
Normal file
219
docs/ux-refactor/gap-cards/S02-workspace-desktop.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# Gap Card — S02 · Workspace Desktop
|
||||
|
||||
> Screen S02 of the Waggle OS UX-refactor. PRD §12.2 (lines 412-447). Execution model: **in-place
|
||||
> incremental refactor** of `apps/web` + targeted sidecar extensions. Mockup
|
||||
> (`screen_02_workspace_desktop.png`) is **directional**; PRD acceptance criteria win.
|
||||
> Every claim grounded in repo source (paths cited).
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Purpose (PRD §12.2):** the primary runtime for a single bounded work context. It demotes chat
|
||||
from "the whole product" to **one widget among many**, surfacing workspace state, artifacts, memory,
|
||||
tasks, research, and activity on a single screen, with chat/agent interaction co-resident.
|
||||
|
||||
**Mockup reading** (`Waggle_OS_Handoff_Assets/screen_02_workspace_desktop.png`): a full-screen
|
||||
(non-floating) layout for "Germany GTM Strategy" with:
|
||||
- **Left nav rail** — Workspaces / Memory / Agents / Automations / Files / MCP Hub / Connectors + favourites + user tile.
|
||||
- **Header** — workspace name + status pill ("Active"), team avatar stack, Share button, global search.
|
||||
- **Tab bar** — Overview · Chat · Research · Artifacts · Memory · Tasks · Timeline.
|
||||
- **Main canvas (Overview tab)** — multi-widget grid: AI workspace/chat preview, Key Artifacts list,
|
||||
Tasks list, Memory highlights, Research overview (donut), Recent activity feed.
|
||||
- **Right panel** — Workspace info, Members, Last activity, quick actions.
|
||||
- (Implied) **status bar** — agents running / automations active / MCPs connected.
|
||||
|
||||
> The blueprint text page for "Screen 2 — Workspace Desktop" (`_blueprint_extracted.txt:675`) is an
|
||||
> image-only placeholder; the only textual spec is PRD §12.2 + the §0/§9/§19 spine references
|
||||
> (`_blueprint_extracted.txt:15,42,63,274,513,585`).
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD/Blueprint)
|
||||
|
||||
**Functional requirements (PRD §12.2, lines 423-430):**
|
||||
- Header: workspace name, **type**, **status**, team/avatar stack, share controls.
|
||||
- Tabs: Overview, Chat, Research/Notes, Artifacts, Memory, Tasks, Timeline, Settings.
|
||||
- Main canvas widgets: AI workspace/chat, key artifacts, tasks, memory highlights, research overview, recent activity.
|
||||
- Right panel: workspace info, members, last activity, quick actions.
|
||||
- Bottom/status bar: agents running, automations active, MCPs connected.
|
||||
- Fixed default layout initial release; configurable widgets deferred to a later phase.
|
||||
|
||||
**States (PRD §12.2, lines 432-441):** No memory · Active work · Agent running · Artifact ready ·
|
||||
Task blocked · Sync conflict · Permission denied · Offline.
|
||||
|
||||
**Acceptance criteria (PRD §12.2, lines 443-447):**
|
||||
- Chat is one widget, not the whole product.
|
||||
- Workspace state is always visible.
|
||||
- User can reach memory, artifacts, agents, skills, tasks, automations, and settings from the workspace.
|
||||
|
||||
**Cross-cutting (Blueprint §state-rules):** server-derived state — "Home Cockpit and Workspace
|
||||
Desktop must use server workspace-state/context APIs" (`_blueprint_extracted.txt:513`); cache
|
||||
invalidation on memory import / artifact update / agent-run completion / connector sync / automation
|
||||
completion / RBAC change (`:514`).
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `create-new` (the tabbed Workspace Desktop screen does not exist), reusing existing
|
||||
substrate heavily.** There is **no** workspace-runtime surface today. The closest analog is a
|
||||
chat-only floating window:
|
||||
|
||||
- **`apps/web/src/components/os/Desktop.tsx`** — root OS shell. `renderAppContent()` is a
|
||||
`switch(win.appId)` (`:276-360`); a workspace "opens" only as `case 'chat'` →
|
||||
`<ChatWindowInstance>` (`:278-296`). There is **no `case 'workspace'`** and no tabbed runtime.
|
||||
Apps are floating windows (`AppWindow` chrome), not a full-screen workspace surface. `appConfig`
|
||||
(`:77`) has no workspace entry.
|
||||
- **`apps/web/src/components/os/apps/ChatWindowInstance.tsx`** — per-window wrapper: owns model
|
||||
fetch + per-window persona/autonomy, renders only `<ChatApp>` (`:221-248`). Chat **is** the whole
|
||||
window — the exact inversion PRD §12.2 forbids.
|
||||
- **`apps/web/src/components/os/WorkspaceBriefing.tsx`** — ChatApp "home screen" shown when a session
|
||||
has no messages. Fetches `GET /api/workspaces/:id/context` via `adapter.getWorkspaceContext()`
|
||||
(`:57`) and renders greeting / stats / pending tasks / recent decisions / "I Remember" memories /
|
||||
recent threads / cross-workspace hints / suggested prompts / upcoming schedules. **This is the
|
||||
single best reuse seed** — it already consumes the "Workspace Now" block the Overview tab needs,
|
||||
but it lives *inside* chat and is read-only (no artifacts, no tasks CRUD, no tabs, no right panel).
|
||||
- **`apps/web/src/components/os/apps/DashboardApp.tsx`** — Workspaces grid (select/create/open-chat).
|
||||
This is the workspace **list** (S01-adjacent), not the per-workspace runtime.
|
||||
- **`apps/web/src/components/os/apps/RoomApp.tsx`** — live sub-agent tiles via `useRoomState` SSE
|
||||
(`:24`); the "agents running" status indicator can be derived from this per-workspace.
|
||||
- **`apps/web/src/components/os/overlays/ContextRail.tsx`** — right-side rail for a clicked
|
||||
frame/entity (`ContextRailTarget`); a **reuse target for the right-panel detail pattern**, but it
|
||||
is an overlay keyed to a single frame, not a persistent workspace info/members/activity panel.
|
||||
|
||||
**Tabs that already have a host component** (to embed, not rebuild): Memory → `MemoryApp.tsx`,
|
||||
Timeline → `TimelineApp.tsx`, Chat → `ChatWindowInstance`/`ChatApp`, Settings →
|
||||
`SettingsApp.tsx`. **Tabs with NO host:** Overview (new), Research/Notes (new), Artifacts (no
|
||||
backing entity at all — see §5), Tasks (store exists server-side, never wired to FE — see §4/§5).
|
||||
|
||||
**Adapter gap (confirmed by grep on `apps/web/src/lib/adapter.ts`):** `getWorkspaceContext` (`:272`),
|
||||
`getWorkspaceFiles` (`:277`), `getPins`/`addPin` (`:1353/:1361`), `getDocuments` (`:1371`) exist;
|
||||
**no `getTasks` / `getWorkspaceState` / `getWorkspaceActivity` methods** (0 matches). So the Tasks
|
||||
list, the `/state` Overview source, and the activity feed have no client plumbing yet.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
### Components to create
|
||||
| Component | Role | Reuse / source |
|
||||
|---|---|---|
|
||||
| `apps/WorkspaceApp.tsx` (new) | Full-screen workspace runtime shell: header (name/type/status/avatars/Share) + `Tabs` + right panel + status bar. Owns `activeTab` state. | `components/ui/tabs.tsx`; header layout from `StatusBar.tsx` + `chat-header-layout.ts`; tab gating via `useFeatureGate`. |
|
||||
| `workspace/OverviewTab.tsx` (new) | Default tab: widget grid (chat preview, key artifacts, tasks, memory highlights, research, recent activity). | **Port the read-only sections of `WorkspaceBriefing.tsx`** (greeting/decisions/memories/threads) into widget cards; add artifacts/tasks/activity widgets. |
|
||||
| `workspace/WorkspaceInfoPanel.tsx` (new) | Right panel: info, members, last activity, quick actions. | Members from `adapter.getTeamMembers()`; "last activity" from new activity hook; quick actions raise `waggle:open-app`. ContextRail stays a separate frame-detail overlay. |
|
||||
| `workspace/TasksTab.tsx` (new) | Task list/board for the workspace. | Net-new FE; backs onto existing `/api/workspaces/:id/tasks` (server store exists, FE plumbing missing). |
|
||||
| `workspace/ResearchTab.tsx` (new) | Research/Notes surface. | Lightweight: notes-as-frames (memory) + wiki pages (`adapter.getWikiPages`). Lowest-fidelity tab; can ship as "notes" v1. |
|
||||
| `workspace/ArtifactsTab.tsx` (new) | Artifacts grid (see S05 dependency). | Backs onto new `/api/artifacts` (net-new) or interim file-registry view via `getWorkspaceFiles`/`getDocuments`. |
|
||||
|
||||
### Components to rework / wire
|
||||
- **`Desktop.tsx`** — add `case 'workspace'` to `renderAppContent` + an `appConfig.workspace`
|
||||
entry; route `openChatForWorkspace` callers that should open the *desktop* (not a chat window) to a
|
||||
new `openWorkspace(workspaceId)`. Decide window-vs-fullscreen (recommend full-bleed window using
|
||||
existing maximize path in `AppWindow.tsx` to avoid a parallel layout system). **Surgical** — do
|
||||
not refactor the window manager.
|
||||
- **`useWindowManager.ts`** — add `workspace` to the `AppId` consumption; reuse `workspaceId` field
|
||||
already on `WindowState`. No new state shape.
|
||||
- **Embed existing apps as tab panels** — Memory/Timeline/Chat/Settings render their existing
|
||||
components scoped by `workspaceId` (already accepted props on `TimelineApp`, `ChatWindowInstance`).
|
||||
|
||||
### Adapter methods / hooks to add (`lib/adapter.ts` — the one contract surface)
|
||||
- `getWorkspaceState(id)` → `GET /api/workspaces/:id/state` (Overview; PARTIAL backend, §5).
|
||||
- `getWorkspaceActivity(id, {limit})` → `GET /api/workspaces/:id/activity` (activity feed; PARTIAL, §5).
|
||||
- `getTasks(workspaceId)` / `createTask` / `updateTask` / `deleteTask` → `/api/workspaces/:id/tasks*`
|
||||
(routes EXIST, adapter methods MISSING).
|
||||
- New hook `useWorkspaceDesktop(workspaceId)` composing state + activity + tasks + members +
|
||||
fleet (agents-running) + cron (automations) + capabilities/status (MCPs-connected) for the status
|
||||
bar. Reuse `useRoomState` for live agents.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work
|
||||
|
||||
Per PRD §16.2 plus the substrate the Overview/widgets/status-bar need. Cross-referenced against
|
||||
backend-routes inventory + backend-map `03c`.
|
||||
|
||||
| PRD §16 endpoint | Status | Extend vs net-new · substrate · migration |
|
||||
|---|---|---|
|
||||
| `GET /api/workspaces/:id` | **EXISTS** | `workspaces.ts`. Header name/team. But `type`+`status` fields are **MISSING** on `WorkspaceConfig` (`hive-mind-core/src/workspace-manager.ts:5-58`) — additive JSON fields, **no DB migration** (workspace.json file). Default `status:'active'`; derive `type` from `templateId`/`group`. |
|
||||
| `PATCH /api/workspaces/:id` | **EXISTS** | `workspaces.ts`. Used for status change / Share controls. Stamp `updatedAt` in `update()` (`workspace-manager.ts:222`, currently unstamped). |
|
||||
| `GET /api/workspaces/:id/context` | **EXISTS** | `workspaces.ts:311` — the "Workspace Now" block (`buildWorkspaceNowBlock()`, `workspace-context.ts:191-404`). Direct feed for Overview widgets (greeting/decisions/memories/threads/pending/schedules). |
|
||||
| `GET /api/workspaces/:id/state` | **PARTIAL → EXTEND** | No `/state` route. `buildWorkspaceState()` (`workspace-state.ts:234-311`) already produces `active/openQuestions/pending/blocked/completed/stale/recentDecisions/nextActions` and is surfaced *inside* `/context` as `workspaceState`. Add a thin `/state` route returning that sub-object directly (Overview/Tasks consume `pending`+`blocked` as task seeds). Substrate: `memory_frames` + session JSONL + `awareness`. No migration. |
|
||||
| `GET /api/workspaces/:id/activity` | **PARTIAL → EXTEND** | No per-workspace `/activity`. Closest: `GET /api/events?workspaceId=` (`events.ts`) and `GET /api/teams/:id/activity`. Add a thin `/activity` alias over the audit-event query (substrate: `ai_interactions`/`execution_traces`/events). No migration. |
|
||||
| Tasks: `GET/POST /api/workspaces/:id/tasks`, `PATCH/DELETE …/:taskId` | **EXISTS (server)** | `tasks.ts` (backend-routes §1.7). Only FE plumbing missing — no net-new backend. |
|
||||
| Status bar feeds | **EXISTS, no aggregate** | agents → `GET /api/fleet` (`fleet.ts`); automations → `GET /api/cron` (`cron.ts`); MCPs connected → `GET /api/capabilities/status` (`mcpServers[]`) / `install_audit`. Compose client-side in v1; an aggregate `/status` route is optional. |
|
||||
| Members / Share | **PARTIAL** | Members → `GET /api/team/members` (EXISTS). **Share** → PRD §16.11 `POST /api/share` is **MISSING** (grep-confirmed, backend-routes §16.11). Header Share button is net-new backend; for non-team workspaces it can be a no-op/disabled in v1. |
|
||||
| Artifacts widget/tab | **MISSING (largest gap)** | No `Artifact` entity, table, or `/api/artifacts*` route anywhere (substrate-types §e; backend-routes §16.6). Interim: render the **file registry** `GET /api/workspaces/:id/files` (`workspaces.ts`) + document versions `GET /api/workspaces/:id/documents` (`documents.ts`) as "artifacts". Full Artifact Center is **S05's** scope — S02 should depend on it, not build it. |
|
||||
|
||||
**Migration flag:** the only `.mind` SQLite migration *adjacent* to this screen is the optional
|
||||
`memory_frames.metadata`/`confidence`/`kind` additions (substrate-types §c) needed for richer Memory
|
||||
**filters** — **not required for S02's Overview/Tasks/Timeline tabs**; defer to S04 (Memory Center).
|
||||
S02's own missing fields (`type`, `status`, `updatedAt`, `lastActiveAt`) are JSON-file additive — **no
|
||||
DB migration**.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
- **`WorkspaceType`, `Scope`, `Confidence` literal unions** (PRD §15.2) — **none exist** in
|
||||
`apps/web/src/lib/types.ts` (substrate-types §e). Add `WorkspaceType` + `WorkspaceStatus`
|
||||
(`active`/`paused`/`archived`) for header.
|
||||
- **`Workspace` → `WorkspaceConfigV2` alignment** (PRD §15.3) — FE `Workspace` (`types.ts:22-40`)
|
||||
lacks `type`, `status`, `description`, `updatedAt`, `lastActiveAt` and uses `persona` vs config's
|
||||
`personaId`. Add the 5 missing fields (optional) for header/last-activity; keep DERIVED display
|
||||
fields.
|
||||
- **`WorkspaceState` type** — exists server-side (`workspace-state.ts:38-55`) but is **not mirrored**
|
||||
in FE `types.ts`. Add a FE `WorkspaceState` mirror for the new `getWorkspaceState` method.
|
||||
- **`Task` type** — `lib/types.ts` has no Task interface (server `tasks.ts` shape only). Add one for
|
||||
the Tasks tab.
|
||||
- **`Artifact` type** — **greenfield** (PRD §15.6). Owned by S05; S02 imports it once defined.
|
||||
- `WorkspaceContext` already exists in `types.ts` (consumed by `WorkspaceBriefing`) — reuse for Overview.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **Phase 1 (Shell + Ctrl+K)** — `_blueprint_extracted.txt:582` — must land first: this screen is
|
||||
opened *from* the shell/route map and the left nav. S02 needs the new `route`/`activeWorkspaceId`
|
||||
global state + the `case 'workspace'` shell wiring.
|
||||
- **Phase 2 (Home + Workspace)** — `_blueprint_extracted.txt:585` — S02 ships **with** S01 (Home
|
||||
Cockpit); both share `/api/workspaces/:id/context` + `/state`. S01's cross-workspace briefing
|
||||
reuses S02's per-workspace builder.
|
||||
- **S05 Artifact Center** — hard dependency for the Artifacts tab/widget (entity + `/api/artifacts*`
|
||||
are entirely net-new there). S02 must ship the Artifacts tab as an **interim file-registry view**
|
||||
if S05 is not ready.
|
||||
- **S04 Memory Center** — the Memory tab embeds `MemoryApp`; richer confidence/kind filters (and the
|
||||
`memory_frames` metadata migration) live there, not in S02.
|
||||
- **S03 Command Center (Ctrl+K)** — the header search + quick actions route through the command provider.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort
|
||||
|
||||
**XL.** Net-new full-screen tabbed runtime that re-architects the product's primary surface (chat →
|
||||
one-widget), needs 5-6 new FE components + several adapter methods/hooks, 2 thin backend routes
|
||||
(`/state`, `/activity`) + workspace `type`/`status`/`updatedAt`/`lastActiveAt` additive fields, and
|
||||
is gated on S05 (Artifacts) for one full tab. The read-only Overview seed exists
|
||||
(`WorkspaceBriefing`), which keeps it from being 2×XL, but the shell-integration + tab embedding +
|
||||
status-bar aggregation breadth dominate.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Window vs full-screen.** Should the Workspace Desktop be a maximized `AppWindow` (reuse existing
|
||||
window manager + chrome) or a dedicated full-bleed route bypassing the floating-window system? The
|
||||
mockup is full-screen; the current OS is windowed. Recommend maximized-window to avoid a parallel
|
||||
layout system — needs founder/eng confirmation.
|
||||
2. **Chat-as-widget vs Chat-tab.** PRD says "chat is one widget" (Overview) AND lists a "Chat" tab.
|
||||
Is the Overview chat widget a live mini-composer or a read-only preview that deep-links to the Chat
|
||||
tab? Affects whether `ChatApp` must run in two render modes.
|
||||
3. **`workspace.type` taxonomy.** PRD §15.2 names `WorkspaceType` but no enum values are given. Derive
|
||||
from the 15 existing `workspace-templates` categories, or define a new fixed set?
|
||||
4. **`status` lifecycle.** Who/what sets `paused`/`archived` (manual header action vs automation)?
|
||||
PRD §12.2 lists the status states but not the transitions.
|
||||
5. **Share scope.** `POST /api/share` is net-new (PRD §16.11). Is Share in S02's MVP, or stubbed
|
||||
until Team Workspace (S10)? For a solo/non-team workspace, what does Share do?
|
||||
6. **Tasks store of record.** Tasks tab — back onto the existing `/api/workspaces/:id/tasks` store, or
|
||||
model tasks as `pending`/`blocked` `StateItem`s from `WorkspaceState` (which are session/awareness-
|
||||
derived, not first-class)? These are two different sources of truth to reconcile.
|
||||
182
docs/ux-refactor/gap-cards/S03-command-center.md
Normal file
182
docs/ux-refactor/gap-cards/S03-command-center.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Gap Card — S03 · Command Center (Ctrl+K)
|
||||
|
||||
> Execution model is the LOCKED **in-place incremental refactor** of `apps/web` + targeted
|
||||
> backend extensions. Mockup (`Waggle_OS_Handoff_Assets/screen_03_win_k_command_center.png`) is
|
||||
> DIRECTIONAL only; PRD §12.3 acceptance criteria win. Every claim below is grounded in a real file.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
The universal command layer — opens from anywhere with Ctrl+K / Cmd+K — for **search, launch,
|
||||
create, run, navigate, and extend** across every major object type. PRD §12.3 (lines 449-482),
|
||||
blueprint screen 3 (`_blueprint_extracted.txt:284-290, 633, 679`). It is the product's "primary
|
||||
interaction" per blueprint line 19 ("Primary interaction: Ctrl+K") and the IA spine item #3
|
||||
(PRD line 19). Goal: "No user needs to know where a feature lives to use it" (PRD line 482).
|
||||
|
||||
The mockup shows a centered modal titled "What do you want to do?" with: a search input
|
||||
("Search anything…"), pill tabs (Show my tasks / Open … STM Strategy / Find market analysis /
|
||||
Summarize…), five category columns — **Launch · Create · Run · Navigate** plus a Search bucket —
|
||||
each listing object/action rows, a "Suggested for you" card strip (review reports, draft proposals),
|
||||
and a footer "Type a natural-language command…". A second "Open in new window" affordance is top-right.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD/Blueprint)
|
||||
|
||||
PRD §12.3 functional requirements (lines 461-467):
|
||||
- Opens from anywhere via Ctrl+K / Cmd+K.
|
||||
- Searches across **workspaces, memory, artifacts, sessions, people, agents, skills, commands, connectors, MCPs**.
|
||||
- Category sections: **Search, Launch, Create, Run, Navigate, Extend**.
|
||||
- Supports **natural-language command input**.
|
||||
- Displays **recent and suggested** actions.
|
||||
- **Permission-gated actions show an approval prompt before execution.**
|
||||
- Reachable by both mouse and keyboard.
|
||||
|
||||
PRD §12.3 states (lines 469-477) + blueprint (`:289-290`):
|
||||
Idle · Query active · Grouped results · No results · Permission prompt · Command success · Command failure.
|
||||
|
||||
Acceptance (lines 479-482): every major object and action is reachable; no user needs to know where a feature lives.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `rework`** (keep the proven overlay shell + keyboard nav + adapter calls; restructure
|
||||
categories to Search/Launch/Create/Run/Navigate/Extend; add execute + permission-prompt + recent/suggested;
|
||||
back it with the net-new `/api/command/*` provider).
|
||||
|
||||
- **`apps/web/src/components/os/overlays/GlobalSearch.tsx`** (362 lines) — the existing Ctrl+K/Cmd+K
|
||||
overlay. Today it does **client-side federated search only** across 5 categories
|
||||
(`SearchCategory = 'command' | 'workspace' | 'memory' | 'session' | 'skill'`, line 15):
|
||||
- `command` = a **hardcoded static `COMMANDS` array** of 23 app ids (lines 40-64) that must be
|
||||
hand-kept in sync with `appConfig` in `Desktop.tsx` (the file's own comment flags this drift as
|
||||
"the cause of FR #13", lines 33-39).
|
||||
- `workspace`/`session`/`skill` pre-fetched on open via `adapter.getWorkspaces()`,
|
||||
`adapter.getSessions(ws.id)` (first 5 ws × 3 sessions, lines 98-127), `adapter.getSkills()` (lines 129-138).
|
||||
- `memory` = debounced (300 ms, min 2 chars) server call `adapter.searchMemory(query, globalScope?'global')`
|
||||
(lines 142-168) — the **only** server-backed category.
|
||||
- Matching is client-side `fuzzyMatch` (`lib/fuzzy-match`, line 11/177-187); keyboard nav (↑/↓/Enter/Esc),
|
||||
selection clamp, scroll-into-view all already work (lines 224-239).
|
||||
- There is **no execute path** — every Enter calls `onNavigate(category, id)` (lines 231-235, 320),
|
||||
i.e. it only *opens a window*. No Create/Run/Extend, no recent, no suggested, no permission prompt.
|
||||
- **`Desktop.tsx` wiring** — `<GlobalSearch open onClose onNavigate={handleSearchNavigate} />` (`:464`),
|
||||
toggled by `ov.toggleGlobalSearch` (Ctrl/Cmd+K via `useKeyboardShortcuts`, `:207`) and the StatusBar
|
||||
search button (`:424`). `handleSearchNavigate` (`Desktop.tsx:217-225`) handles only `command`→`wm.openApp`,
|
||||
`workspace`→`selectWorkspace`+`openChatForWorkspace`, `memory`→`openApp('memory')`. **No `session`/`skill`
|
||||
navigation, no execute.**
|
||||
- **`apps/web/src/components/os/overlays/KeyboardShortcutsHelp.tsx`** (92 lines) — static cheat-sheet
|
||||
modal (`shortcuts` array, lines 9-32). Lists "⌘K → Global Search" (line 21). **Disposition `keep`** —
|
||||
it is the separate `Cmd+?` help overlay, not the command center; only update its label if Ctrl+K is
|
||||
rebranded "Command Center".
|
||||
- **`adapter.executeCommand(command, workspaceId)`** (`lib/adapter.ts:1345-1350`) → `POST /api/commands/execute`
|
||||
(note **plural** `commands`). Runs **slash commands only** (`/catchup`, `/status`, `/memory`, `/skills`);
|
||||
workflow/spawn commands return "not available" (`packages/server/src/local/routes/commands.ts:1-87`).
|
||||
Exists but is **not** the generic palette execute.
|
||||
- **`adapter.searchSessions`** (`adapter.ts:424-427`), `adapter.searchMemory` (`:476-487`) exist and are
|
||||
reusable as federation inputs. `lib/suggested-actions.ts` is **chat-message-scoped** (extracts follow-up
|
||||
chips from the last assistant message) — NOT command-palette suggestions; do not reuse for §12.3 "suggested".
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Rework `GlobalSearch.tsx` → `CommandCenter.tsx`** (rename or keep filename; founder-directional).
|
||||
Reuse 100% of the modal chrome, framer-motion animation, debounce, keyboard-nav, selection/scroll logic.
|
||||
|
||||
Concrete changes:
|
||||
- **Expand `SearchCategory`** to the PRD §12.3 verbs: add `'create'`, `'run'`, `'navigate'`, `'extend'`
|
||||
alongside `'search'` (today's `command/workspace/memory/session/skill` become **Search/Navigate** result
|
||||
feeds). Category headers + ordering (`CATEGORY_LABELS`/`CATEGORY_ORDER`, lines 67-75) extend accordingly —
|
||||
this matches the mockup's Launch/Create/Run/Navigate columns.
|
||||
- **Replace client federation with a server provider.** Swap the multi-call client logic (lines 91-168)
|
||||
for a single debounced `adapter.commandSearch(q, { scope })` → `GET /api/command/search?q=` (net-new
|
||||
adapter method on `lib/adapter.ts`, the one contract surface). Keep the existing per-category calls only
|
||||
as offline/fallback. Eliminates the hardcoded `COMMANDS`-vs-`appConfig` drift (FR #13).
|
||||
- **Add an execute path.** New `onExecute(commandId|nlInput)` → `adapter.commandExecute()` →
|
||||
`POST /api/command/execute`. On Enter for a Run/Create/Extend item (vs Navigate items which keep calling
|
||||
`onNavigate`). Render the §12.3 **permission prompt** state inline before executing gated actions
|
||||
(reuse the approvals pattern — `useChat().pendingApproval`/`approveAction` and `ApprovalRequest` type,
|
||||
`lib/types.ts`), plus **command success / command failure** toasts (`hooks/use-toast`).
|
||||
- **Recent + Suggested.** On idle (empty query), render two strips matching the mockup: "Recent" from
|
||||
`adapter.commandRecent()` → `GET /api/command/recent`, and "Suggested for you" from
|
||||
`adapter.commandSuggestions()` → `GET /api/command/suggestions`.
|
||||
- **Natural-language input** (PRD line 464): when the query doesn't match a structured result, surface a
|
||||
"Run as command: '<query>'" row that posts the raw string to execute.
|
||||
- **New adapter methods** (4): `commandSearch`, `commandExecute`, `commandRecent`, `commandSuggestions`.
|
||||
- **State**: keep local `query/selected/sections`; add `recent`, `suggested`, `pendingPermission`,
|
||||
`executing` states. Props: extend `GlobalSearchProps` (line 27) with `onExecute` and a workspace-id
|
||||
for execute scoping. New optional hook `useCommandCenter()` (wraps the 4 adapter calls + debounce) is the
|
||||
clean home for the provider, mirroring the existing domain-hook pattern.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.3)
|
||||
|
||||
All four are MISSING from the sidecar (grep-confirmed in `backend-routes.md:448-451`; `/api/command/*` =
|
||||
0 matches in `packages/server/src/local/routes/*.ts`). None needs a new data store — all federate over
|
||||
existing substrate, consistent with the in-place model.
|
||||
|
||||
| PRD §16.3 endpoint | Status | Plan |
|
||||
|---|---|---|
|
||||
| `GET /api/command/search?q=` | **MISSING** | **NET-NEW** route file `packages/server/src/local/routes/command.ts`. Federates over existing reads: workspaces (`WorkspaceManager.list()`), memory (`MindDB` full-text, reuse `memory.ts` search), sessions (reuse `sessions.ts` `/sessions/search`), skills (`skills.ts`), connectors (`connectors.ts`), MCPs (from `capabilities/status` `mcpServers[]` + `@waggle/shared` `mcp-catalog.ts`), agents/personas (`personas.ts`+`agent-groups.ts`), and the app/command catalog (extract from a shared catalog to kill the `COMMANDS` drift). Substrate touched: read-only across `memory_frames`, sessions JSONL, install-audit/marketplace, workspace configs. **No `.mind` migration.** |
|
||||
| `POST /api/command/execute` | **PARTIAL** | EXTEND, do **not** duplicate. `POST /api/commands/execute` (plural, `commands.ts`) already runs slash commands with a real `CommandContext`. Either (a) add a singular `/api/command/execute` alias that broadens the context to also dispatch Navigate/Create/Run/Extend intents (open app, create object, fleet-spawn, install), or (b) widen the existing plural route. Reuses `commandRegistry.execute`, `fleet/spawn`, `marketplace/install`. Substrate: same as the underlying action; add an **install-audit** write for gated executes (`InstallAuditStore.record`, `core/install-audit.ts`). **No migration.** |
|
||||
| `GET /api/command/recent` | **MISSING** | **NET-NEW** (or derive client-side). Cheapest server path: record executed commands to `ai_interactions` / a small recents list and read back; or derive from session/event history (`events.ts`). Substrate: `ai_interactions` table (read) — no schema change required. |
|
||||
| `GET /api/command/suggestions` | **MISSING** | **NET-NEW**. Reuse the **workspace-state next-actions** seed: `deriveNextActions` in `packages/server/src/local/workspace-state.ts:182-218` + `buildWorkspaceNowBlock` (`workspace-context.ts`) give cross-workspace suggestion candidates; `skills.ts` `/skills/suggestions` is the skill-domain analog to fold in. Substrate: read-only over memory/awareness/cron. **No migration.** |
|
||||
|
||||
Permission gating: the execute route should run gated actions through the existing approval/SecurityGate
|
||||
path so the FE permission-prompt state has a real backend (reuse `approval.ts` + marketplace SecurityGate).
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs lib/types.ts)
|
||||
|
||||
PRD §15 defines no dedicated Command type, but the palette needs a result/command union. Add to
|
||||
`apps/web/src/lib/types.ts` (and mirror server-side in the route):
|
||||
- `CommandResult { id; kind: 'search'|'launch'|'create'|'run'|'navigate'|'extend'; objectType: 'workspace'|'memory'|'artifact'|'session'|'person'|'agent'|'skill'|'connector'|'mcp'|'command'|'app'; title; subtitle?; icon?; score; requiresApproval?: boolean; payload? }` — supersedes the local `SearchResult` interface (`GlobalSearch.tsx:17-25`).
|
||||
- `CommandExecuteRequest`/`CommandExecuteResult` (success/failure + optional permission descriptor).
|
||||
- Reuse existing `ApprovalRequest` (`lib/types.ts`) for the permission-prompt state rather than inventing a new one.
|
||||
- The `objectType` union overlaps PRD §15.2's missing unions (`ArtifactKind`, `AgentType`) and the absent
|
||||
Artifact entity (substrate-types §e) — Artifact/Agent results are blocked until those screens land (see §7).
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **PRD Sprint 3 = Command Center** (PRD lines 1321-1326: indexed search provider, result groups,
|
||||
command execution, recent/suggested) — this card IS Sprint 3. Depends on **Sprint 1 (Shell + Ctrl+K**,
|
||||
blueprint `:582`) being the home of the command provider.
|
||||
- **Search breadth is gated by other screens' substrate.** "artifacts" and "agents/people" facets need:
|
||||
Artifacts (S05, PRD §16.6 — entirely net-new, substrate-types §e: no Artifact entity exists) and
|
||||
Agents (S09, PRD §16.7 — sidecar agent CRUD MISSING). Ship Command Center with the **available** facets
|
||||
(workspaces/memory/sessions/skills/commands/connectors/MCPs) and add artifact/agent facets when those
|
||||
screens land. Do not block the whole screen on them.
|
||||
- Execute's Create/Run/Extend intents lean on existing fleet-spawn / marketplace-install / cron — already present.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort
|
||||
|
||||
**L.** The FE overlay is largely reusable (shell, keyboard nav, debounce all done — that caps it below XL),
|
||||
but the work spans: 4 net-new/extended backend routes federating across ~8 substrates, an execute +
|
||||
permission-prompt path with audit writes, recent/suggested providers, a shared `CommandResult` type, and
|
||||
killing the `COMMANDS`/`appConfig` drift — full-stack across multiple existing route files.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Route naming:** add singular `/api/command/*` (PRD-literal) as the new surface, or rename the existing
|
||||
plural `/api/commands/execute`? Plural is already referenced by `adapter.executeCommand` + `commands.ts`.
|
||||
Recommend: new singular `command.ts` + alias execute to the existing registry to avoid a breaking rename.
|
||||
2. **"Open in new window"** affordance in the mockup (top-right) — is the Command Center expected to also
|
||||
open as a persistent windowed app (an `AppId`), or is it modal-only? Affects whether it needs an
|
||||
`appConfig`/dock entry vs staying an overlay.
|
||||
3. **Suggested/recent scope:** cross-workspace blended, or scoped to the active workspace? PRD says
|
||||
"recent and suggested" without scope; `deriveNextActions` is per-workspace today.
|
||||
4. **Natural-language commands** (PRD line 464): heuristic intent-parse (cheap, deterministic) vs an LLM
|
||||
round-trip? The existing `commands.ts` is registry-keyed; NL needs an intent resolver — confirm budget.
|
||||
5. **Permission prompt reuse:** is the chat approvals pipeline (`approval.ts` + `useChat.pendingApproval`)
|
||||
the intended mechanism for palette-initiated gated actions, or a lighter inline confirm?
|
||||
6. The `COMMANDS`-vs-`appConfig` drift (FR #13) — confirm the shared app-catalog refactor is in-scope here
|
||||
vs a separate cleanup (it is the right place to consolidate `AppId` and retire stale `AppView`).
|
||||
267
docs/ux-refactor/gap-cards/S04-memory-center.md
Normal file
267
docs/ux-refactor/gap-cards/S04-memory-center.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# Gap Card — S04 Memory Center
|
||||
|
||||
> Screen S04 of the Waggle OS UX Refactor. Execution model: **in-place incremental
|
||||
> refactor** of `apps/web` + targeted backend extension over the existing `.mind`
|
||||
> substrate. Mockup is directional; PRD §12.4 + §16.4 acceptance criteria win.
|
||||
> Sources: PRD `docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`
|
||||
> (§12.4 lines 484-517, §16.4 lines 1086-1094, §15.4 lines 988-1013, §20.2 line 1276);
|
||||
> backend-map `sections/03b-api-memory.md`, `sections/05b-subsystem-memory.md`;
|
||||
> inventories under `docs/ux-refactor/_inventory/`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
Make memory **visible, trustworthy, searchable, and editable** (PRD §12.4). Every
|
||||
memory item must answer: *what do I know, why do I know it, where did it come from,
|
||||
how confident am I, can I edit it?* (PRD line 517).
|
||||
|
||||
Mockup (`screen_04_memory_center.png`, directional) shows a 3-pane layout:
|
||||
- **Left rail** — search box, type/source/confidence/workspace/tag/date filter facets, scope tabs.
|
||||
- **Center list** — memory cards (title, type icon, snippet, confidence/importance, source chip, date).
|
||||
- **Right detail panel** — selected memory: content, type/source/evidence, confidence ring/score,
|
||||
related memories, connected-graph mini-view, source-evidence chips, and an actions row
|
||||
(edit / merge / archive / delete / share / add-to-workspace). A right-most column in the
|
||||
mockup shows aggregate stats (frame count, distribution donut, top entities, recent activity).
|
||||
|
||||
This is the **Work-layer** "visible memory" pillar (PRD §1, §6 principle 2, §10.2).
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD/Blueprint)
|
||||
|
||||
**Tabs (PRD §12.4):** `Active · Workspace · Team · Sources · Graph · Trash`.
|
||||
|
||||
**Filters (PRD §12.4):** type, source, confidence, importance, workspace, tag, date.
|
||||
|
||||
**Memory detail view fields (PRD §12.4 line 500):** content, type, source, evidence,
|
||||
confidence, relevance, last used, tags, timeline, connected graph.
|
||||
|
||||
**Actions (PRD §12.4 line 502):** edit, merge, archive, delete, share, add to workspace/team;
|
||||
show related memories + source evidence.
|
||||
|
||||
**Screen states (PRD §12.4 lines 504-513 + global §14.1/§14.4):** Empty memory · Importing ·
|
||||
Consolidating · Active · Low confidence · Conflict · Deprecated · Source unavailable —
|
||||
plus the global set Loading / Populated / Error / Offline-local-only / Permission-denied /
|
||||
Partial-data / Approval-required, and the memory lifecycle states Raw/Imported/Working/
|
||||
Consolidated/Active/Low-confidence/Conflicting/Deprecated/Archived/Deleted-tombstoned (§14.4).
|
||||
|
||||
**Acceptance (PRD line 517):** every memory exposes content + source + provenance + confidence
|
||||
+ edit/delete. Privacy (§18.2): memory source and scope must be visible; user must be able
|
||||
to archive/delete; team sharing requires explicit scope.
|
||||
|
||||
> Blueprint note: `_blueprint_extracted.txt` page 31 ("Screen 4 - Memory Center") is an
|
||||
> image-only heading — no extra written spec beyond PRD. Cross-refs confirm the rework
|
||||
> intent: line 571 "Turn `MemoryApp.tsx` into Memory Center with source/confidence/
|
||||
> evidence/edit actions", line 296 "Visible memory with provenance and [confidence]".
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: REWORK** (matches PRD §20.2 line 1276: `MemoryApp.tsx -> Memory Center`).
|
||||
Substantial reuse of substrate + hook; the screen's tab axis, detail panel, and trust
|
||||
surfacing are rebuilt.
|
||||
|
||||
**Primary component — `apps/web/src/components/os/apps/MemoryApp.tsx` (343 LOC).**
|
||||
Today it is a 6-tab hub on a *different* axis than the PRD:
|
||||
- `MEMORY_TABS` (line 46): `timeline | graph | harvest | weaver | wiki | evolution` — NOT the
|
||||
PRD's `Active/Workspace/Team/Sources/Graph/Trash`. Only **Graph** overlaps 1:1.
|
||||
- Left sidebar (line 119): search input (`onSearchChange`), a type-filter chip set
|
||||
(`FRAME_TYPES`, line 21) + a min-importance range slider; flat chronological frame list.
|
||||
- Detail pane (line 281): renders type icon, title, `type` chip, `importance: N/5`, timestamp,
|
||||
markdown content, and a raw `metadata` JSON dump (line 310). **Edit button is a no-op
|
||||
(line 289 — no handler); only Delete is wired.** No source, no confidence, no evidence,
|
||||
no relevance, no related-memories, no tags, no timeline, no merge/archive/share.
|
||||
- `readFrameProvenanceTool()` (line 32) already reads `metadata.tool|sourceTool|source` and
|
||||
renders a small amber provenance badge (line 202) — the **only** trust signal present today.
|
||||
- `ImportReminderBanner` (line 236) handles the "you have pending imports" nudge.
|
||||
|
||||
**Subcomponents — `apps/web/src/components/os/apps/memory/` (6 files):**
|
||||
- `KnowledgeGraphViewer.tsx` → PRD **Graph** tab (keep-promote, reuse as-is).
|
||||
- `HarvestTab.tsx` → maps to **Sources** tab inputs (keep; Sources tab wraps/extends it).
|
||||
- `WeaverPanel.tsx`, `WikiTab.tsx`, `EvolutionTab.tsx`, `ImportReminderBanner.tsx` → these
|
||||
are **out of the PRD Memory-Center tab set**. Per inventory `frontend.md` (f), Weaver/Wiki/
|
||||
Evolution belong to the Intelligence layer (traces/distillation). The rework should **move
|
||||
them off the Memory-Center tab bar** (relocate to their IA home or keep behind a secondary
|
||||
surface) rather than delete — flag as IA cleanup, not in-scope deletion.
|
||||
- Grep confirmed: **no file under `memory/` renders confidence/evidence/provenance** beyond the
|
||||
inline badge in `MemoryApp.tsx`. The trust UI is greenfield.
|
||||
|
||||
**Hook — `apps/web/src/hooks/useMemory.ts` (81 LOC).** Provides `frames`, `selectedFrame`,
|
||||
`filters{types,minImportance,searchQuery}`, `addFrame/editFrame/deleteFrame/incrementAccess/
|
||||
refresh`, `stats{total,filtered,entities,relations}`. Calls `adapter.getMemoryFrames`,
|
||||
`searchMemory`, `getMemoryStats`, `addMemoryFrame`, `updateMemoryFrame`, `deleteMemoryFrame`,
|
||||
`incrementFrameAccess`. **Reuse and extend** (add merge/archive, scope/source/confidence/tag
|
||||
filters, single-frame fetch).
|
||||
|
||||
**Wiring — `Desktop.tsx`:** `const memory = useMemory(activeWorkspaceId)` (line 107);
|
||||
`<MemoryApp .../>` rendered for appId `memory` (lines 309-316) with `frames/selectedFrame/
|
||||
searchQuery/stats/typeFilters/minImportance` + KG props (`knowledgeGraph`, `kgScope`). The
|
||||
appId `memory` already exists in `AppId` (dock-tiers) — no new route needed (single-route
|
||||
windowed desktop; inventory `frontend.md` §b).
|
||||
|
||||
**Frontend type — `apps/web/src/lib/types.ts:118-127` `MemoryFrame`:** `id,type,title,content,
|
||||
importance:number,timestamp,workspaceId,metadata?`. Note inventory finding: this FE shape does
|
||||
**not** match what `/api/memory/frames` returns (server `normalizeFrame` emits `source,
|
||||
source_mind, frameType, accessCount, score, …`, `memory.ts:25-58`) — a real FE/BE contract
|
||||
mismatch to reconcile in this rework.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Rework `MemoryApp.tsx` → Memory Center** (keep file path per §20.2; do not create a parallel app).
|
||||
|
||||
Components to create/rework (small files, per house rules):
|
||||
1. **`MemoryApp.tsx` (rework shell)** — replace the 6-tab bar with PRD tabs
|
||||
`Active | Workspace | Team | Sources | Graph | Trash`. Keep the 3-pane layout
|
||||
(rail / list / detail). State: `activeTab`, `filters`, `selectedFrameId`, `view`.
|
||||
2. **`memory/MemoryFilterRail.tsx` (new)** — facet filters: type, source, confidence range,
|
||||
importance, workspace, tag, date. Drives `useMemory().setFilters`.
|
||||
3. **`memory/MemoryList.tsx` (new)** — extract the card list out of `MemoryApp`; each card shows
|
||||
title, type icon, snippet, confidence badge, source chip, date. Loading/empty/error states.
|
||||
4. **`memory/MemoryDetailPanel.tsx` (new)** — the PRD detail view: content + type + source +
|
||||
`evidence[]` chips + `ConfidenceBadge` + relevance + last-used + tags + mini-timeline +
|
||||
connected-graph snippet + related-memories list. Actions row: edit, merge, archive, delete,
|
||||
share, add-to-workspace/team. Reuse design-system primitives from PRD §19.1
|
||||
(`Confidence badges`, `Source/evidence chips`, `Status badges`, `Timeline`, `Detail drawer`,
|
||||
`Approval prompt` for share/delete).
|
||||
5. **`memory/MemoryEditDialog.tsx` (new)** — wire the currently-dead Edit button
|
||||
(`MemoryApp.tsx:289`) to `useMemory().editFrame`.
|
||||
6. **`memory/MemoryMergeDialog.tsx` (new)** — select 2+ frames → call merge adapter method.
|
||||
7. **Reuse as-is:** `KnowledgeGraphViewer.tsx` (Graph tab), `ContextMenu.tsx`,
|
||||
`HintTooltip`, `renderSimpleMarkdown`. **Relocate off this tab bar:** `WeaverPanel`,
|
||||
`WikiTab`, `EvolutionTab`, `HarvestTab` (Harvest folds into the new **Sources** tab).
|
||||
8. **`overlays/ContextRail.tsx`** is already the right-side full-context rail (`onContextRail`
|
||||
prop, `MemoryApp.tsx:75,183`) — keep the integration.
|
||||
|
||||
**Hook/adapter work (`useMemory.ts` + `lib/adapter.ts`):**
|
||||
- Extend `MemoryFilters` to `{ types, sources, minConfidence, minImportance, workspaceId,
|
||||
tags, dateFrom, dateTo, scope, searchQuery }`; filter client-side first, push server-side
|
||||
where the route supports it (`/api/memory/search` already accepts `since/until/workspace/scope`,
|
||||
`memory.ts:117-120`).
|
||||
- New adapter methods (thin wrappers; adapter is the single sidecar contract surface — inventory
|
||||
`frontend.md` (c)): `getMemoryFrame(id)`, `archiveMemoryFrame(id)`, `mergeMemoryFrames(ids[])`,
|
||||
`shareMemoryFrame(id, scope)` (Team tab). Team tab reuses existing `searchTeamMemory`
|
||||
(`adapter.ts:489`).
|
||||
- Reconcile `MemoryFrame` FE type vs server `normalizeFrame` output (add `source`, `sourceMind`,
|
||||
`confidence`, `tags`, `evidence`, `status`, `relatedMemoryIds` — see §6).
|
||||
|
||||
**States to implement (all PRD-required):** Loading / Empty / Populated / Error / Offline /
|
||||
Permission-denied / Importing / Consolidating / Low-confidence (badge) / Conflict (badge +
|
||||
`conflictNote` from `CombinedRetrieval`, backend-map 05b §8) / Deprecated / Source-unavailable /
|
||||
Trash.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.4 endpoints)
|
||||
|
||||
Status per inventory `backend-routes.md` Part 2 §16.4, re-verified against
|
||||
`packages/server/src/local/routes/memory.ts` + `knowledge.ts`. Substrate = `memory_frames`
|
||||
(+`_fts`/`_vec`) and `knowledge_entities/relations` in `packages/hive-mind-core/src/mind/`.
|
||||
|
||||
| PRD §16.4 endpoint | Status | Action — EXTEND existing vs NET-NEW · substrate |
|
||||
|---|---|---|
|
||||
| `GET /api/memory` | **PARTIAL** | EXTEND: alias/accept on existing `GET /api/memory/frames` (`memory.ts:188`). No new substrate. |
|
||||
| `GET /api/memory/:id` | **MISSING** | NET-NEW thin route reading `FrameStore.getById(id)` (backend-map 05b §2). `memory_frames`. No migration. |
|
||||
| `POST /api/memory` | **PARTIAL** | EXTEND: alias on `POST /api/memory/frames` (`memory.ts:237`). |
|
||||
| `PATCH /api/memory/:id` | **PARTIAL** | EXTEND `PUT /api/memory/frames/:id` (`memory.ts:448`) to accept `PATCH` + bare `:id`. `FrameStore.update`. |
|
||||
| `POST /api/memory/:id/archive` | **MISSING** | NET-NEW. Model archive as `FrameStore.update(id, importance:'deprecated')` (no hard delete) OR add `status` in the new `metadata` column (§6). `memory_frames`. |
|
||||
| `DELETE /api/memory/:id` | **PARTIAL** | EXTEND: alias bare `:id` over `DELETE /api/memory/frames/:id` (`memory.ts:551`, → `FrameStore.delete`). |
|
||||
| `POST /api/memory/merge` | **MISSING** | NET-NEW. Read N frames, synthesize merged content, write one frame, archive/delete the originals. Reuse `FrameStore` + dedup (`findDuplicate`, 05b §2). `memory_frames`. |
|
||||
| `GET /api/memory/graph` | **EXISTS** | `knowledge.ts` `GET /api/memory/graph?scope=` — Graph tab. No work. |
|
||||
|
||||
**Adjacent reuse (no new endpoint):** Team tab → existing `GET /api/team/memory/search`
|
||||
(`team.ts`, **Tier: TEAMS**, inventory backend-routes 1.8); Sources tab → existing
|
||||
`GET /api/harvest/sources` (`harvest.ts`); stats column → existing `GET /api/memory/stats`
|
||||
(`memory.ts:391`); conflict/relevance signals available from `CombinedRetrieval`
|
||||
(backend-map 05b §8 `hasConflict`/`conflictNote`, `finalScore` relevance).
|
||||
|
||||
**.MIND MIGRATION FLAG (one, low-risk).** To back confidence / provenance-id / source-url /
|
||||
tags / evidence / kind / title / status / relatedMemoryIds, `memory_frames` has **NO metadata
|
||||
column today** (substrate-types.md (c): unlike `awareness.metadata` etc.). The migration runner
|
||||
already does idempotent additive `ADD COLUMN` (precedent: it added `source`, `mind/db.ts:116-124`).
|
||||
**Recommended:** ONE additive migration adding nullable
|
||||
`metadata TEXT NOT NULL DEFAULT '{}'` to `memory_frames`, storing
|
||||
`{kind,title,scope,sourceId,sourceUrl,confidence,tags,evidence,relatedMemoryIds,status}` as JSON.
|
||||
This avoids touching the FTS5/vec0 virtual tables and the IPB scoring path. If `confidence`
|
||||
becomes a primary filter/sort axis (PRD "filter by confidence" / "low-confidence surfaced"),
|
||||
promote `confidence REAL` to a real indexable column in a later migration (same ADD-COLUMN
|
||||
pattern). PRD §15.4 line 1013 explicitly endorses metadata-first.
|
||||
|
||||
> Latent-bug flag (not session-induced, surfaced by substrate-types.md (d)): `AuditRiskLevel`
|
||||
> TS includes `'critical'` but the DDL CHECK allows only `low/medium/high` — irrelevant to S04
|
||||
> but the same install-audit store backs Sources-tab provenance if surfaced; note for the plan.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
PRD §15.2 unions — **none exist in `apps/web/src/lib/types.ts`** (substrate-types.md (e)):
|
||||
- Add `MemoryKind = 'fact'|'decision'|'task'|'preference'|'strategy'|'learning'|'goal'|'entity'`
|
||||
(PRD line 949). Current `MemoryFrame.type` (`types.ts:120`) uses `fact/event/insight/decision/
|
||||
task/entity` — OVERLAPS but mismatched (`event`/`insight` vs PRD `preference/strategy/learning/
|
||||
goal`). Reconcile.
|
||||
- Add `Scope = 'personal'|'workspace'|'team'|'organization'` (line 945) — drives the Active/
|
||||
Workspace/Team tab partition. Today scope is IMPLICIT (which `.mind` file; surfaced as `_mind`
|
||||
tag, `memory.ts:34`).
|
||||
- Add `Confidence = number` (0-100, line 946).
|
||||
|
||||
Extend `MemoryFrame` (PRD §15.4, lines 990-1013) toward: `kind, title, content, scope,
|
||||
workspaceId, teamId, source, sourceId, sourceUrl, confidence, importance, evidence[], tags[],
|
||||
relatedMemoryIds[], relatedArtifactIds[], createdAt, updatedAt, lastAccessedAt, status`.
|
||||
`relatedArtifactIds[]` stays **deferred** (no Artifact entity exists yet — that's S05;
|
||||
substrate-types.md (e)). Also fix the FE-type ↔ `normalizeFrame` mismatch noted in §3/§4.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **Phase placement:** PRD §8 Phase 2 / Sprint 4 (Memory + Artifacts). Depends on **Phase 0**
|
||||
(shared frontend types + AppShell/IA freeze — the §15.2 unions live there) and on the
|
||||
workspace-state/context contract from Phase 1.
|
||||
- **Workspace scope** (`scope`/`workspaceId`) presupposes the S01/S02 workspace-type-and-status
|
||||
fields (`WorkspaceConfigV2`, substrate-types.md (a)) for the Workspace/Team partition; usable
|
||||
with the implicit `_mind` scope before that lands, so soft dependency only.
|
||||
- **Team tab** depends on TEAMS tier + the team substrate (S10 Team Workspace); gate behind
|
||||
`useFeatureGate`/tier and degrade to a "Teams feature" empty state otherwise.
|
||||
- **Sources tab** reuses Harvest (already shipped); the broader Memory Import onboarding (S15/S16)
|
||||
is independent.
|
||||
- **No dependency on S05 Artifacts** for v1 (defer `relatedArtifactIds`).
|
||||
- Open question O3 (PRD line 1418): Graph view in v1 or later — Graph already works, so keep.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **L**
|
||||
|
||||
Frontend is a multi-pane rework with a brand-new trust/detail surface (confidence, evidence,
|
||||
related, merge, archive, share) replacing a thin detail pane, plus 4-5 new subcomponents and a
|
||||
hook/adapter/type reconciliation. Backend is mostly EXTEND/alias over existing memory routes
|
||||
(7 of 8 endpoints), but the trust fields require **one additive `.mind` migration** + a real
|
||||
`merge` endpoint + a `:id` read + an archive path — net-new logic, low schema risk. The single
|
||||
migration and the FE/BE `MemoryFrame` contract reconciliation push this past M into L.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Confidence source.** `memory_frames` has no confidence today; `knowledge_relations.confidence`
|
||||
exists (edges only, schema.ts:93). Do we (a) store confidence in the new `metadata` JSON,
|
||||
(b) promote to a `REAL` column now for indexable low-confidence filtering, or (c) derive a
|
||||
proxy from `source` trust-class + `importance` until real confidence is computed? (PRD §12.4
|
||||
wants "filter by confidence" + "low-confidence surfaced for review".)
|
||||
2. **Conflict detection surfacing.** `CombinedRetrieval.detectConflict` (05b §8) yields
|
||||
`hasConflict`/`conflictNote` at recall time, not as a stored per-frame state. Is the PRD
|
||||
"Conflict" state (§14.4) a live recall-time signal or a persisted frame status?
|
||||
3. **Archive vs deprecate vs tombstone.** PRD §14.4 distinguishes Deprecated / Archived /
|
||||
Deleted-tombstoned. `importance:'deprecated'` exists; is "archive" a distinct status (needs
|
||||
the `metadata.status` field) or an alias of deprecate? Delete = hard `FrameStore.delete` or
|
||||
tombstone? (PRD Open Question O8, line 1423.)
|
||||
4. **Merge semantics.** Does `POST /api/memory/merge` LLM-synthesize a combined frame, or just
|
||||
concatenate + re-cognify? What happens to the originals (archive vs hard delete)?
|
||||
5. **Tab-relocation scope.** Confirm Weaver/Wiki/Evolution move OUT of the Memory-Center tab bar
|
||||
to their Intelligence-layer home (frontend.md (f)) is in-scope for S04, or deferred to the IA
|
||||
pass so S04 only adds the new tabs and leaves the legacy ones temporarily.
|
||||
6. **`MemoryKind` reconciliation.** Drop FE `event`/`insight` and add PRD `preference/strategy/
|
||||
learning/goal`? This changes existing frame rendering + the type-filter chips.
|
||||
243
docs/ux-refactor/gap-cards/S05-artifact-center.md
Normal file
243
docs/ux-refactor/gap-cards/S05-artifact-center.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# Gap Card — S05 Artifact Center
|
||||
|
||||
> Execution model: **in-place incremental refactor** of `apps/web` + targeted backend extensions.
|
||||
> Mockups are directional; PRD acceptance criteria win over pixels (PRD §24).
|
||||
> Sources: PRD §12.5 + §16.6, blueprint p.32 (`_blueprint_extracted.txt:306-317, 525, 547`),
|
||||
> mockup `screen_05_artifact_center.png`, baseline inventories under `docs/ux-refactor/_inventory/`,
|
||||
> backend-map `sections/04-feature-map.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
The **Artifact Center** is the **outcome layer**: it organizes *outputs* (documents, presentations,
|
||||
spreadsheets, dashboards, research, code, media, designs, other) as first-class **relational objects** —
|
||||
not file attachments. PRD §12.5 purpose verbatim: "Organize outcomes, not just attachments." The
|
||||
headline acceptance criterion (PRD line 532): **searching a topic (e.g. "Germany GTM") returns all
|
||||
relevant outcome objects plus related memories, sessions, tasks, agents, and people — not just files.**
|
||||
|
||||
Mockup (directional): left filter rail (type / status / workspace / tag facets), a center **artifact
|
||||
table** (icon, title, type, workspace, status badge, updated, owner) with a top search bar + view
|
||||
toggle + pagination, and a right **detail panel** (preview thumbnail, metadata, related items, actions).
|
||||
This is a **data-heavy table+detail screen**, the same shape the blueprint flags as acceptable in light
|
||||
variant (`_blueprint_extracted.txt:484`).
|
||||
|
||||
The defining difference from today's Files app: an artifact is an **outcome with relations**
|
||||
(`relatedMemoryIds / relatedSessionIds / relatedTaskIds / relatedAgentIds`,
|
||||
`generatedByAgentId`, `status: draft|final|shared|generated`), addressable by a stable `id`, spanning
|
||||
**all storage backends and all formats**. Today's Files app exposes only a raw filesystem tree scoped to
|
||||
one workspace + one storage tab.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
PRD §12.5 functional requirements:
|
||||
- Artifact **categories** (= `ArtifactKind`, PRD §15.2 line 950): `document | presentation |
|
||||
spreadsheet | dashboard | research | code | media | design | other`.
|
||||
- **Topic search returns artifacts + related memories/sessions/tasks/agents/people** (the §16.6
|
||||
`GET /api/artifacts/search-related?q=` contract).
|
||||
- **Detail panel**: preview, metadata, workspace, creator, updated time, access, status, tags, related
|
||||
items, actions.
|
||||
- **Actions**: open, share, duplicate, move, delete, relate to workspace/memory/session/task.
|
||||
|
||||
State model — PRD §14.1 global states (every major screen): Loading, Empty, Populated, Error,
|
||||
Offline/local-only, Syncing, Permission denied, Partial data, Approval required.
|
||||
Plus the **artifact-specific states** (PRD §14 line not enumerated but blueprint `:312-314, 458`):
|
||||
**Draft, Final, Shared, Generated, External-missing (source unavailable), Permission denied.**
|
||||
|
||||
So the concrete states to build:
|
||||
1. Loading (skeleton table + skeleton detail).
|
||||
2. Empty ("no artifacts yet" — first-run / no outputs produced).
|
||||
3. Populated (table + facets + detail).
|
||||
4. Error (fetch failed).
|
||||
5. Offline/local-only (sidecar unreachable — mirror FilesApp offline banner pattern).
|
||||
6. Permission denied (team-scoped artifact the user can't view).
|
||||
7. Per-row status badges: Draft / Final / Shared / Generated.
|
||||
8. Source-unavailable (artifact row whose backing file/url is missing — show broken-link affordance).
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo (disposition: **create-new** for the screen; **keep-promote** the substrates)
|
||||
|
||||
**There is NO Artifact entity, type, route, or component anywhere.** Grep-confirmed:
|
||||
- No `Artifact` type in `apps/web/src/lib/types.ts` (the only `Artifact` hit in `apps/web/src` is
|
||||
`components/os/apps/memory/EvolutionTab.tsx`, referring to evolution `artifacts_json` — unrelated).
|
||||
- No `artifacts` table in `packages/hive-mind-core/src/mind/schema.ts`.
|
||||
- No `/api/artifacts/*` routes (grep over `packages/server/src/local/routes/*.ts`: 0 matches).
|
||||
- Confirmed by `_inventory/substrate-types.md:259-266` ("**NO backing entity anywhere** … the single
|
||||
largest entity gap") and `_inventory/backend-routes.md:476-484` (all 6 §16.6 rows PARTIAL/MISSING).
|
||||
|
||||
**The closest current surface is the Files app** (the current-component hint), which is **NOT an
|
||||
artifact center** — disposition for it is **keep-as-is, do not retrofit**:
|
||||
- `apps/web/src/components/os/apps/FilesAppTabs.tsx` — P16 three-tab (Virtual/Local/Team) wrapper that
|
||||
remounts `FilesApp` per `storageType`. Storage-location switcher, not an outcome browser.
|
||||
- `apps/web/src/components/os/apps/FilesApp.tsx` (735 LOC) — full file-manager: tree + list/grid +
|
||||
preview + upload + rename/move/copy/delete + bulk ops + properties dialog + inline `VersionHistory`.
|
||||
It is **path/workspace/storage-scoped** (`adapter.listFiles(workspaceId, currentPath)`), has no
|
||||
cross-workspace aggregation, no type/status/relation model, no facet filtering by outcome kind.
|
||||
- Sub-components `components/os/files/{FileTree,FilePreview,FileActions,FileUploadZone,SyntaxPreview,
|
||||
WorkspaceRail}.tsx` — operate on `FileEntry` (`lib/types.ts:42-50`: `name/path/type/size/mimeType/
|
||||
modifiedAt/createdAt`), a raw FS entry, not an outcome object.
|
||||
|
||||
**Three existing backend substrates the new Artifact layer must aggregate over (reuse, do not duplicate):**
|
||||
1. **Workspace file registry** — `GET /api/workspaces/:id/files` (`workspaces.ts:594-606`) returns
|
||||
`readFileRegistry(dataDir, id)` of `FileRegistryEntry { name, type, summary, sizeBytes, ingestedAt }`
|
||||
(`routes/ingest.ts:125-130`). This is an **ingest log**, newest-first — closest thing to a
|
||||
per-workspace "produced/ingested things" list, but no id, no status, no relations.
|
||||
2. **Document version registry** — `GET /api/workspaces/:id/documents` +
|
||||
`/documents/:name/versions` (`routes/documents.ts`, JSON at
|
||||
`~/.waggle/workspaces/{id}/documents.json`, shapes `TrackedDocument`/`DocumentVersion`). Gives
|
||||
versioning + size + createdAt keyed by name; already surfaced in FilesApp's `VersionHistory`
|
||||
(`FilesApp.tsx:27-54`). No type/status/relations.
|
||||
3. **Workspace storage files** — `GET /api/workspaces/:id/storage/files` + `/storage/read|write|delete`
|
||||
(`workspaces.ts:885+`) — the actual byte store for virtual/local/team.
|
||||
|
||||
**Verdict:** the screen is **create-new** (`ArtifactCenter` is in PRD §20.3 "Create" list, line 1289).
|
||||
The backend is **a thin net-new aggregation/normalization layer over the three existing stores** — no
|
||||
new data store required (`_inventory/backend-routes.md:604-607`).
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**New top-level app (dock id `artifacts`).** Register in `Desktop.tsx` `appConfig` + `renderAppContent`
|
||||
switch, add `AppId` `'artifacts'` in `lib/dock-tiers.ts`, and a Work-bucket dock entry (the IA maps
|
||||
Artifacts to the **Work** layer — `_inventory/frontend.md:360`). Do **not** route — this is a windowed
|
||||
single-route desktop; opening is by `AppId` via `openApp` (`useWindowManager`).
|
||||
|
||||
Components to **create** (keep files small, ~200-400 LOC each per repo file-org rule):
|
||||
- `components/os/apps/ArtifactCenterApp.tsx` — shell: search bar + view toggle + facet rail + table +
|
||||
detail panel + pagination. Owns query/filter/selection state. Mirrors the FilesApp three-pane layout
|
||||
idiom (rail / main / detail) so it feels native.
|
||||
- `components/os/artifacts/ArtifactTable.tsx` (or `ArtifactRow.tsx` — blueprint names `ArtifactRow`,
|
||||
`_blueprint_extracted.txt:487`) — list rows with icon/title/type/workspace/status badge/updated/owner.
|
||||
- `components/os/artifacts/ArtifactFacetRail.tsx` — type/status/workspace/tag facet filters (left rail in mockup).
|
||||
- `components/os/artifacts/ArtifactDetailPanel.tsx` — preview + metadata + related-items list + actions.
|
||||
- `components/os/artifacts/ArtifactRelatedList.tsx` — renders related memories/sessions/tasks/agents;
|
||||
clicking a related item should raise the relevant window via the existing `waggle:open-app`
|
||||
CustomEvent (and/or `onContextRail` like FilesApp does, `FilesApp.tsx:206-208`).
|
||||
|
||||
**Reuse targets (do not rebuild):**
|
||||
- Status/confidence badges, `Skeleton`, `Table`, view-toggle, `Pagination`, `HoverCard` — all exist in
|
||||
`components/ui/*` (shadcn set, `_inventory/frontend.md:337-342`).
|
||||
- Offline banner pattern + retry — copy from `FilesApp.tsx:384-389`.
|
||||
- File preview for an artifact's backing file — reuse `components/os/files/FilePreview.tsx`.
|
||||
- Empty-state idiom — `FilesApp.tsx:470-477`.
|
||||
- Detail-panel metadata layout idiom — FilesApp Properties dialog (`FilesApp.tsx:667-729`).
|
||||
- ContextRail for "show full context of this artifact" — `overlays/ContextRail.tsx` already exists
|
||||
(extend `ContextRailTarget` with an `'artifact'` variant).
|
||||
|
||||
**New hook + adapter methods:**
|
||||
- `hooks/useArtifacts.ts` — `{ artifacts, filters, setFilter, selected, select, search, refresh,
|
||||
create, patch, remove, share }`; reads/writes through the adapter. Follow the `useMemory` shape
|
||||
(`hooks/useMemory.ts`).
|
||||
- Extend `lib/adapter.ts` (the single sidecar gateway, ~1930 LOC — new §16 methods land here per
|
||||
`_inventory/frontend.md:380`) with: `getArtifacts`, `getArtifact`, `createArtifact`,
|
||||
`patchArtifact`, `deleteArtifact`, `searchRelatedArtifacts`, `shareArtifact`.
|
||||
|
||||
**Props/state notes:** `ArtifactCenterApp` takes `{ workspaces?, activeWorkspaceId?, onSelectWorkspace?,
|
||||
onContextRail? }` (same cross-workspace pattern FilesApp uses). It is **cross-workspace by default**
|
||||
(the whole point vs FilesApp) — workspace becomes a *facet*, not a hard scope.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.6)
|
||||
|
||||
> No new data store. Every endpoint is a **net-new aggregation/normalization route** over the existing
|
||||
> file registry + document versions + workspace storage. New file:
|
||||
> `packages/server/src/local/routes/artifacts.ts`, registered in `local/index.ts`. The Artifact `id`
|
||||
> can be a stable composite of `workspaceId + source-store + name/path` (or a registry-assigned id if a
|
||||
> lightweight `artifacts.json` index is added per workspace, mirroring `documents.json`).
|
||||
|
||||
| PRD §16.6 endpoint | Status | Plan (EXTEND vs NET-NEW) + substrate |
|
||||
|---|---|---|
|
||||
| `GET /api/artifacts` | **PARTIAL → NET-NEW route** | No `/api/artifacts` domain. NET-NEW `GET /api/artifacts` in `artifacts.ts` that **fans out over `WorkspaceManager.list()`** and, per workspace, normalizes (a) file registry `GET /api/workspaces/:id/files` (`workspaces.ts:594`, `FileRegistryEntry`), (b) document versions `GET /api/workspaces/:id/documents` (`documents.ts`), into a unified `Artifact[]`. Supports `?workspaceId=&type=&status=&tag=&q=` facet filters. Cross-workspace = the differentiator. |
|
||||
| `POST /api/artifacts` | **PARTIAL → NET-NEW route (thin)** | Closest writes that already persist bytes: `POST /api/ingest` (`ingest.ts`), `POST /api/workspaces/:id/files/upload` (`files.ts`), `POST /api/workspaces/:id/documents` (`documents.ts`), `POST /api/workspaces/:id/storage/write` (`workspaces.ts`). NET-NEW `POST /api/artifacts` records artifact metadata (kind/title/status/tags/relations + `generatedByAgentId`) in a per-workspace `artifacts.json` index and (optionally) writes the backing file via the storage route. |
|
||||
| `GET /api/artifacts/:id` | **MISSING → NET-NEW** | Resolve composite id → normalized `Artifact` with relations + preview metadata. Reuse `documents.ts` version lookup for `relatedVersions`. |
|
||||
| `PATCH /api/artifacts/:id` | **MISSING → NET-NEW** | Update title/status/tags/relations in the `artifacts.json` index (move = re-point `workspaceId`/`storagePath`; reuse `files/move`). |
|
||||
| `DELETE /api/artifacts/:id` | **PARTIAL → NET-NEW route** | Closest: `POST /api/workspaces/:id/files/delete` (`files.ts`), `DELETE /api/workspaces/:id/storage/delete` (`workspaces.ts`). NET-NEW `DELETE /api/artifacts/:id` removes the index entry and (optionally) the backing file via those. |
|
||||
| `GET /api/artifacts/search-related?q=` | **MISSING → NET-NEW (the headline endpoint)** | Federated search: query the normalized artifact index **plus** `GET /api/memory/search` (`memory.ts`), session search `GET /api/workspaces/:wid/sessions/search` (`sessions.ts`), tasks `GET /api/tasks` (`tasks.ts`), and fleet/agents (`GET /api/agents/active`/`/api/fleet`), returning grouped `{ artifacts, memories, sessions, tasks, agents }`. Internally can lean on existing FTS (`memory_frames_fts`, marketplace FTS5, wiki search). Delivers PRD line 532 acceptance. |
|
||||
| `POST /api/artifacts/:id/share` (blueprint `:525`) | **MISSING → NET-NEW** | Blueprint adds a `/share` action not in PRD §16.6 list. Maps to the missing `POST /api/share` (`_inventory/backend-routes.md:551`) + team scope. Defer to the Team phase (see §7); gate behind TEAMS tier like `/api/team/*`. |
|
||||
|
||||
**Substrate touched:** workspace file registry (`ingest.ts` `FileRegistryEntry`), document versions
|
||||
(`documents.ts` JSON), workspace storage (`workspaces.ts` storage routes), memory FTS
|
||||
(`memory_frames_fts`), sessions JSONL, tasks store, fleet/orchestrator. **No `.mind` migration
|
||||
required** for a metadata-first implementation: artifact metadata + relations live in a per-workspace
|
||||
`artifacts.json` index (same pattern as `documents.json`). If artifacts must later be queryable in SQL
|
||||
alongside frames, a future additive `artifacts` table in `schema.ts` follows the established
|
||||
idempotent ADD-pattern (`mind/db.ts:116-124`) — flag, not now.
|
||||
|
||||
**Governance note:** if agent-generated artifacts (`generatedByAgentId`) need an audit trail, reuse
|
||||
`InstallAuditStore`/`emitAuditEvent` (`workspaces.ts:631` already emits `workspace_update`) rather than
|
||||
a parallel log.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15.2 / §15.6 vs `lib/types.ts`)
|
||||
|
||||
**Net-new, none exist today** (`_inventory/substrate-types.md:226, 259-266`):
|
||||
- `ArtifactKind` union (PRD §15.2 line 950) — add to `lib/types.ts` (and `packages/shared/src/types.ts`
|
||||
if the sidecar route also imports it, to keep one contract).
|
||||
- `ArtifactStatus = 'draft' | 'final' | 'shared' | 'generated'` (from blueprint states `:312`,
|
||||
PRD §14 artifact states). Note blueprint also implies `external-missing`/`source-unavailable` —
|
||||
model as a derived flag, not a status value.
|
||||
- `Artifact` interface — PRD §15.6 (lines 1039-1056) ∪ blueprint `:547`: `id, title, kind, workspaceId,
|
||||
teamId?, createdBy, generatedByAgentId?, source, status, mimeType?, storagePath?, previewUrl?,
|
||||
summary?, tags[], relatedMemoryIds[], relatedSessionIds[], relatedTaskIds[], relatedAgentIds[],
|
||||
createdAt, updatedAt`.
|
||||
- `RelatedSearchResult` — the grouped `{ artifacts, memories, sessions, tasks, agents }` envelope for
|
||||
`search-related`.
|
||||
|
||||
**Reconcile, don't fork:** define `Artifact`/`ArtifactKind`/`ArtifactStatus` **once** (shared package
|
||||
preferred) so the sidecar route and the frontend hook share the contract — avoid the existing
|
||||
FE↔BE `MemoryFrame` drift the inventory flags (`_inventory/substrate-types.md:246-247`). Optionally add
|
||||
`relatedArtifactIds[]` to the memory side later (PRD §15.4) so the relation is bidirectional.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **PRD Phase 2 — Work layer** (this screen's home; Memory Center is its sibling, Sprint 4). The
|
||||
`search-related` federated endpoint is the binding dependency on Memory (FTS) + Sessions + Tasks.
|
||||
- **AppShell / dock IA (Phase 0/1)** must exist first so `artifacts` registers as a Work-bucket dock
|
||||
entry (consolidate on `AppId`, retire stale `AppView`).
|
||||
- **Command Center (Ctrl+K) (Phase 1)** should index artifacts (`_blueprint_extracted.txt:515` — command
|
||||
index unifies artifacts) — soft dependency; Artifact Center can ship before Ctrl+K wires it in.
|
||||
- **`WorkspaceConfigV2` `type`/`status` fields** (S-workspace cards) help facet labels but are not
|
||||
blocking.
|
||||
- **Team Workspace / RBAC (Phase 5)** gates `POST /api/artifacts/:id/share` + permission-denied state.
|
||||
Ship Artifact Center personal-scoped first; share/team-scope is a follow-on.
|
||||
- **Agent run → artifact linkage** (PRD Journey 7, §15.6 `generatedByAgentId`) depends on the agent
|
||||
runtime writing artifact records — a downstream integration, not a blocker for the read/browse screen.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **L**
|
||||
|
||||
Net-new full-stack surface: 5-6 new frontend components + a new hook + 7 new adapter methods, **plus**
|
||||
a net-new backend aggregation domain (`artifacts.ts`, ~6 routes) that must normalize **three** existing
|
||||
stores into one entity and a **federated** search across memory/sessions/tasks/agents. No new DB and
|
||||
heavy component/route reuse keep it out of XL, but the cross-workspace aggregation + the
|
||||
`search-related` federation + a brand-new shared `Artifact` contract make it clearly more than M.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Artifact identity & index.** Synthesize `id` as a composite (`workspaceId:store:name`), or add a
|
||||
per-workspace `artifacts.json` index (mirroring `documents.json`) that assigns stable ids and holds
|
||||
status/tags/relations? (PATCH/relations effectively require the latter.) → maps to PRD Open Q6
|
||||
(storage: workspace FS vs virtual store vs external refs, PRD line 1421).
|
||||
2. **What counts as an artifact in v1?** Only explicit outputs (generated docs/decks/etc.), or every
|
||||
ingested file in the registry? The registry mixes ingested inputs with produced outputs — need a
|
||||
classification rule for `kind`/`status`.
|
||||
3. **`search-related` scope/cost.** Federating memory FTS + sessions + tasks + agents per query — cap
|
||||
per-source result counts and run async result groups (PRD §24 perf mitigation), or a single indexed
|
||||
provider? (Mirrors the Command Center search concern.)
|
||||
4. **Share semantics (blueprint `/share` vs PRD `/api/share`).** Is `POST /api/artifacts/:id/share`
|
||||
in-scope for the first Artifact Center cut, or deferred entirely to the Team phase?
|
||||
5. **Preview generation.** `previewUrl`/thumbnails — generate server-side, reuse `FilePreview`
|
||||
on-demand client-side, or skip thumbnails in v1 (icon + on-click preview only)?
|
||||
6. **Bidirectional relations.** Do we add `relatedArtifactIds[]` to memory frames now (PRD §15.4) or
|
||||
keep relations one-directional (artifact → others) in v1?
|
||||
153
docs/ux-refactor/gap-cards/S06-skills-hub.md
Normal file
153
docs/ux-refactor/gap-cards/S06-skills-hub.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Gap Card — S06 Skills Hub (+ Skill Builder)
|
||||
|
||||
> Screen 06 of the Waggle OS UX-refactor. Sources: PRD §12.6 + §16.8 + §15 (`docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`),
|
||||
> blueprint extract pp.33 + screen-table row 6 + states table (`_blueprint_extracted.txt`),
|
||||
> mockup `Waggle_OS_Handoff_Assets/screen_06_skills_hub.png` (directional only),
|
||||
> backend-map `sections/03d-api-marketplace-skills.md` + `sections/05g-subsystem-skills-marketplace-wiki.md`,
|
||||
> and live repo grep/read. Execution model: **in-place incremental refactor** of `apps/web` + targeted backend extension.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Skills Hub** — the surface where reusable capabilities (a "skill" = a Markdown file in `~/.waggle/skills/{name}.md`, injected into the agent system prompt) are browsed, installed, authored, tested, and assigned to agents/workspaces. Blueprint row 6: *"Reusable capabilities across users, workspaces and agents. Install, create, test, assign to agent/workspace, archive."* Acceptance criterion (PRD §12.6 / blueprint): *"A user can understand what a skill does, where it is used, and what access it has."*
|
||||
|
||||
The mockup shows a left rail of category/state filters, a **My Skills** table (name / category / status / usage / last-used columns), a **Marketplace** panel, a **Workspace Skills** table, a **Custom Skills** panel, and a **Create Skill** CTA — i.e. a unified library + marketplace + builder entry, replacing the current "Skills & Apps" pack-grid.
|
||||
|
||||
Per the locked IA (frontend.md §f), this is the **Intelligence** layer, expressed through the existing dock/window-manager (`AppId='capabilities'`, dock key `skills`) — NOT a new route. The PRD also pairs this screen with the **Skill Builder** (PRD §12.6 5-step stepper; PRD §20.3 lists `SkillBuilder` under "Create").
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
PRD §12.6 functional requirements:
|
||||
- **Skills Hub tabs:** My Skills · Marketplace · Custom Skills · Workspace Skills.
|
||||
- **Skill object fields:** name, description, category, instructions, inputs, outputs, tools/data, memory access, owner, status, usage, last used.
|
||||
- **Skill Builder steps:** Basic Info · Instructions · Inputs & Outputs · Tools & Data · Review & Create.
|
||||
- Support **test run before publishing**.
|
||||
- Skills can be **assigned to agents, workspaces, automations, or used directly**.
|
||||
|
||||
Blueprint per-object states (states table) for **Skills**: `Loading, empty, populated, error` + item states `Installed, draft, custom, workspace, marketplace, update available`; actions `Test, install, publish, archive, rollback`. PRD §14.7 Extension states also apply: `Available / Installed / Update available / Installing / Failed install / Risk approval required / Disabled-revoked`.
|
||||
|
||||
Global states every screen must implement (PRD §14.1): Loading · Empty · Populated · Error · Offline/local-only · Syncing · Permission denied · Partial data · Approval required.
|
||||
|
||||
**Trust/tier state (load-bearing):** custom skills + marketplace install/publish are PRO-gated; FREE = built-in skills only (`tiers.ts`; 05g §19). A `403 → upgrade` state is required on create/install/publish.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `partial`** — strong backend + a real (but mislabelled) hub component exist; the screen needs **rework** of the FE component to the PRD tab/table model, plus a **net-new Skill Builder** and **thin backend extensions** for `:id/test`, `:id/install`, and skill scope (workspace/custom).
|
||||
|
||||
### Frontend (exists, reworkable)
|
||||
- `apps/web/src/components/os/apps/CapabilitiesApp.tsx` — the live Skills surface ("**Skills & Apps**" header, line 339). Tabs are `installed | starter | marketplace | tools | audit` (line 80, 358) — a **pack-centric grid**, NOT the PRD's My/Marketplace/Custom/Workspace tabs. Renders `PackCard`/`PackDetail` over `SkillPack` (pack groupings), has skill **test preview** (`handleTestSkill` → `POST /api/skills/test`, lines 88-102), pack install with `403→UpgradeModal` routing (`handleInstallError`, lines 149-162), a `tools` read-only catalog, and an `audit` tab (`GET /api/audit/installs`). Reachable via dock key `skills` → `appId 'capabilities'` (`lib/dock-tiers.ts:64`).
|
||||
- `apps/web/src/components/os/apps/MarketplaceApp.tsx` — standalone marketplace browser (search/install/uninstall over `/api/marketplace/*`); registered as `appId 'marketplace'` but **no dock entry points at it** (frontend.md §a). Marketplace is doubly-represented (here + folded into CapabilitiesApp) — an IA-cleanup point for this screen.
|
||||
- Adapter methods already wired (`lib/adapter.ts`): `getSkills`, `createSkill`, `getStarterPacks`, `getCapabilityPacks`, `installPack`, plus full marketplace set (frontend.md §c).
|
||||
- Types: `SkillPack` (`lib/types.ts:210-218`: id/name/description/category(5-enum)/skills[]/installed/trust) — a **pack** shape, NOT the PRD per-skill `Skill` object. No `Skill`/`SkillScope`/`SkillStatus`/`ExtensionType` type in FE (substrate-types.md §e).
|
||||
|
||||
### What does NOT exist (gaps)
|
||||
- **No `SkillBuilder` component** anywhere (grep confirmed — the only `SkillBuilder`/`Custom Skill` string matches are upgrade-modal copy in `TrialExpiredModal.tsx`/`UpgradeModal.tsx`). PRD §12.6 5-step builder is **net-new FE**.
|
||||
- **No "Workspace Skills" / "Custom Skills" scoping at runtime.** Skills are flat files in `~/.waggle/skills/` (05g §8); the prompt loader ignores frontmatter (`loadSkills`, 05g §2 note). A `SkillScope` (`personal|workspace|team|enterprise`) exists only in `skill-frontmatter.ts` frontmatter + the `promote_skill` path — not persisted/queryable by the skills route (grep `scope|workspace` in `routes/skills.ts` → **0 matches**). So the PRD's Workspace/Custom tabs have **no backing filter today**.
|
||||
|
||||
### Backend (mostly exists — see §5)
|
||||
`packages/server/src/local/routes/skills.ts` already serves list/create/get/update/delete + `test` + `suggestions` + `hash-status` + starter/capability-pack catalogs + `/api/audit/installs`. Substrate detailed in 05g (skill-creator / -recommender / -usage / -retirement / -watcher) and 03d.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Rework `CapabilitiesApp.tsx` → Skills Hub** (keep the file; this is the in-place refactor target), and **create `SkillBuilder`**. Reuse the existing window-manager surface (no new route); resolve the marketplace double-representation by making Skills Hub the canonical host and retiring/redirecting `MarketplaceApp`'s dead `appId`.
|
||||
|
||||
**Components to rework / create**
|
||||
- **Rework** `CapabilitiesApp` tab model: `installed|starter|marketplace|tools|audit` → PRD **My Skills · Marketplace · Custom · Workspace** (keep `tools`/`audit` as secondary panels or fold `audit` into a detail-drawer "install history"). My Skills must render a **per-skill table** (name/category/status/usage/last-used per mockup), not just a pack grid.
|
||||
- **Create** `SkillCard` / `SkillRow` (per-skill, distinct from `PackCard`) — design-system `SkillCard` is named in PRD §19.1 + blueprint components list. Show status badge (installed/draft/custom/workspace/update-available — PRD §14.7) and trust chip (reuse `lib/skill-pack-display.ts` `describeTrust`).
|
||||
- **Create** `SkillBuilder.tsx` overlay (PRD §20.3 "Create") — `Builder stepper` (PRD §19.1) with the 5 steps. Pattern-match the existing builder/wizard idioms (`overlays/onboarding/*` step components, `CreateWorkspaceDialog`, `SpawnAgentDialog`). Step 5 "Review & Create" wires a **test run** before publish.
|
||||
- **Create/extend** a `useSkills` hook (none exists today — `CapabilitiesApp` calls `adapter` directly via `Promise.allSettled`, lines 104-145). New hook returns `{ skills, packs, marketplace, install, create, test, refresh }`; keep `403→'waggle:tier-insufficient'` event routing (CapabilitiesApp lines 149-162) and `UpgradeModal` wiring intact.
|
||||
|
||||
**Reuse targets**
|
||||
- `POST /api/skills/test` → already wired (`handleTestSkill`); reuse for builder Step 5 + per-skill test.
|
||||
- `GET /api/skills/starter-pack/catalog` / `capability-packs/catalog` → already supply `state` per skill (`active|installed|available`); reuse for Marketplace/My-Skills state.
|
||||
- `GET /api/skills/suggestions?context=` → recommended-skills strip.
|
||||
- `dedupePacks`, `skill-pack-display`, `skill-recommendations`, `HintTooltip` — existing libs to keep.
|
||||
|
||||
**Props/state**
|
||||
- `Skill` type (new, §6): `{ name, description, category, status, scope, usageCount, lastUsedAt, trust, instructions?, inputs?, outputs?, tools?, memoryAccess?, owner? }`.
|
||||
- Builder form state mirrors `POST /api/skills/create` body (`{ name, description, steps[], tools?, category? }`) extended with inputs/outputs/memoryAccess once §5 lands.
|
||||
|
||||
**Adapter methods to add** (`lib/adapter.ts`): `testSkill(id, testInput?)` (path variant), `installSkill(id)` (unified dispatch), `updateSkill(id, patch)`, and scope-aware `getSkills({scope})` once the backend filter exists.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work — PRD §16.8 endpoint-by-endpoint
|
||||
|
||||
> Cross-ref against backend-map 03d/05g + `routes/skills.ts`. Skills are flat files in `~/.waggle/skills/`; the marketplace is `~/.waggle/marketplace.db`. **No `.mind` migration is required for §16.8** — skills/marketplace are not in the `.mind` SQLite schema. (One optional migration is flagged below for the *install-audit critical column*, see "Cross-cutting".)
|
||||
|
||||
| PRD §16.8 endpoint | Status | Extend vs net-new | Substrate touched |
|
||||
|---|---|---|---|
|
||||
| `GET /api/skills` | **EXISTS** | as-is (`routes/skills.ts`, 05g §8) — returns `{ skills:[{name,length,preview}], count, directory }` | `~/.waggle/skills/*.md` |
|
||||
| `POST /api/skills` | **EXISTS** | as-is — raw create `{name,content}`; structured create is `POST /api/skills/create` `{name,description,steps[],tools?,category?}` (the Builder's real target) | `~/.waggle/skills/*.md` + `redactSkillContent` + `skillHashStore` + install-audit |
|
||||
| `PATCH /api/skills/:id` | **PARTIAL** | **EXTEND** existing `PUT /api/skills/:name` (keyed by **name**, method `PUT`). Add a `PATCH` alias and accept `:id`↔`:name`. No new substrate. | `~/.waggle/skills/*.md` |
|
||||
| `POST /api/skills/:id/test` | **PARTIAL** | **EXTEND** existing `POST /api/skills/test` (body-driven `{skillName,testInput?}`) — add the `:id` path variant routing to the same handler. Sandbox/dry-run only (parses frontmatter, shows `wouldInject`); no execution. | skill file + `parseSkillFrontmatter` |
|
||||
| `POST /api/skills/:id/install` | **PARTIAL** | **NET-NEW thin dispatcher** over existing installs: `POST /api/skills/starter-pack/:id`, `POST /api/skills/capability-packs/:id`, `POST /api/marketplace/install` (PRO-gated, SecurityGate). `/skills/:id/install` resolves the source and delegates. Keep the `requireTier('PRO')` gate for marketplace-sourced skills. | marketplace.db + `MarketplaceInstaller` + `SecurityGate` + install-audit |
|
||||
|
||||
**Result: 0 net-new domains, 0 net-new stores.** 16.8 is `2 EXISTS / 3 PARTIAL` (matches `backend-routes.md` §16.8). All gaps are aliases/dispatchers over existing handlers.
|
||||
|
||||
**Backend gaps the PRD §12.6 tab model implies but §16.8 does NOT enumerate (flag for the plan):**
|
||||
1. **Workspace/Custom scope filtering.** PRD tabs (Workspace Skills, Custom Skills) need a queryable `scope`. Today `SkillScope` lives only in optional frontmatter and is ignored by the prompt loader. **Extend** `GET /api/skills` to parse `parseSkillFrontmatter` and return `scope` + a `?scope=` filter (custom = user-authored vs starter/marketplace; workspace = `scope: workspace` frontmatter). No store change — derive at read time. Net-new query param, reusing the existing parser (05g §2). The "Custom" tab can also be derived as "not in starter/capability-pack catalogs."
|
||||
2. **Assign-to-workspace/agent.** PRD "assign to agent/workspace" — workspace config already has a `skills?: string[]` field (`WorkspaceConfig`, substrate-types.md §a; matches `WorkspaceConfigV2.skills`). Assignment is a `PATCH /api/workspaces/:id { skills }` (EXISTS) — no new endpoint, but the FE must wire it.
|
||||
3. **Archive/rollback** (blueprint actions). `retireStaleSkills` (05g §6) **moves** skills to `~/.waggle/skills-archive/` (recoverable) — an archive substrate exists but has **no HTTP route**. A manual "archive"/"restore" pair would be net-new thin routes over `skill-retirement.ts`. Defer unless in MVP scope (PRD §12.6 lists archive in blueprint, not in §16.8).
|
||||
4. **Publish.** `POST /api/marketplace/publish` (PRO-gated) already exists for skill→catalog publish (03d/05g §13) — reuse for the Builder's "publish to marketplace" path.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
Currently the FE has only `SkillPack` (a pack grouping, `lib/types.ts:210-218`) — **no per-skill `Skill` type, no `SkillScope`/`SkillStatus`/`ExtensionType`** (substrate-types.md §e: all PRD §15.2 unions MISSING in FE).
|
||||
|
||||
Add to `apps/web/src/lib/types.ts` (FE), mirroring the existing backend `SkillFrontmatter`/`SkillTemplate` shapes (05g §2-3) so FE↔BE stay aligned:
|
||||
```ts
|
||||
type SkillScope = 'personal' | 'workspace' | 'team' | 'enterprise'; // matches skill-frontmatter.ts
|
||||
type SkillStatus = 'installed' | 'draft' | 'custom' | 'workspace' | 'marketplace' | 'update-available';
|
||||
type ExtensionType = 'skill' | 'connector' | 'mcp' | 'model' | 'template' | 'external_tool'; // PRD §15.2
|
||||
|
||||
interface Skill {
|
||||
name: string; // file stem (id surrogate)
|
||||
description: string;
|
||||
category: string;
|
||||
scope: SkillScope;
|
||||
status: SkillStatus;
|
||||
trust?: 'verified' | 'community' | 'experimental';
|
||||
usageCount?: number; // from skill-usage.json (05g §5)
|
||||
lastUsedAt?: string; // ISO
|
||||
instructions?: string; // body
|
||||
inputs?: string[]; outputs?: string[]; tools?: string[];
|
||||
memoryAccess?: boolean; owner?: string;
|
||||
}
|
||||
```
|
||||
Keep `SkillPack` for the pack-grid (starter/capability packs); `Skill` is the per-row object for My/Custom/Workspace tabs. Align `Skill.category` to the broader server `inferCategory` set (research/coding/knowledge/writing/planning/general — 05g §3), not the FE `SkillPack`'s 5-value enum.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **PRD Phase 3 (Intelligence layer)** — Skills Hub + Skill Builder ship here (PRD §8, Sprint 6). Depends on **Phase 0/1 AppShell + IA + shared types** (`ExtensionType`, `Skill`) and the dock/zone IA being settled.
|
||||
- **Soft dependency on S04 Memory** (memory-access declaration in skills — PRD field "memory access") and **S09 Agent Center** (skills assigned to agents — shared `skillIds` concept; `AgentDef` lacks `skillIds` today, substrate-types.md §e §15.5).
|
||||
- **Shares the Extend-governance install-audit read surface** with S07 Connector / S08 MCP / Marketplace screens — the `GET /api/audit/installs` route exists (05g §8) and is already consumed by `CapabilitiesApp` AuditTab; the broader `GET /api/extend/audit` (substrate-types.md §d, gap 1) is shared across S06/S07/S08.
|
||||
- **No dependency on Home/Command Center net-new domains.** Skills Hub can ship before those.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort
|
||||
|
||||
**M.** The backend is almost entirely present (2 EXISTS / 3 PARTIAL aliases-and-dispatchers, no new store, no `.mind` migration); the lift is FE: rework `CapabilitiesApp` from pack-grid to the PRD tab/table model, add a per-skill `Skill` type + `useSkills` hook, build the net-new 5-step `SkillBuilder`, and resolve the marketplace double-representation. Workspace/Custom scope filtering is a read-time derivation (cheap). Bumps toward L only if archive/rollback + full assign-to-agent wiring land in the same slice.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Tab semantics for "Custom" vs "Workspace":** is "Custom" = user-authored-not-from-catalog (derive by excluding starter/capability-pack ids) and "Workspace" = `scope: workspace` frontmatter? Confirm, since neither is a first-class persisted state today (skills are flat files; scope is optional frontmatter the loader ignores).
|
||||
2. **Does the Builder publish to the local skills dir, the marketplace catalog, or both?** `POST /api/skills/create` (local) vs `POST /api/marketplace/publish` (PRO-gated catalog) are different targets — PRD §12.6 says "test run before publishing" but doesn't say which surface "publish" writes to.
|
||||
3. **PRD §16.4 / §23 Q4 parity:** how much of the Marketplace tab is the live synced catalog (`/api/marketplace/search`, ~30 sources) vs seeded/mock entries in v1? Affects the Marketplace panel's empty/syncing states.
|
||||
4. **Skill "inputs/outputs/memory access" persistence:** the current `SKILL.md` frontmatter (`skill-frontmatter.ts`) has `permissions` but **no structured inputs/outputs**. Builder Steps 3 ("Inputs & Outputs") + 4 ("Tools & Data") imply extending the frontmatter schema — confirm whether to extend `SkillFrontmatter` (and `generateSkillMarkdown`/`serializeFrontmatter`) or store these in skill body markdown only.
|
||||
5. **Archive/rollback in MVP?** Blueprint lists `archive`/`rollback` as skill actions but PRD §16.8 omits them. `skill-retirement.ts` provides the move-to-archive substrate but no HTTP route — include now or defer to a later Extend-governance slice?
|
||||
6. **Install-audit critical-column bug (cross-cutting, substrate-types.md §d gap 2):** `AuditRiskLevel` TS includes `'critical'` but both DDL CHECKs only allow `low/medium/high` — a `record()` with `'critical'` throws. Skill installs route through this audit path; confirm the plan picks up the one-line migration (or that the marketplace's CRITICAL→`'high'`+`approvalClass:'blocked'` mapping is the permanent contract).
|
||||
190
docs/ux-refactor/gap-cards/S07-connector-hub.md
Normal file
190
docs/ux-refactor/gap-cards/S07-connector-hub.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# Gap Card — S07 · Connector Hub
|
||||
|
||||
> Execution model: **in-place incremental refactor** of `apps/web` + targeted backend extensions.
|
||||
> PRD acceptance criteria win over the (directional) mockup. Every claim below is grounded in real files.
|
||||
> PRD = `docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`.
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Connector Hub** (PRD §12.7, blueprint Screen 7, dock app id `connectors`). Purpose: "Connect
|
||||
external tools and data." It is the **Extend** layer surface for service connectors (distinct from
|
||||
**S08 MCP Hub** — PRD §12.8 — which the current `ConnectorsApp` folds in as a second "MCP Servers" tab
|
||||
via `McpCatalog`; the refactor should keep the catalog but the two are separate PRD screens).
|
||||
|
||||
PRD §12.7 acceptance criterion (the bar this card must hit):
|
||||
> **"Users see exactly what tools are connected and whether data is flowing."**
|
||||
|
||||
The "whether data is flowing" half is the load-bearing gap — today there is no sync state, no last-sync
|
||||
timestamp, and no recent-activity surface anywhere in the stack.
|
||||
|
||||
## 2. Required states (PRD/Blueprint)
|
||||
|
||||
PRD §12.7 functional requirements + blueprint Screen-7 state list (`_blueprint_extracted.txt:326-331`,
|
||||
state list `:467`):
|
||||
|
||||
1. **Connected connectors** with **status** and **last sync**.
|
||||
2. **Available connectors** grouped **by category**.
|
||||
3. **Health surface**: health, recent sync activity, **token expiry**, and **errors**.
|
||||
4. **Actions**: connect, **sync now**, manage, **revoke**, **reconnect**.
|
||||
5. **Recommended** connectors based on onboarding + workspace needs.
|
||||
6. State machine per blueprint: `connected · disconnected · expired token · syncing · failed · recommended`
|
||||
(plus list-level `loading / empty / populated / error`).
|
||||
7. RBAC/trust (PRD §17.3, §18.1): "Connectors require consent and revocation path" + "Connector/MCP
|
||||
install audit" (append-only trust trail).
|
||||
|
||||
Mockup (`screen_07_connector_hub.png`, directional) adds visual chrome the PRD does not mandate but
|
||||
implies: top tabs (Connected / Available / Recommended / Built by Waggle), a **Connector Summary** donut
|
||||
(connected/syncing/error/disconnected counts), a **System Health** tile (API/sync/error-rate gauges), a
|
||||
**Recent Activity** feed, and a "Need Help?" card. Treat these as direction, not a contract.
|
||||
|
||||
## 3. Current state in repo (disposition: **rework**)
|
||||
|
||||
**Frontend** — `apps/web/src/components/os/apps/ConnectorsApp.tsx` (356 LOC):
|
||||
- Two-tab sidebar: **Services** (native connectors) + **MCP Servers** (`McpCatalog`). Filter rail = `all / connected / available`.
|
||||
- Renders connectors from `adapter.getConnectors()` grouped by a **hardcoded** `CATEGORIES` map (`:41-50`)
|
||||
+ hardcoded `SETUP_HINTS` (`:52-60`). Per-row expand reveals capability chips, setup steps, and a
|
||||
token/API-key input.
|
||||
- Connect flow (`handleConnect`, `:113-135`): writes credential via `adapter.addVaultSecret('connector:<id>')`
|
||||
**then** calls `adapter.connectConnector(id)` (note: the adapter's `connectConnector` POSTs **no body** —
|
||||
`adapter.ts:1161`; the credential is pre-seeded in vault). Disconnect = `adapter.disconnectConnector(id)`.
|
||||
- `shouldResetCredentialInputs` (`:69-71`) is an exported, regression-tested pure guard
|
||||
(`phase5b-connectors.test`) — **keep**, it prevents credential cross-submission.
|
||||
- Subcomponents (`apps/web/src/components/os/apps/connectors/`): `BrandTile.tsx`, `brand-identity.ts`
|
||||
(logo/colour identity — **keep, reuse**), `McpCatalog.tsx` + `McpServerCard.tsx` + `mcp-registry.ts`
|
||||
(the MCP tab — belongs to **S08**, leave for that card).
|
||||
- **What's MISSING in the UI vs PRD:** no last-sync, no health probe call, no "sync now", no "revoke"
|
||||
(only disconnect), no recommended tab, no recent-activity feed, no summary/health tiles, no token-expiry
|
||||
surfacing. `adapter.getConnectorHealth` **exists** (`adapter.ts:1156`) but is **never called** anywhere
|
||||
(grep-confirmed) — the UI shows only the coarse list `status`.
|
||||
|
||||
**Backend** — `packages/server/src/local/routes/connectors.ts` (121 LOC): 4 routes —
|
||||
`GET /api/connectors`, `GET /api/connectors/:id/health`, `POST /api/connectors/:id/connect`,
|
||||
`POST /api/connectors/:id/disconnect`. Backed by `fastify.connectorRegistry` (`ConnectorRegistry` in
|
||||
`packages/agent/src/connectors/connector-registry.ts`; **30** connectors registered via
|
||||
`setup-connectors.ts`; per-connector `healthCheck()` impls exist in every
|
||||
`packages/agent/src/connectors/*-connector.ts`).
|
||||
|
||||
**Disposition: rework** — the shell, brand tiles, category grouping, and connect/disconnect flow are
|
||||
solid and reusable, but the screen is missing the entire "is data flowing?" dimension (sync/health/
|
||||
activity/recommended/audit). This is additive rework on a good base, not a rebuild.
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Rework `ConnectorsApp.tsx` into the PRD Connector Hub** (keep MCP tab in place for S08):
|
||||
|
||||
- **Add tabs** matching PRD: `Connected · Available · Recommended` (mockup also shows "Built by Waggle" —
|
||||
optional). The existing `all/connected/available` filter rail collapses into these tabs.
|
||||
- **Per-row health + last sync**: lazy-call `adapter.getConnectorHealth(id)` for connected rows (already in
|
||||
adapter, currently dead) to render `status` (connected/expired/error), `tokenExpiresAt` (token-expiry
|
||||
badge), `lastChecked`, and (new) `lastSyncAt`. Extract a **new `ConnectorCard.tsx`** component
|
||||
(blueprint names `ConnectorCard`, `:488`) from the inline row JSX (`:262-340`) — status pill, last-sync,
|
||||
health dot, actions menu (Connect / Sync now / Manage / Revoke / Reconnect).
|
||||
- **Recommended tab**: reuse the existing `recommendConnectors(personaId)` from `@waggle/shared`
|
||||
(`connector-recommendations.ts:146`) — today wired **only** into the MCP tab (`McpCatalog.tsx:69`),
|
||||
not service connectors. Forward `personaId` (already a prop, `:28`) to a recommended-connectors section.
|
||||
- **Summary + health + activity tiles** (mockup, directional): a small **`ConnectorSummary`** donut from
|
||||
client-side status counts (no new endpoint), a **System Health** tile from the new
|
||||
`GET /api/connectors/health` aggregate (§5), and a **Recent Activity** feed from the new
|
||||
`GET /api/connectors/activity` (sync/connect/revoke events). The blueprint's reusable `Timeline` and
|
||||
`EvidencePanel` (`:488`) can host the activity list.
|
||||
- **Actions wiring**: add `syncConnector(id)` → `POST /api/connectors/:id/sync`; add `revokeConnector(id)`
|
||||
→ `POST /api/connectors/:id/revoke` (PRD verb; aliases existing disconnect); "Reconnect" reuses the
|
||||
existing connect flow. Surface a `syncing` row state + toasts on `failed`.
|
||||
- **State**: extend the local `Connector` interface (`:31-38`) with optional `lastSyncAt`, `tokenExpiresAt`,
|
||||
`health`, `category`, `recommended`. **Move category off the hardcoded map** onto the
|
||||
`ConnectorDefinition.category` field that already exists in `@waggle/shared` (`types.ts:299`) but is not
|
||||
yet emitted by the row data — reconcile so the UI stops carrying its own `CATEGORIES`.
|
||||
|
||||
**Adapter (`apps/web/src/lib/adapter.ts`) — add 2 methods, reuse 1:**
|
||||
- `syncConnector(id): POST /api/connectors/:id/sync` (NEW).
|
||||
- `revokeConnector(id): POST /api/connectors/:id/revoke` (NEW; or alias to existing `disconnectConnector`).
|
||||
- `getConnectorHealth(id)` already exists (`:1156`) — start calling it; optionally add
|
||||
`getConnectorsHealth(): GET /api/connectors/health` aggregate + `getConnectorActivity()`.
|
||||
|
||||
## 5. Backend work (PRD §16.9 connectors subset)
|
||||
|
||||
> §16.9 spans Connectors + MCPs + Marketplace. This card scopes the **Connectors** rows only; MCP rows
|
||||
> (`/api/mcps*`) and `/api/marketplace*` belong to **S08 / S13**.
|
||||
|
||||
| PRD §16.9 endpoint | Status | Action |
|
||||
|---|---|---|
|
||||
| `GET /api/connectors` | **EXISTS** | `connectors.ts:6` → `registry.getDefinitions()`. **EXTEND** the definition payload to carry `category` (already on the type, `types.ts:299`) + `lastSyncAt` so the UI drops its hardcoded `CATEGORIES`/sync-shim. |
|
||||
| `POST /api/connectors/:id/connect` | **EXISTS** | `connectors.ts:55`. **EXTEND**: write an `install-audit` entry (see audit gap below) on success. |
|
||||
| `POST /api/connectors/:id/sync` | **MISSING** | **NET-NEW** route in `connectors.ts`. No sync substrate exists — `WaggleConnector` (`connector-sdk.ts`) has `connect`/`healthCheck`/`execute` but **no `sync()`**. Minimum viable: probe `healthCheck()` + record a `lastSyncAt` timestamp (persist in vault sub-key `connector:<id>:lastSync` or a small store) + emit an activity event. Full sync (re-pull data) is a larger connector-SDK addition — flag as phased. |
|
||||
| `POST /api/connectors/:id/revoke` | **PARTIAL** | Closest is `POST /api/connectors/:id/disconnect` (`connectors.ts:107`, removes `connector:<id>` + sub-keys). **EXTEND**: add a `/revoke` alias (or rename) that additionally writes an `install-audit` `action:'rejected'`/revoke entry. Same intent, PRD verb. |
|
||||
|
||||
**Additional backend work the PRD requires but §16.9 does not enumerate (implied by §18.1 / §12.7):**
|
||||
- **Install/consent audit on connect & revoke (MISSING write path).** `connectors.ts` does **NOT** call
|
||||
`fastify.auditStore.record(...)` (grep-confirmed — only `marketplace.ts:224-319` writes audit today).
|
||||
PRD §18.1 mandates "Connector/MCP install audit". The substrate exists: `InstallAuditStore`
|
||||
(`packages/core/src/install-audit.ts`) with `AuditCapabilityType` including `'connector'` (`:22`),
|
||||
actions `installed/rejected` (`:15`). **EXTEND** connect/revoke to record entries. ⚠ **Latent enum-drift
|
||||
bug** to avoid: TS `AuditRiskLevel` allows `'critical'` (`install-audit.ts:16`) but both DDL CHECKs only
|
||||
allow `low/medium/high` (`install-audit.ts:65`, `schema.ts:130`) — pass `'high'`, never `'critical'`.
|
||||
- **Audit read route (MISSING).** No HTTP surface lists the audit trail (`getRecent`/`getByCapability`
|
||||
have no route). The Connector Hub's "Recent Activity" + the Extend trust-trail view need a NEW read,
|
||||
e.g. `GET /api/connectors/activity` (or a shared `GET /api/extend/audit` filtered to `type:'connector'`).
|
||||
Net-new but reads existing `install_audit` rows — no migration.
|
||||
- **System-health aggregate (optional, mockup).** A `GET /api/connectors/health` that fans
|
||||
`registry.healthCheck()` over connected connectors for the System-Health tile. Net-new thin aggregator;
|
||||
no new substrate.
|
||||
|
||||
**`.mind` migration:** **None required.** `install_audit` already exists in `schema.ts:119`; connector
|
||||
credentials live in the **vault**, not `.mind`. `lastSyncAt` can live as a vault sub-key or a tiny store —
|
||||
no schema change. (The audit table's pre-existing `risk_level` CHECK drift is a code-discipline note, not
|
||||
a migration.)
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
- **`ConnectorDefinition` / `ConnectorHealth` / `ConnectorStatus` already exist** in
|
||||
`packages/shared/src/types.ts:266-312` and are the canonical contract — the FE should import these
|
||||
instead of the ad-hoc local `Connector` interface in `ConnectorsApp.tsx:31-38` (a lossy duplicate).
|
||||
- **`lib/types.ts` has a thin `Connector`** (frontend mirror, §(d) substrate inventory) — reconcile it to
|
||||
the shared `ConnectorDefinition` shape (add `category`, `tokenExpiresAt`, `lastSyncAt`, optional `health`).
|
||||
- **New optional fields:** `ConnectorDefinition.lastSyncAt?: string` (and surface `category` which is
|
||||
already declared but not always populated). PRD §15.2 lists an `ExtensionType` union — connectors are one
|
||||
member; not blocking for S07 but should be defined when S13 (Marketplace) lands.
|
||||
- **`ConnectorRecommendation`** already exists (`connector-recommendations.ts:38`) — reuse as-is for the
|
||||
Recommended tab.
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **Onboarding (S12, PRD §12.12 Tool Discovery / §16.1)** feeds the Recommended tab's "based on
|
||||
onboarding" signal. `recommendConnectors(personaId)` works standalone today, so S07 is **not blocked** —
|
||||
but the richer "based on workspace needs" recommendation improves once onboarding tool-discovery answers
|
||||
are persisted.
|
||||
- **S08 MCP Hub** shares this app shell (the MCP tab). Coordinate the tab split so reworking S07 doesn't
|
||||
regress the MCP catalog; ideally S07 and S08 land in the same phase or S07 first with the MCP tab
|
||||
untouched.
|
||||
- **Extend trust-trail / Marketplace (S13)** shares the install-audit read route — build the audit write +
|
||||
read once, consume in both. Sequence the audit endpoint before/with S07's Recent Activity.
|
||||
- No hard dependency on Home/Workspace screens.
|
||||
|
||||
## 8. Effort: **M**
|
||||
|
||||
Frontend rework on a solid existing shell (new tabs, ConnectorCard extraction, wire two dead/easy adapter
|
||||
paths) is modest. The backend lift is small-but-real: `/sync` and `/revoke` routes + audit write on
|
||||
connect/revoke + an audit read route + a health aggregate — all over existing substrate (registry, vault,
|
||||
`install_audit`), zero migration. The one true unknown (real data-pull `sync` in the connector SDK) is
|
||||
phaseable to a health-probe-only v1, which keeps this **M** rather than **L**.
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Sync semantics.** Does "sync now" mean (a) re-probe health + stamp `lastSyncAt` (v1, cheap), or
|
||||
(b) re-pull/refresh cached connector data (needs a new `sync()` on `WaggleConnector` + a cache)? PRD
|
||||
§12.7 says "show recent sync activity" but connectors are currently **runtime tools** (`execute(action)`),
|
||||
not background data-syncers. Recommend v1 = health-probe + timestamp.
|
||||
2. **`lastSyncAt` storage.** Vault sub-key (`connector:<id>:lastSync`) vs a small dedicated store vs an
|
||||
`install_audit` derived value? Vault sub-key is lowest-risk and matches the existing `connector:<id>:email`
|
||||
pattern.
|
||||
3. **revoke vs disconnect.** Add `/revoke` as a true alias of `disconnect`, or have `revoke` additionally
|
||||
purge OAuth tokens (`<provider>_oauth_token`, `oauth.ts`) + write a stronger audit entry? PRD §17.3
|
||||
("revocation path") implies the latter for OAuth connectors.
|
||||
4. **Audit scope.** Connector-specific `GET /api/connectors/activity` vs a shared
|
||||
`GET /api/extend/audit?type=connector`? The latter serves S07 + S08 + S13 with one route (preferred).
|
||||
5. **Audit `.mind` location.** `install_audit` is per-`.mind` (per-workspace). Connectors are **global**
|
||||
(vault-scoped, not workspace-scoped). Confirm the audit-store decorator binds to `personal.mind` so the
|
||||
global Connector Hub trail is consistent (substrate inventory §(d) flags this exact ambiguity).
|
||||
6. **Token-expiry source.** `ConnectorHealth.tokenExpiresAt` is populated only on the fallback path
|
||||
(`connectors.ts:49`); confirm each connector's `healthCheck()` surfaces real expiry, else the expired-token
|
||||
state will never show for SDK-backed connectors.
|
||||
270
docs/ux-refactor/gap-cards/S08-mcp-hub.md
Normal file
270
docs/ux-refactor/gap-cards/S08-mcp-hub.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# Gap Card — S08 · MCP Hub
|
||||
|
||||
> UX-refactor planning artifact. Execution model is the LOCKED **in-place incremental refactor** of
|
||||
> `apps/web` + targeted backend extensions. Mockup is directional; PRD acceptance criteria win.
|
||||
> Every claim below cites a real file. PRD = `docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**MCP Hub** — the power-user capability-extension surface for Model Context Protocol servers (PRD §12.8,
|
||||
PRD line 566-579). Belongs to the **Extend** IA layer (PRD §10.4; blueprint line 74, 81: route group
|
||||
`/mcps`). Explicit blueprint directive: **"Do not bury Connectors/MCPs inside Settings"** (blueprint line 79).
|
||||
|
||||
Purpose (PRD §12.8): *"Enable power-user extension through Model Context Protocol servers."* MCPs are
|
||||
auditable capability providers for power users and agents (blueprint line 140). The governing acceptance
|
||||
criterion: **"MCPs are powerful but always visible, scoped, auditable, and reversible."** (PRD line 579).
|
||||
|
||||
Primary user journey — **J11 / Journey 9 "Install MCP"** (PRD lines 736-743; blueprint line 206-208):
|
||||
power user opens MCP Hub → selects e.g. Postgres MCP → reviews risk and scope → approves installation →
|
||||
MCP becomes available to selected workspace/agent → **install audit is recorded**.
|
||||
|
||||
Mockup (directional only — `screen_08_mcp_hub.png`): top toolbar with tabs (Installed / Available /
|
||||
Marketplace / Custom / Remote Registry), search, "Add MCP Server" CTA; left an "Installed MCPs" table
|
||||
(name, status pill, connected-to, last-used, scope, actions) over an "Available MCPs" category-filtered
|
||||
grid; right rail with an "MCP Overview" donut (counts), "MCP Health", "Recent Activity" (audit/log feed),
|
||||
and a "Custom MCP" add affordance.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
**Tabs (PRD §12.8 line 572):** Installed · Available · Marketplace · Custom · Remote Registry.
|
||||
|
||||
**MCP object fields (PRD §12.8 line 573):** name, description, version, status, **connected to**,
|
||||
**last used**, **locality** (local/remote), **risk**, **permissions**, **logs**. Target schema
|
||||
`Extension/MCP` (blueprint line 554, PRD §15.2 `ExtensionType`): `id, type, name, source, version,
|
||||
endpoint, capabilities, riskLevel, approvalClass, status, health, installedAt, approvedBy`.
|
||||
|
||||
**Actions (PRD §12.8 line 574):** install · start/stop · test · scope · view logs · revoke · add custom MCP.
|
||||
Risky MCPs require approval + audit trail (PRD line 575; blueprint line 161 "elevated connectors/MCPs …
|
||||
require approval").
|
||||
|
||||
**Extension lifecycle states (PRD §14.7 lines 906-914):** Available · Installed · Update available ·
|
||||
Installing · Failed install · **Risk approval required** · Disabled/revoked. Plus per-server runtime
|
||||
status (blueprint line 334-335): installed; available; **running**; **stopped**; **error**; risk-approval-needed.
|
||||
|
||||
**Connector/MCP UI states (PRD §14.5 / blueprint line 467):** Loading, empty, populated, error.
|
||||
|
||||
**Scope (acceptance-critical):** an MCP must be scopeable to workspace and/or agent (PRD line 742;
|
||||
blueprint line 207 "scope to workspace/agent"; line 601 "Connector and MCP installs are visible,
|
||||
permissioned, health-checked and auditable"; line 600 "a workspace agent cannot run with hidden
|
||||
memory/tool/MCP access").
|
||||
|
||||
**Offline degradation (blueprint line 516):** offline mode degrades MCPs gracefully while keeping local
|
||||
workspace/memory available.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**There is NO dedicated MCP Hub app today.** MCPs surface in three thin, mostly-static places:
|
||||
|
||||
1. **Static catalog (discovery only) — `apps/web/src/components/os/apps/connectors/McpCatalog.tsx`** +
|
||||
**`McpServerCard.tsx`** + **`mcp-registry.ts`** (a re-export shim of `@waggle/shared`). Rendered as the
|
||||
"MCP Servers" tab inside **`ConnectorsApp.tsx`** (`apps/web/src/components/os/apps/ConnectorsApp.tsx:197-205,348-349`).
|
||||
The catalog data is `MCP_CATALOG` / `MCP_CATEGORIES` / `CATEGORY_EMOJI` in
|
||||
**`packages/shared/src/mcp-catalog.ts`** (`McpServer` interface at `:17`; `MCP_CATALOG` at `:53`; 14
|
||||
categories at `:30`). Per the backend-map (`05f` §12, lines 391-401), this catalog is **static and not
|
||||
connected at runtime** — a discovery/install-command directory.
|
||||
- **The only "install" UX is copy-a-shell-command-then-restart** (`McpServerCard.tsx:30-37,98-138`
|
||||
copies `server.installCmd`; tooltip text `:111-116`: "Copy the command … Run it in your terminal …
|
||||
Restart Waggle"). There is **no in-app install, start/stop, test, scope, revoke, logs, risk, or
|
||||
status** — none of the PRD §12.8 actions/fields exist in the UI.
|
||||
|
||||
2. **Live runtime status (read-only) — `GET /api/capabilities/status`**
|
||||
(`packages/server/src/local/routes/capabilities.ts:35-53,103-110`) returns
|
||||
`mcpServers:[{name,state,healthy,tools}]` from the real runtime. **But the runtime is empty by default**
|
||||
(see #3 below), so this list is always `[]` on a stock install.
|
||||
|
||||
3. **The MCP runtime engine (rich, but unwired) — `packages/agent/src/mcp/mcp-runtime.ts`.**
|
||||
`McpRuntime` has the full lifecycle the PRD needs: `addServer(config)` (`:308`), `removeServer(name)`
|
||||
(`:327`), `startAll`/`stopAll`, per-instance `start()/stop()/callTool()` (`McpServerInstance` `:102,154,178`),
|
||||
`getServerStates()` (`:362`), `getHealthy()` (`:370`), `isServerHealthy(name)` (`:399`), and **per-workspace
|
||||
scoping** via `McpServerConfig.workspaceId` (`:13`) + `getToolsForWorkspace(workspaceId)` (`:385`).
|
||||
**CRITICAL GAP:** the runtime is instantiated empty — `const mcpRuntime = new McpRuntime()` with comment
|
||||
*"empty by default"* (`packages/server/src/local/index.ts:911`), and **nothing ever calls `addServer()`**
|
||||
(grep `mcpRuntime.addServer` / `.mcp.json` / `loadMcpConfig` → 0 matches in `packages/server/src`). So
|
||||
there is no persistence layer that loads installed MCP configs at boot, and no HTTP route to install/
|
||||
start/stop/test/remove a server.
|
||||
|
||||
**Install-audit substrate is present** (`AuditCapabilityType` includes `'mcp'` —
|
||||
`packages/core/src/install-audit.ts:22`; `InstallAuditStore.record/getRecent/getByCapability` exist) but
|
||||
**has no read HTTP route** (substrate-types inventory §d: writes only; `GET /api/audit/installs` exists in
|
||||
`skills.ts` but is a generic recent-installs feed). MCP installs are not currently audited because there is
|
||||
no MCP install path to audit.
|
||||
|
||||
**Adapter:** no `getMcps`/`installMcp`/`testMcp` methods in `apps/web/src/lib/adapter.ts` (grep → 0 matches).
|
||||
|
||||
**Disposition: `create-new`** (with substrate reuse). A net-new **MCPHub app** (`MCPHubApp.tsx`) promoted
|
||||
out of ConnectorsApp's MCP tab, **plus net-new `/api/mcps/*` backend routes wiring the existing `McpRuntime`
|
||||
+ a new persisted config store + install-audit**. The static catalog component (`McpCatalog`/`McpServerCard`)
|
||||
is reused as the "Available"/"Marketplace" tab content. This is the single largest backend gap on the Extend
|
||||
layer — the engine exists but is entirely unwired to UI or persistence.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**New IA placement:** add an `mcp-hub` (or reuse the dead `marketplace`-adjacent slot) `AppId` in
|
||||
`apps/web/src/lib/dock-tiers.ts`, register chrome in `Desktop.tsx` `appConfig` + a `renderAppContent`
|
||||
switch case (per frontend inventory §b lines 123-129), and add a dock entry under the **Extend** zone-parent
|
||||
(`dock-tiers.ts` zone model). MCP Hub is power-user/PRO-tier-gated (PRD personas: power user/developer needs
|
||||
MCPs — PRD line 151).
|
||||
|
||||
**Components to CREATE** (`apps/web/src/components/os/apps/`):
|
||||
- `MCPHubApp.tsx` — shell with the 5 PRD tabs (Installed / Available / Marketplace / Custom / Remote
|
||||
Registry). Mirror `ConnectorsApp.tsx`'s sidebar-tab + filter layout (`:183-216`) so it's visually
|
||||
consistent with the Connector Hub it splits from.
|
||||
- `mcp/InstalledMcpTable.tsx` — table of running/installed servers: name, status pill (running/stopped/
|
||||
error/installing — maps `McpServerState` `mcp-runtime.ts:16`), connected-to, last-used, locality,
|
||||
scope chip, row actions (start/stop, test, scope, view logs, revoke). Drives PRD §12.8 line 573-574.
|
||||
- `mcp/AddCustomMcpForm.tsx` — Custom tab: `{ name, command, args[], env{}, workspaceId? }` → `POST /api/mcps`
|
||||
(matches `McpServerConfig` `mcp-runtime.ts:8-14`). Reuse `injection-scanner` patterns server-side.
|
||||
- `mcp/McpDetailPanel.tsx` (right rail) — MCP Overview counts, MCP Health, permissions/scope editor,
|
||||
**logs viewer**, and the **risk/approval banner** ("Risk approval required" state). Reuse the approvals
|
||||
surface pattern from `ApprovalsApp.tsx` / inline-chat approval (same backend `/api/approval/*`).
|
||||
- `mcp/McpScopeDialog.tsx` — scope-to-workspace/agent picker (writes `workspaceId` onto the server config;
|
||||
later, `mcpIds[]` onto workspace/agent per §6).
|
||||
|
||||
**Components to REUSE (keep-promote):**
|
||||
- `McpCatalog.tsx` + `McpServerCard.tsx` + `mcp-registry.ts` → become the **Available** + **Marketplace** +
|
||||
**Remote Registry** tab bodies. Today `McpServerCard` only shows a copy-command strip
|
||||
(`McpServerCard.tsx:98-138`) — **rework** it to add a real **Install** button (calls the new
|
||||
`installMcp` adapter method) while keeping copy-command as the offline fallback.
|
||||
- `recommendConnectors` from `@waggle/shared` (already used `McpCatalog.tsx:15,69`) for "Recommended" tile.
|
||||
- `LockedFeature.tsx` for tier gating; `ContextMenu.tsx` for row actions; shadcn `table`, `badge`, `dialog`,
|
||||
`tabs`, `tooltip` primitives (`components/ui/*`, frontend inventory §e).
|
||||
|
||||
**Adapter methods to ADD** (`apps/web/src/lib/adapter.ts` — the single sidecar gateway, §c lines 162-265):
|
||||
`getMcps()`, `installMcp(payload)`, `addCustomMcp(config)`, `startMcp(id)`, `stopMcp(id)`, `testMcp(id)`,
|
||||
`scopeMcp(id, scope)`, `revokeMcp(id)`, `getMcpLogs(id)` — one per PRD §16.9 / §12.8 action.
|
||||
|
||||
**New hook:** `hooks/useMcps.ts` (mirrors the `useConnectors`-style pattern) returning
|
||||
`{ installed, available, custom, install, start, stop, test, scope, revoke, refresh }`. Wire SSE/poll over
|
||||
`/api/capabilities/status` or a new `/api/mcps` for live status (cache-invalidate on
|
||||
"connector sync" / install completion per blueprint line 514).
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.9 endpoints — all MCP rows)
|
||||
|
||||
> All net-new routes live in a new `packages/server/src/local/routes/mcps.ts`, registered in
|
||||
> `local/index.ts`, wiring the already-built `McpRuntime` (`server.agentState.mcpRuntime`,
|
||||
> decorated `local/index.ts:1361`). **No new SQLite table is strictly required** — but a persisted
|
||||
> config source IS (see migration flag).
|
||||
|
||||
| PRD §16.9 endpoint | Status | Extend vs net-new + substrate it touches |
|
||||
|---|---|---|
|
||||
| `GET /api/mcps` | **PARTIAL → build net-new route over existing data** | No `/api/mcps` exists (grep-confirmed in backend-routes inventory, line 422 & §16.9 line 522). The data is **derivable today**: live runtime status from `McpRuntime.getServerStates()/getHealthy()/getAllTools()` (already surfaced in `capabilities.ts:35-53`), enriched with catalog metadata from `MCP_CATALOG` (`@waggle/shared`). **Net-new** thin route in `mcps.ts` that joins runtime state + persisted config + catalog into the `Extension/MCP` shape (§2). Touches: `McpRuntime` (agent), `MCP_CATALOG` (shared), new config store. |
|
||||
| `POST /api/mcps/install` | **PARTIAL → net-new, route through existing install path** | No `/api/mcps/install` (inventory §16.9 line 523). Closest install paths are marketplace (`POST /api/marketplace/install` — SecurityGate + audit, `marketplace.ts:224-319`) and plugin install (`POST /api/plugins/install`). **Net-new** MCP install that: (a) persists an `McpServerConfig`, (b) calls `mcpRuntime.addServer(config)` + `start()` (`mcp-runtime.ts:308,102`), (c) runs the **SecurityGate + writes an `install_audit` row** with `capability_type:'mcp'` (`install-audit.ts:22`) — satisfying the "install audit is recorded" acceptance step (PRD line 743). Risky servers → return `risk approval required` state instead of starting. Tier: PRO (match marketplace). |
|
||||
| `POST /api/mcps/:id/test` | **MISSING** | No MCP test/health route (inventory §16.9 line 524). Closest analog is `GET /api/connectors/:id/health`. **Net-new**: resolve the server, `start()` if needed, assert `isHealthy()` (`mcp-runtime.ts:94,399`) and/or do a `tools/list` round-trip, return health + discovered tools. Touches `McpRuntime`. |
|
||||
| `POST /api/mcps/:id/revoke` | **MISSING** | No MCP revoke/uninstall by id (inventory §16.9 line 525). Closest: `DELETE /api/plugins/:name`. **Net-new**: `mcpRuntime.removeServer(name)` (`mcp-runtime.ts:327`) + delete persisted config + write `install_audit` `action:'rejected'`/`'revoked'`. Touches `McpRuntime` + config store + install-audit. |
|
||||
| `GET /api/marketplace` | **PARTIAL** | Listing is `GET /api/marketplace/search` (`marketplace.ts`); the Marketplace tab reuses it. Alias of `/search` (inventory §16.9 line 526). Not MCP-specific work. |
|
||||
| `POST /api/marketplace/install` | **EXISTS** | `marketplace.ts` (Tier PRO, SecurityGate) — reused by the Marketplace tab. |
|
||||
|
||||
**Additional routes implied by PRD §12.8 actions not in §16.9 list (net-new):**
|
||||
- `POST /api/mcps` (add **custom** MCP) — blueprint API line 530 lists `GET/POST /mcps`. Persists config +
|
||||
`addServer` + audit. **Net-new.**
|
||||
- `POST /api/mcps/:id/start` and `POST /api/mcps/:id/stop` — PRD §12.8 "start/stop" action. Map to
|
||||
`McpServerInstance.start()/stop()` (`mcp-runtime.ts:102,154`). **Net-new.**
|
||||
- `PATCH /api/mcps/:id/permissions` (scope/permissions) — blueprint API line 530 (`PATCH /mcps/:id/permissions`).
|
||||
Writes `workspaceId`/scope onto the persisted config (runtime honors it via `getToolsForWorkspace`,
|
||||
`mcp-runtime.ts:385`). **Net-new.**
|
||||
- `GET /api/mcps/:id/logs` — PRD §12.8 "view logs". No log capture exists in `McpServerInstance` today
|
||||
(stderr is piped `:113` but not retained). **Net-new** + small runtime change to buffer stderr/state-change
|
||||
events for retrieval.
|
||||
- `GET /api/extend/audit` (or extend `/api/audit/installs`) — surface the install-audit trail for the MCP Hub
|
||||
right-rail "Recent Activity". `InstallAuditStore.getRecent()` exists but has no MCP-scoped HTTP read
|
||||
(substrate-types §d gap #1). **Net-new** (implied addition to §16.9, flagged in substrate inventory).
|
||||
|
||||
**Migration / persistence flag (IMPORTANT):**
|
||||
- **No `.mind` SQLite migration is required for the MCP entity itself** — installed MCP configs can persist as
|
||||
a JSON file (mirrors `workspace.json` / `.mcp.json` convention; note repo root already has an untracked
|
||||
`.mcp.json` per git status) read at boot to populate `mcpRuntime.addServer()`. **This persistence layer is
|
||||
entirely net-new** (today `mcpRuntime` is empty and never populated — `local/index.ts:911`). This is the
|
||||
load-bearing backend gap: without it, "Installed" MCPs do not survive restart.
|
||||
- **Install-audit DDL drift (latent, flag for the plan):** `AuditRiskLevel` TS includes `'critical'`
|
||||
(`install-audit.ts:16`) but both CHECK constraints allow only `low|medium|high`
|
||||
(`install-audit.ts:65`, `schema.ts:130`). Any MCP install recording `riskLevel:'critical'` would throw a
|
||||
CHECK violation — map CRITICAL → `'high'` + `approvalClass:'blocked'` as marketplace.ts already does
|
||||
(`marketplace.ts:224-319`), or fix the DDL.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
- **`ExtensionType`** (PRD §15.2 line 953: `'skill'|'connector'|'mcp'|'model'|'template'|'external_tool'`)
|
||||
— **MISSING** in `apps/web/src/lib/types.ts` (substrate-types §e: none of the §15.2 unions exist in FE).
|
||||
Add it.
|
||||
- **`Extension` / `Mcp` interface** (blueprint line 554: `id, type, name, source, version, endpoint,
|
||||
capabilities, riskLevel, approvalClass, status, health, installedAt, approvedBy`) — **MISSING**. No
|
||||
`Extension`/`Mcp` type anywhere in FE. Add to `lib/types.ts` (and ideally `packages/shared/src/types.ts`
|
||||
so the sidecar route and FE share it). The existing `McpServer` (`@waggle/shared` `mcp-catalog.ts:17`:
|
||||
`id,name,description,author,category,url,installCmd,capabilities,official?,logo?`) is the **catalog/discovery**
|
||||
shape — distinct from the **installed-instance** `Mcp` shape; keep both, the Hub joins them by `id`.
|
||||
- **Runtime types already exist server-side** and should be the source of truth for the route contract:
|
||||
`McpServerConfig`, `McpServerState`, `McpToolInfo` (`packages/agent/src/mcp/mcp-runtime.ts:8-22`). Mirror
|
||||
`McpServerState` (`'starting'|'ready'|'error'|'stopped'`) into the FE status union (map to PRD §14.7 states).
|
||||
- **Reuse from install-audit:** `AuditCapabilityType`, `AuditRiskLevel`, `AuditApprovalClass`
|
||||
(`packages/core/src/install-audit.ts:15-22`) for the riskLevel/approvalClass/audit fields — do not invent
|
||||
parallel enums.
|
||||
- **`mcpIds[]` on Workspace/Agent** (PRD §15.3 `WorkspaceConfigV2.mcpIds`, §15.5 Agent `mcpIds`) —
|
||||
**MISSING** from both `WorkspaceConfig` (`workspace-manager.ts`, substrate-types §a) and `AgentDef`
|
||||
(`packages/shared/src/types.ts:36-47`, §e). Needed for the "scope to workspace/agent" action. Additive
|
||||
optional field (no DB migration — `workspace.json` is JSON).
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **Connector Hub (S07)** — MCP Hub is split OUT of `ConnectorsApp.tsx`'s MCP tab; do the Connector Hub
|
||||
promotion first (or jointly) so the shared sidebar-tab/filter/health/state patterns are settled and the
|
||||
MCP tab can be cleanly extracted. Same `Extend`-layer dock placement work.
|
||||
- **Extend layer / dock-zone IA** — requires the new `AppId` + dock-zone wiring (frontend inventory §b/§d:
|
||||
consolidate on `AppId`, add Extend zone entries). Blocks any new Extend app from being reachable.
|
||||
- **Install-audit read route (§d gap)** — shared with Skills (S?) and Connectors governance; build once,
|
||||
reuse across the Extend layer.
|
||||
- **Approvals surface** — risk-approval flow reuses existing `/api/approval/*` + `ApprovalsApp` patterns;
|
||||
no new approval substrate, but the wiring depends on that surface staying stable.
|
||||
- **Marketplace** — the Marketplace tab reuses `GET /api/marketplace/search` + `POST /api/marketplace/install`
|
||||
(already exist); ensure marketplace MCP packages are tagged so they route to the MCP install path.
|
||||
- **Phase hint:** later phase. Backend wiring (persisted config store + runtime population + `/api/mcps/*`)
|
||||
is the prerequisite for any non-static UI; the static catalog already ships, so this is a depth upgrade,
|
||||
not a day-1 blocker. Sequence after Home/Workspace/Memory core screens.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **XL**
|
||||
|
||||
Largest Extend-layer gap: the runtime engine exists but is **completely unwired** (empty `McpRuntime`, no
|
||||
persistence, no HTTP surface, copy-command-only UI). Requires a net-new persisted MCP-config store + boot-time
|
||||
runtime population + ~8 net-new sidecar routes (install/start/stop/test/scope/revoke/logs + custom add) +
|
||||
SecurityGate/audit integration + a full new app shell with 5 tabs, installed-table, detail/logs panel, scope
|
||||
dialog, risk-approval flow, and adapter/hook layer — while preserving the static catalog as the
|
||||
Available/Marketplace tabs. (Frontend-only would be M; the backend wiring + persistence + governance is what
|
||||
pushes it to XL.)
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Persistence location/format for installed MCP configs** — JSON file (`.mcp.json` at dataDir, mirroring
|
||||
`workspace.json`; root already has an untracked `.mcp.json`) vs a new `install_audit`-adjacent table?
|
||||
Recommendation: JSON file (no migration), but confirm the dataDir path + multi-workspace scoping model.
|
||||
2. **Scope model** — is an MCP scoped by writing `workspaceId` onto its single config (1 server : 1 workspace,
|
||||
per current `McpServerConfig.workspaceId`), or by an `mcpIds[]` membership array on each workspace/agent
|
||||
(N:N)? PRD §15.3/§15.5 imply `mcpIds[]` (N:N); the runtime today only supports the single-`workspaceId`
|
||||
field (`mcp-runtime.ts:13,385`). N:N needs a runtime change.
|
||||
3. **Logs capture** — `McpServerInstance` pipes stderr but does not retain it (`mcp-runtime.ts:113`). Add a
|
||||
ring-buffer of stderr + stateChange events for `GET /api/mcps/:id/logs`, or defer logs to a later phase?
|
||||
4. **"Remote Registry" tab semantics** — is this the Composio gateway (already referenced in
|
||||
`McpCatalog.tsx:106,294-301`), the official `modelcontextprotocol/servers` registry, or a remote
|
||||
(HTTP/SSE-transport) MCP class distinct from local stdio? The runtime today is **stdio-only**
|
||||
(`mcp-runtime.ts:108-115`); remote-transport MCPs would need a new transport in `McpServerInstance`.
|
||||
5. **Install execution surface** — does in-app "Install" run `npx …` (spawn a child process to install the
|
||||
package) in the sidecar, or only register config + rely on a globally-installed binary? Security review
|
||||
needed (spawning installers vs the current copy-to-terminal model). Tauri IPC/CSP implications.
|
||||
6. **Tier gate** — confirm MCP Hub is PRO+ (matches marketplace install gate) vs power-user-density-tier
|
||||
(`UserTier`) only. Three tier vocabularies to reconcile (frontend inventory §f).
|
||||
144
docs/ux-refactor/gap-cards/S09-agent-center.md
Normal file
144
docs/ux-refactor/gap-cards/S09-agent-center.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# Gap Card — S09 Agent Center
|
||||
|
||||
> UX-refactor planning artifact. Execution model: **in-place incremental refactor** of `apps/web` + targeted backend extensions over the existing sidecar/substrate. Mockup is directional; PRD §12.9 + §16.7 acceptance criteria win.
|
||||
> PRD: `docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md` (§12.9 lines 581-595, §16.7 lines 1112-1120, §15.5 lines 1015-1035).
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Agent Center** — manage agents as explicit, governed work actors (PRD §12.9, blueprint Screen 9, page 36). Per blueprint p.8 "Agent Model" (lines 125-136), an Agent is a scoped worker carrying goal, model, autonomy, memory scopes, skills, tools, MCPs, permissions — distinct from a Persona (a prompt+tools template). The Center is the list/manage surface; the **Agent Builder** (S18, PRD §12.9 + blueprint p.45) is the 5-step create flow.
|
||||
|
||||
Mockup (`screen_09_agent_center.png`, directional): top category tab strip (All Agents / Personal / Workspace / Team / Autonomous / Archive) + Templates + Filters + **Create Agent**; a KPI metric row (active count, success-rate avg, run counts, hours-saved, cost); a left list/table of agent rows (avatar, name, goal sub-line, type chip, model, status pill, success-rate bar, owner avatars, last-run, sparkline); a right rail (Agents by Type donut, Top Capabilities, Recent Activity, Quick Actions). Search + sort + pagination on the list.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD/Blueprint)
|
||||
|
||||
PRD §12.9 functional requirements:
|
||||
- **Agent categories:** Personal, Workspace, Team, Autonomous, Templates, Archive (tabs).
|
||||
- **Agent card/list** shows: goal, status, owner, workspace, capabilities, model, success rate, last run.
|
||||
- **Agent Builder** steps: Basic Info → Capabilities → Memory & Tools → Permissions → Review & Create.
|
||||
- **Agent fields** (§15.5): name, goal, description, persona/avatar, model, autonomy, type, memoryScopes, skillIds, connectorIds, mcpIds, permissions, status, createdBy, lastRunAt, successRate.
|
||||
- **Agent safety:** no hidden tool/memory access; all elevated access reviewed (blueprint p.8: "Agent Builder must require model, goal, memory scope, skills/tools/MCPs and autonomy level"; "run logs link to execution_traces and ai_interactions"; "Autonomous agents require explicit schedule/trigger and stop/pause").
|
||||
|
||||
PRD agent state model (§14.5): Draft · Idle · Running · Paused · Failed · Waiting for approval · Completed · Archived. Blueprint p.18 (line 342): Idle; running; paused; failed; needs approval; archived.
|
||||
|
||||
Global states (§14.1) every screen must implement: Loading · Empty · Populated · Error · Offline/local-only · Syncing · Permission denied · Partial data · Approval required.
|
||||
|
||||
Acceptance: "User can explain what an agent can see and do before enabling it." (PRD line 595.)
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo (exact files + what they do)
|
||||
|
||||
**Disposition: `rework`** (the AgentsApp surface is a Personas manager, not the PRD Agent Center; the new IA needs an agent-as-actor object the current code does not model — but it reuses persona/group/fleet/trace substrate, so it is a rework + create-new-entity, not a from-scratch replacement).
|
||||
|
||||
Live frontend (grep-confirmed under `apps/web/src/components/os/apps/`):
|
||||
- `AgentsApp.tsx` (346 LOC) — titled **"Personas"**, NOT "Agents". Two tabs: `agents` (= persona catalog) + `groups` (= agent groups). Loads `adapter.getPersonas()` + `adapter.getCapabilityStatus()` (for tool list) + `adapter.getAgentGroups()`. CRUD over **personas** (`createPersona/updatePersona/deletePersona/generatePersona`) and **groups** (`create/update/delete/runAgentGroup`). No goal/status/owner/successRate/last-run/autonomy concept; no category tabs; no KPI row; no run/pause controls on a card.
|
||||
- `agents/AgentCard.tsx` (48 LOC) — avatar + name + description + delete (custom only) + chevron. No status pill, success bar, model, owner, last-run.
|
||||
- `agents/AgentDetail.tsx` (108 LOC) — persona detail: tools list, suggested commands, workspace affinity. No run/pause/traces, no permissions panel.
|
||||
- `agents/CreateAgentForm.tsx`, `CreateGroupForm.tsx`, `GroupCard.tsx`, `GroupDetail.tsx`, `GroupExecutionPanel.tsx` — persona/group create+exec UI. `CreateAgentForm` is a single form (name/description/icon/tools/systemPrompt), NOT the PRD 5-step Builder.
|
||||
- `agents/types.ts` — `BackendPersona` (id/name/description/icon/affinity/commands/tools/systemPrompt/custom), `AgentGroup`, `GroupExecState`. **No Agent type with goal/autonomy/status/successRate.**
|
||||
- `overlays/SpawnAgentDialog.tsx` (447 LOC) — ad-hoc sub-agent run: task + persona-override + model + parent-workspace; 2-step config→confirm with cost estimate; calls `adapter.spawnAgent()` → `POST /api/fleet/spawn`. This is the real "run an agent" path today, but it is launch-only, not a managed agent.
|
||||
- `apps/MissionControlApp.tsx` (227 LOC) — live **fleet** sessions (`adapter.getFleet()`), per-session pause/resume/stop (`fleetAction`), plus team/activity tabs and AI-tool inventory. This is the closest existing "running agents" view; it shows sessions, not agent definitions.
|
||||
- `apps/RoomApp.tsx` + `apps/WaggleDanceApp.tsx` + `apps/EventsApp.tsx` — live sub-agent tiles / coordination signals / event stream (run-time visibility, not agent management).
|
||||
|
||||
Registration: `agents` is an `AppId` in `lib/dock-tiers.ts` and routed in `Desktop.tsx` `renderAppContent`; `mission-control` is a separate AppId. Frontend inventory confirms: `AgentsApp` = "Personas manager" mapped to Intelligence bucket (`_inventory/frontend.md:29,361`).
|
||||
|
||||
Live backend (grep-confirmed, `_inventory/backend-routes.md` §16.7):
|
||||
- Persona catalog CRUD: `/api/personas` GET/POST/PATCH/DELETE + `/generate` (`personas.ts`; POST/generate are **Tier: PRO**). 22 built-in personas in `packages/agent/src/persona-data.ts` + custom from disk (`05a:220-261`).
|
||||
- Agent groups: `/api/agent-groups` GET/POST/PATCH/DELETE + `/run` — **`/run` is a placeholder stub, does NOT execute** (`03a:221`, JSON file `dataDir/agent-groups.json`).
|
||||
- Sub-agent orchestrator state: `GET /api/agents/active` → `{ workers, active }` (`03a:136`).
|
||||
- Fleet (real run/pause/resume/kill of sessions): `GET /api/fleet`, `POST /api/fleet/spawn`, `POST /api/fleet/:workspaceId/{pause,resume,kill}` (`backend-routes §1.13`).
|
||||
- One-shot retrieval: `POST /api/agent/run` (SSE, separate path; `03a:228`).
|
||||
- Trace store: `execution_traces` table (`mind/schema.ts:199`) with per-trace outcome/cost/duration; `TraceRecorder` auto-wires from agent loop (`05a:65`). **No HTTP listing route for traces** (`_inventory/backend-routes.md §16.7` → `/traces` PARTIAL).
|
||||
|
||||
**Key finding:** PRD's `/api/agents/*` (CRUD + run/pause/traces) is the **Cloud** server's Clerk-gated `routes/agents.ts`, NOT the sidecar. In the desktop (sidecar) context the entire §16.7 agent CRUD is **MISSING locally** (`backend-routes.md` §16.7 note, lines 488-492). There is **no persisted Agent entity** with PRD §15.5 fields anywhere local.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Rework `AgentsApp.tsx` into the Agent Center** (keep the file; restructure). Concrete:
|
||||
|
||||
- **New `AgentCenter` shell** (rework `AgentsApp.tsx`): category tab strip (All / Personal / Workspace / Team / Autonomous / Templates / Archive) replacing the current Personas/Groups toggle. Keep Groups as a sub-view or fold groups under "Team/Autonomous". Add KPI metric row (active count, avg success rate, runs, est. hours saved, est. cost) — sourced from existing `getAgentStatus`/`getCostSummary`/`getFleet` + new agent list.
|
||||
- **New `AgentRow`/rework `AgentCard.tsx`**: avatar, name, goal sub-line, type chip (`AgentType`), model, **status pill** (PRD §14.5 union), **success-rate bar**, owner avatar(s), last-run relative time, run/pause action buttons. Reuse `components/ui/badge` (status), `progress` (success bar), `avatar`. Status colors can follow `MissionControlApp` statusColors map.
|
||||
- **Rework `AgentDetail.tsx`**: add Goal, Autonomy, Memory scope, Permissions panel, Run/Pause controls, and a **Traces** tab (links to `execution_traces` via new `/api/agents/:id/traces`).
|
||||
- **New `AgentBuilder`** (PRD §20.3 "create"; blueprint S18): 5-step stepper (Basic Info → Capabilities → Memory & Tools → Permissions → Review & Create). Reuse the design-system **Builder stepper** (PRD §19.1) and the existing model picker `components/os/ModelSelector.tsx` + `lib/spawn-agent-helpers.ts` (model default selection) + persona/skill/connector/MCP multi-selects. `CreateAgentForm.tsx` (persona form) is a partial reuse target for the Basic Info + tools steps but must be extended to the agent contract.
|
||||
- **Right rail**: Agents-by-Type donut (reuse `components/ui/chart`), Top Capabilities, Recent Activity (reuse fleet/events feed), Quick Actions.
|
||||
- **Wire run/pause to fleet**: card "Run" → `/api/agents/:id/run` (which maps onto `fleet/spawn` per §5); "Pause" → `/api/agents/:id/pause` (maps onto `fleet/:wid/pause`). Keep `SpawnAgentDialog` as the quick-launch entry but have it also accept a saved agent id.
|
||||
- **States**: implement all §14.1 globals — Loading (existing `Loader2`), Empty ("No agents — create one"), Error (existing error banner pattern in `AgentsApp`), Offline (`useOfflineStatus`), Permission denied (Team agents gated by `useBilling`/tier), Approval required (reuse `ApprovalsApp`/inline approval surface for elevated agent actions).
|
||||
|
||||
**Adapter methods/hooks (add to `lib/adapter.ts` — the single sidecar gateway):**
|
||||
- `getAgents()`, `getAgent(id)`, `createAgent(body)`, `patchAgent(id, body)`, `runAgent(id, opts)`, `pauseAgent(id)`, `getAgentTraces(id)`.
|
||||
- New `useAgents()` hook (mirror `useWorkspaces` shape: list/select/create/patch/run/pause/refresh). Reuse `useAgentStatus`, `useEvents`, `useRoomState`, `getFleet` for live run state.
|
||||
|
||||
**Shared types (FE):** add `Agent`, `AgentType`, `AutonomyLevel`, `AgentStatus`(state union) to `lib/types.ts` (see §6).
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.7, against the local sidecar)
|
||||
|
||||
> Naming collision flagged: `/api/agents/*` CRUD currently exists ONLY on the Clerk-gated **Cloud** server (`packages/server/src/routes/agents.ts`), not the sidecar. The desktop talks only to the sidecar, so all of §16.7 is net-new **locally**. Add a new sidecar route file `packages/server/src/local/routes/agents.ts` registered in `local/index.ts`.
|
||||
|
||||
| PRD §16.7 endpoint | Status (sidecar) | Plan — EXTEND vs NET-NEW + substrate |
|
||||
|---|---|---|
|
||||
| `GET /api/agents` | **MISSING** | NET-NEW route. Backed by a **new agent store** (see migration note) — a persisted Agent entity. List can union saved agents + (optionally) derive read-only "agents" from `getPersonas()` for back-compat. Reuse `/api/agents/active` + `/api/fleet` to overlay live status. |
|
||||
| `POST /api/agents` | **MISSING** | NET-NEW. Persist an Agent (§15.5 fields). Closest existing writes: `POST /api/personas` (custom persona) / `POST /api/agent-groups`. Record install/elevated-permission grant to `install_audit` if the agent claims elevated tools/MCPs. |
|
||||
| `GET /api/agents/:id` | **MISSING** | NET-NEW read from the agent store (or map onto persona/group id for legacy). |
|
||||
| `PATCH /api/agents/:id` | **MISSING** | NET-NEW. Closest: `PATCH /api/personas/:id` / `PATCH /api/agent-groups/:id`. |
|
||||
| `POST /api/agents/:id/run` | **PARTIAL** | EXTEND: wire onto `POST /api/fleet/spawn` (`fleet.ts`, real execution — `{task, persona?, model?, parentWorkspaceId?}`). Resolve agent → persona+model+workspace, then spawn. Do NOT use `agent-groups/:id/run` (stub). Substrate: fleet/orchestrator + `execution_traces` via `TraceRecorder`. |
|
||||
| `POST /api/agents/:id/pause` | **PARTIAL** | EXTEND: map agent→active session, reuse `POST /api/fleet/:workspaceId/pause`. |
|
||||
| `GET /api/agents/:id/traces` | **PARTIAL** | EXTEND/NET-NEW thin route reading the `execution_traces` store (`mind/schema.ts:199`) filtered by agent/session; also expose `ai_interactions` links per blueprint p.8. No HTTP listing exists today — add one. Also reuse session timeline `GET /api/workspaces/:wid/sessions/:sid/timeline`. |
|
||||
|
||||
**Substrate touched:** persona catalog (`personas.ts` + `persona-data.ts`), agent-groups JSON store, fleet/sub-agent orchestrator (`05a`), `execution_traces` + `ai_interactions` tables, `install_audit` (for elevated-permission grants), `cron` (for Autonomous agents' schedule/trigger — blueprint p.8 line 136).
|
||||
|
||||
**.mind migration flag (REQUIRED for full §15.5 persistence):** there is **no Agent entity/table** in `mind/schema.ts` and personas live on disk (not as agents with goal/autonomy/status/successRate). Two options, lowest-risk first:
|
||||
1. **JSON store (no DB migration):** persist agents to a `dataDir/agents.json` (mirroring the agent-groups JSON pattern, `03a:213`) — additive, reversible, no schema change. Recommended for v1.
|
||||
2. **Mind table:** add an `agents` table to `mind/schema.ts` (bump `SCHEMA_VERSION`, use the established idempotent `ADD COLUMN`/`CREATE TABLE IF NOT EXISTS` migration pattern in `mind/db.ts`). Only if agents must be FTS/relation-queryable.
|
||||
`successRate`/`lastRunAt` are **derivable** from `execution_traces.outcome` + `procedures.success_rate` (`schema.ts:147,206`) — compute at read time rather than store, per the §15.5 note that successRate "partially exists … derivable" (`substrate-types.md:255`).
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
None of the PRD §15.2 agent unions exist today (`substrate-types.md:218-229,249-257`). Add to `apps/web/src/lib/types.ts` (and mirror in `packages/shared/src/types.ts` if the cloud/agent runtime consumes them):
|
||||
|
||||
- `type AgentType = 'personal' | 'workspace' | 'team' | 'autonomous';` — **MISSING** (PRD §15.2 line 951).
|
||||
- `type AutonomyLevel = 'manual' | 'guided' | 'medium' | 'high';` — **MISSING** (PRD §15.2 line 952).
|
||||
- `type AgentRunState = 'draft' | 'idle' | 'running' | 'paused' | 'failed' | 'waiting_for_approval' | 'completed' | 'archived';` — **MISSING** (PRD §14.5). Note the existing FE `AgentStatus` (`types.ts:249-254`) is the cost/model snapshot, NOT this lifecycle union — keep both, distinct names.
|
||||
- `interface Agent { id; name; goal; description?; type: AgentType; personaId?; avatar?; model?; autonomyLevel: AutonomyLevel; workspaceIds?: string[]; teamId?; memoryScopes?: Scope[]; skillIds?: string[]; connectorIds?: string[]; mcpIds?: string[]; permissions?: ...; status: AgentRunState; createdBy?; lastRunAt?; successRate?; createdAt; updatedAt }` — **MISSING entirely.** Closest is shared `AgentDef` (`packages/shared/src/types.ts:36-47`: id/userId/teamId/name/role/systemPrompt/model/tools/config/createdAt) which lacks goal/type/autonomy/status/scopes/successRate — extend or define `Agent` alongside it.
|
||||
- `type Scope = 'personal' | 'workspace' | 'team' | 'organization';` — **MISSING** (PRD §15.2 line 946), shared by S06 Memory; define once.
|
||||
|
||||
The existing `BackendPersona` (`agents/types.ts:3-13`) stays for the persona catalog; the new `Agent` is a distinct object that may *reference* a `personaId`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **Phase 0 (Shell/IA)** — `Agent` shared types + `AgentType`/`AutonomyLevel`/`AgentRunState`/`Scope` unions must land first (consumed here + by S06 Memory + S18 Builder).
|
||||
- **S04 Workspace Desktop** — Workspace agents are scoped by workspace; Agent Center's Workspace/Team tabs depend on `WorkspaceConfigV2.agentIds` (a §15.3 MISSING field, `substrate-types.md:32`) and `type`/`status`. Coordinate the workspace-config additive change.
|
||||
- **S18 Agent Builder** — the create flow is a sibling screen (PRD §12.9 same section); Agent Center's "Create Agent" opens it. Plan together.
|
||||
- **S11 Automation Center** — "Autonomous" agents need a schedule/trigger; depends on cron substrate shared with Automations.
|
||||
- **S08 MCP Hub / S07 Connector Hub / S05 Skills Hub** — the Capabilities/Memory&Tools builder steps pick from those catalogs (`skillIds`/`connectorIds`/`mcpIds`); their list endpoints feed the multi-selects.
|
||||
- **Approval surface** — elevated agent actions reuse `ApprovalsApp` + `/api/approval/*` (already exists).
|
||||
- Lands in **Phase 3 (Intelligence layer)** per PRD §8 / Sprint 6 (§21).
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **L**
|
||||
|
||||
Rework of a 6-file persona surface into a category-tabbed Agent Center, a net-new persisted Agent entity (JSON store v1) + 4 net-new sidecar routes + 3 extend-over-fleet/traces routes, new shared types, plus a 5-step Agent Builder (sibling). Not XL because the run/pause/trace substrate (fleet, execution_traces, orchestrator) already exists and is wired — the work is a management/persistence layer + IA over it, not new agent-execution machinery. Builder may split into its own card to keep this at L.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Agent vs Persona boundary:** does v1 introduce a true persisted Agent object distinct from Persona, or does Agent Center initially just re-skin the persona catalog with derived status? (Recommend: real Agent entity, JSON store, referencing `personaId`.)
|
||||
2. **`/api/agents/*` collision:** confirm the desktop should get its OWN sidecar `agents.ts` rather than proxying the Clerk-gated cloud route (the sidecar has no Clerk). (Backend-map says local is MISSING; assume net-new sidecar.)
|
||||
3. **Persistence choice:** JSON store (`agents.json`, no migration) vs `agents` table in `mind/schema.ts` (SCHEMA_VERSION bump)? PRD §14.4 non-goal favors minimal backend; JSON recommended for v1.
|
||||
4. **successRate/lastRun:** derive at read from `execution_traces`/`procedures`, or persist on the agent? (Derive recommended; storing risks staleness.)
|
||||
5. **Autonomous agents:** are these modeled as Agent + linked cron automation (S11), or a distinct entity? Blueprint p.8 says "explicit schedule/trigger" — likely an Agent that owns a cron id.
|
||||
6. **Categories taxonomy:** PRD lists Personal/Workspace/Team/Autonomous/Templates/Archive (6); mockup shows All/Personal/Workspace/Team/Autonomous/Archive + a separate Templates control. Confirm whether Templates is a tab or a side affordance.
|
||||
7. **Run target resolution:** when an agent has multiple `workspaceIds`, which workspace does `/run` spawn into? Needs a picker or a default.
|
||||
274
docs/ux-refactor/gap-cards/S10-team-workspace.md
Normal file
274
docs/ux-refactor/gap-cards/S10-team-workspace.md
Normal file
@@ -0,0 +1,274 @@
|
||||
# Gap Card — S10 · Team Workspace
|
||||
|
||||
> Screen S10 of the Waggle OS UX-refactor. Execution model is the LOCKED **in-place
|
||||
> incremental refactor** of `apps/web` + targeted backend extensions. Mockups are
|
||||
> directional; PRD acceptance criteria win. Every claim below is grounded in real files.
|
||||
>
|
||||
> Sources: PRD §12.11 (lines 613-626), §16.11 (1152-1158), §17 RBAC (1162-1187),
|
||||
> §10.5 (329-338), §18 (1191-1218); blueprint `_blueprint_extracted.txt` lines 346-349,
|
||||
> 470-473, 509, 514, 531-540, 542-545, J18/J19/J20/J21 (227-242); mockup
|
||||
> `screen_10_team_workspace.png`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Purpose (PRD §12.11):** "Shared intelligence for teams." Team mode must feel like
|
||||
**shared knowledge and shared outcomes**, not just a member list + chat (explicit
|
||||
acceptance criterion, PRD line 626). It is the surface for the **Team layer** of the IA
|
||||
(PRD §10.5): team workspace, members, roles/permissions, shared memory, shared artifacts,
|
||||
shared skills, shared MCPs, and activity/audit.
|
||||
|
||||
This is a **Teams/Enterprise-tier** surface (PRD tier table; current placeholder gates it
|
||||
to "Teams ($49/mo per seat) and Enterprise"). It is the UI home for RBAC (PRD §17) and the
|
||||
team-governance API contract (PRD §16.11).
|
||||
|
||||
The mockup shows: workspace header with a team switcher + Invite/+ buttons; a metrics strip
|
||||
(memory frames, agents, automations, tasks); a "Team Activity" feed; "Pinned" items;
|
||||
"Team Members" panel with avatars/roles; a "Team Intelligence" summary card; "Upcoming"
|
||||
events; and a "Team Goals" progress section. Treat as visual direction only.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
**Tabs (PRD §12.11, line 619):** Overview · Shared Memory · Shared Artifacts · Skills ·
|
||||
Agents · MCPs · Automations · Settings.
|
||||
|
||||
**Overview content (PRD line 620):** team spaces, metrics, members, activity, pinned items,
|
||||
team goals, upcoming events, team intelligence summary.
|
||||
|
||||
**Sharing (PRD line 621):** share memory / artifact / skill / MCP / automation **into team
|
||||
scope subject to role**. Journeys: J19 share memory → choose scope → audit → appears in team
|
||||
memory; J20 share artifact → team/workspace/member → permissions → activity feed (blueprint
|
||||
234-239).
|
||||
|
||||
**Member management + RBAC (PRD line 622):** invite/member management (J18: invite → role
|
||||
selected → accept → permissions applied, blueprint 227-228).
|
||||
|
||||
**RBAC roles (conflict to resolve — see §6):**
|
||||
- PRD §17.2 table: **Owner / Admin / Contributor / Viewer**.
|
||||
- Blueprint §RBAC model + role table: **Owner / Admin / Member / Viewer / Guest** (lines 156,
|
||||
536-540, 603).
|
||||
- Live substrate (`teams.db`): **owner / admin / member / viewer** (no contributor, no guest).
|
||||
|
||||
**Empty / loading / permission states (blueprint 470-473):** Loading, empty, populated,
|
||||
**permission denied**; Invite pending, role conflict, shared/private, audit event. Member
|
||||
actions: request access, change role, resend invite, export audit. J21: permission denied →
|
||||
permission message → request access (**no silent failures or data leakage**, blueprint 240-242).
|
||||
|
||||
**Cache invalidation triggers (blueprint 514):** memory import, artifact update, agent run
|
||||
completion, connector sync, automation completion, **RBAC change** — the Team Workspace must
|
||||
re-fetch on any of these.
|
||||
|
||||
**Solo/offline behavior:** RBAC must be enforced at **both API and UI layers** (blueprint 162).
|
||||
Local team CRUD already works in solo mode with a local userId (see §3).
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo (disposition: **rework**)
|
||||
|
||||
**Frontend — `apps/web/src/components/os/apps/TeamGovernanceApp.tsx` (45 lines):** a pure
|
||||
**static placeholder**. It renders three hardcoded info cards (Role-Based Access / Tool
|
||||
Governance / Audit Trail) + a tier-gate banner. **Zero data fetching, zero adapter calls, no
|
||||
tabs, no members, no activity.** Registered as appId `governance` in `Desktop.tsx`/`Dock.tsx`
|
||||
(see frontend inventory §a, line 47). The mockup's entire surface is unbuilt.
|
||||
|
||||
**Adapter — `apps/web/src/lib/adapter.ts`** has only the **remote-proxy** team methods:
|
||||
`teamConnect` (`:1226`), `teamDisconnect`, `getTeamStatus` (`:1234`), `getTeamMembers`
|
||||
(`:1239`), `getTeamActivity` (`:1247`), `getTeamMessages` (`:1255`), `searchTeamMemory`
|
||||
(`:489`). **There are NO adapter methods for the local `/api/teams/*` CRUD** (create team,
|
||||
get team detail, invite/add member, change role, remove member, team activity/audit). This is
|
||||
the single biggest frontend wiring gap.
|
||||
|
||||
**Backend — substrate is strong and already live.** `packages/server/src/local/routes/team.ts`
|
||||
exposes two prefixes:
|
||||
- `/api/team/*` = **remote team-server proxy** with local fallbacks (connect/disconnect/status/
|
||||
teams/members/presence/activity/messages/governance/memory-search). `/api/team/connect` is
|
||||
`requireTier('TEAMS')` (`:110`); `/api/team/governance/permissions` is `requireTier('ENTERPRISE')`
|
||||
(`:418`).
|
||||
- `/api/teams/*` = **local SQLite CRUD on `teams.db`** (`teams` + `team_members` tables, DDL at
|
||||
`team.ts:51-72`), works in **solo mode** via `getLocalUserId(dataDir)` (`:457`). Full CRUD:
|
||||
`POST /api/teams` (`:460`, auto-adds creator as `owner`), `GET /api/teams` (`:492`),
|
||||
`GET /api/teams/:id` (`:505`, returns team + members), `PUT /api/teams/:id` (`:523`),
|
||||
`DELETE /api/teams/:id` (`:553`, owner-only), `POST /api/teams/:id/members` (`:581`,
|
||||
owner/admin only — **this is invite/add**), `PUT`+`PATCH /api/teams/:id/members/:userId`
|
||||
(`:615`/`:642`, role change), `DELETE /api/teams/:id/members/:userId` (`:665`),
|
||||
`GET /api/teams/:id/activity` (`:690`, reads `audit_events` via `getAuditDb` from `events.js`,
|
||||
`:713-722`).
|
||||
|
||||
**RBAC is already enforced server-side** with per-action checks: owner/admin gate add-member
|
||||
(`:591`), owner-only gate role change in PUT (`:624`) but owner/admin in PATCH (`:649` — a
|
||||
real inconsistency to fix, see §9), owner-only delete-team (`:558`), can't remove the owner
|
||||
(`:682`), member self-removal allowed (`:677`). The local `team_members.role` CHECK constraint
|
||||
is `('owner','admin','member','viewer')` (`team.ts:64`) — **no contributor, no guest**.
|
||||
|
||||
**Remote/cloud governance substrate (backend-map §02b):** the Postgres team layer
|
||||
(`packages/server/src/db/schema.ts`, 20 tables) has `teams`, `team_members` (composite PK
|
||||
`(team_id,user_id)`, role default `'member'`, 02b:93-101), `team_entities`/`team_relations`/
|
||||
`team_resources` (shared KG + assets, 02b:181-228), and the three **team-capability governance**
|
||||
tables added in migration `0001`: `team_capability_policies` (standing policy per role, 02b:232),
|
||||
`team_capability_overrides` (one-off allow/deny, 02b:247), `team_capability_requests`
|
||||
(request→decision queue, 02b:262). These back the per-role tool allow/deny + approval surfaces
|
||||
the mockup/PRD imply, **but only on the cloud server** — the local `/api/team/governance/permissions`
|
||||
proxy (`:418`) is the only sidecar window into them. **Note (02b:44): Postgres FKs are all
|
||||
`ON DELETE no action` — no cascades; deleting a team/user is blocked if children reference it.**
|
||||
|
||||
**Disposition rationale — rework (not create-new):** the `governance` app slot, the dock entry,
|
||||
the tier-gate, and (critically) the **entire local team CRUD + RBAC enforcement substrate already
|
||||
exist**. The screen needs the placeholder component replaced with a real tabbed surface wired to
|
||||
existing routes + new adapter methods — not a new app and not a new backend data store.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Rework `TeamGovernanceApp.tsx`** (rename concept to "Team Workspace"; keep appId `governance`
|
||||
to avoid touching `Desktop`/`Dock`/`dock-tiers` routing) into a tabbed shell. Reuse the existing
|
||||
tabbed-app pattern (`MemoryApp.tsx` 6-tab model, `SettingsApp.tsx` 8-tab model, `ui/tabs.tsx`).
|
||||
|
||||
**Components to create** (keep files small, 200-400 LOC per CLAUDE.md §coding-style):
|
||||
- `apps/team/TeamWorkspaceApp.tsx` (or in-place rework of `TeamGovernanceApp.tsx`) — tab shell:
|
||||
Overview · Shared Memory · Shared Artifacts · Skills · Agents · MCPs · Automations · Settings.
|
||||
Tier-gate stays (Teams/Enterprise) via existing `LockedFeature.tsx` / `useFeatureGate`.
|
||||
- `apps/team/TeamOverviewTab.tsx` — metrics strip + activity feed + pinned + team goals +
|
||||
upcoming + team-intelligence summary (mockup Overview).
|
||||
- `apps/team/TeamMembersPanel.tsx` — member list with avatars + role badges + role-change
|
||||
dropdown + remove (gated by current-user role). Reuse `ui/avatar`, `ui/badge`,
|
||||
`ui/dropdown-menu`.
|
||||
- `overlays/InviteMemberDialog.tsx` — invite by email/userId + role select (J18). Reuse
|
||||
`ui/dialog`, `ui/select`. POSTs to `/api/teams/:id/members`.
|
||||
- `apps/team/TeamActivityFeed.tsx` — audit/activity list (reuse `TimelineApp`/`EventsApp` row
|
||||
styling). Export audit action (blueprint 472).
|
||||
- `apps/team/ShareToTeamDialog.tsx` — scope picker (team/workspace/member) for sharing a
|
||||
memory/artifact/skill/MCP/automation into team scope with role check (J19/J20). Surfaced
|
||||
from Memory/Artifact screens too; lives here as the canonical component.
|
||||
- `components/os/PermissionDenied.tsx` — shared "permission denied + request access" state
|
||||
(J21) — reusable across Team tabs and elsewhere.
|
||||
|
||||
**Reuse targets:** `MemoryApp`'s frame list for the Shared Memory tab (filtered to team scope
|
||||
via `searchTeamMemory`); `CapabilitiesApp`/`AgentsApp`/`ScheduledJobsApp` list rows for the
|
||||
Skills/Agents/MCPs/Automations tabs (read-only team-scoped views first). `ContextMenu` for
|
||||
row-level "Share to team".
|
||||
|
||||
**New hook — `hooks/useTeam.ts`** (mirror `useWorkspaces` shape): returns `{ team, members,
|
||||
activity, currentUserRole, createTeam, invite, changeRole, removeMember, refresh }`. Drives
|
||||
RBAC at the UI layer (blueprint 162): compute `currentUserRole` from `GET /api/teams/:id`
|
||||
members + local userId, then hide/disable actions per PRD §17.2 capability matrix.
|
||||
|
||||
**New adapter methods** (add to `lib/adapter.ts` `LocalAdapter`, local `/api/teams/*` family —
|
||||
this is where new PRD §16 endpoints land per frontend inventory §c): `getTeams`,
|
||||
`getTeam(id)`, `createTeam`, `updateTeam`, `deleteTeam`, `inviteMember(id, {userId?, email?,
|
||||
displayName?, role?})`, `changeMemberRole(id, userId, role)`, `removeMember(id, userId)`,
|
||||
`getTeamAudit(id)`, and (for §16.11) `shareToTeam(...)`.
|
||||
|
||||
**State management (blueprint 511-514):** Team Workspace re-fetches on the RBAC-change cache
|
||||
trigger + memory-import/artifact-update/automation-completion triggers. Use the existing
|
||||
`QueryClient` invalidation pattern.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.11)
|
||||
|
||||
| PRD §16.11 endpoint | Status | Action |
|
||||
|---|---|---|
|
||||
| `GET /api/teams/:id` | **EXISTS** | `team.ts:505` — returns team + members. Wire adapter `getTeam`. No backend change. |
|
||||
| `POST /api/teams/:id/invite` | **PARTIAL → EXTEND** | Invite is implemented as `POST /api/teams/:id/members` (`team.ts:581`, owner/admin gated). **Add a thin `/invite` alias** that forwards to the members handler (PRD uses `/invite`; substrate is `team_members` in `teams.db`). No new substrate. |
|
||||
| `PATCH /api/teams/:id/members/:memberId` | **EXISTS** | `team.ts:642` (`:userId` == PRD `:memberId`); `PUT` variant at `:615`. **Fix the PUT/PATCH role-gate inconsistency** (PUT owner-only vs PATCH owner/admin — §9). Wire adapter `changeMemberRole`. |
|
||||
| `GET /api/teams/:id/audit` | **PARTIAL → ALIAS** | Closest is `GET /api/teams/:id/activity` (`team.ts:690`, reads `audit_events` via `events.js` `getAuditDb`). **Alias `/audit` → the activity handler** (or add the audit-export shape). Substrate: `audit_events` table (events.ts). No new store. |
|
||||
| `POST /api/share` | **MISSING → NET-NEW** | No `/api/share` route anywhere (grep-confirmed across `local/routes`). Sharing into team scope today is **implicit** (workspace `teamId` linkage at create + remote team-server frame sync). Net-new route accepting `{ kind: memory\|artifact\|skill\|mcp\|automation, id, scope: team\|workspace\|member, targetId }`, **role-checked** (PRD §17.2 "Can share"), writing into the team scope and emitting an audit event (reuse `emitAuditEvent`, already used in `team.ts:484`). Touches: `team_entities`/`team_resources` (cloud, 02b) for the synced object, `audit_events` (local) for the trail. For solo/local mode, share = tag the frame/artifact with team scope (note the FE/DB scope is implicit per-`.mind` today — see §6 + substrate-types §c). |
|
||||
|
||||
**Implied additional route (not in §16.11, flag for plan):** an **install-audit / governance read
|
||||
route** for the Skills/Agents/MCPs tabs. `InstallAuditStore.getRecent/getByCapability` exist in
|
||||
`packages/core/src/install-audit.ts` but **have no HTTP endpoint** (substrate-types §d). The
|
||||
Team Workspace governance view (who installed what / risk / trust / approval) needs e.g.
|
||||
`GET /api/extend/audit` or `GET /api/teams/:id/governance` surfacing `team_capability_policies/
|
||||
overrides/requests`. The Enterprise proxy `GET /api/team/governance/permissions` (`team.ts:418`)
|
||||
is the only existing window and is read-only + ENTERPRISE-gated.
|
||||
|
||||
**.mind migration:** **None required for the team CRUD/RBAC core** — `teams.db` is a standalone
|
||||
SQLite file with its own DDL (created on demand). The Postgres `team_capability_*` tables already
|
||||
exist (migration `0001`). The only schema-adjacent work is if team-scope sharing needs a
|
||||
structured `scope`/`teamId` on `memory_frames` (currently implicit per-`.mind`; substrate-types
|
||||
§c flags `teamId`/`scope` as MISSING columns) — that would be a metadata-column migration on
|
||||
`memory_frames`, deferrable behind the implicit-scope approach for v1.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
- **No `Team` / `TeamMember` / `TeamRole` types exist in `apps/web/src/lib/types.ts`.** Add
|
||||
them (frontend inventory §d confirms the types.ts export list has no team entities). Source of
|
||||
truth for the server shapes: `team.ts` `TeamRole = 'owner'|'admin'|'member'|'viewer'` (`:21`),
|
||||
`MemberRow` (`team.ts:799-810` mapper).
|
||||
- **RBAC role-union conflict — MUST be resolved before coding (decision needed, §9):**
|
||||
- PRD §17.2 → `Owner | Admin | Contributor | Viewer`
|
||||
- Blueprint → `Owner | Admin | Member | Viewer | Guest`
|
||||
- Live `teams.db` CHECK → `owner | admin | member | viewer`
|
||||
Recommendation: keep the live 4-role union (`owner/admin/member/viewer`) for v1 to avoid a DB
|
||||
CHECK migration + RBAC-logic rewrite; treat PRD "Contributor" == "Member" and defer "Guest" as
|
||||
a follow-up (it needs a new role + new deny-by-default capability rules). Surface this to the
|
||||
founder per CLAUDE.md §3.1.
|
||||
- **`Scope` union** (`personal/workspace/team/organization`) — MISSING in FE types
|
||||
(substrate-types §e). Needed for `ShareToTeamDialog`. Add to `lib/types.ts` (PRD §15.2).
|
||||
- **Capability/policy types** for the governance tabs (`team_capability_policies/overrides/
|
||||
requests` shapes from 02b) — add FE types when that read route lands.
|
||||
- Reuse existing `TierCapabilities` from `@waggle/shared` (`tiers.ts`) for the tier-gate; do not
|
||||
invent a parallel gate.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **RBAC role-model decision (§6/§9)** blocks the `TeamRole` type + all member-management UI —
|
||||
resolve first.
|
||||
- **`ShareToTeamDialog`** is cross-screen: it is invoked from S-Memory (J19) and S-Artifacts
|
||||
(J20). Artifacts have **no backing entity at all** (substrate-types §e — the single largest
|
||||
entity gap), so artifact-sharing depends on the Artifacts screen/entity landing first; memory-
|
||||
sharing can ship independently against `memory_frames` + `searchTeamMemory`.
|
||||
- **Permission-denied / request-access pattern (J21)** is shared with other screens — build the
|
||||
reusable `PermissionDenied` component here, reuse elsewhere.
|
||||
- Tier gating depends on `useFeatureGate`/`LockedFeature` (exist) — no new dependency.
|
||||
- The remote/cloud governance tabs (per-role capability policies) depend on the **install-audit
|
||||
read route** (§5 implied) which is itself a separate small backend task.
|
||||
|
||||
**Phase placement:** this is a **later-phase** screen. The foundational Work/Intelligence screens
|
||||
(Home, Workspace Desktop, Memory, Agents) and the Artifacts entity should land first; Team
|
||||
Workspace composes their list rows + shares into team scope.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **L**
|
||||
|
||||
The backend core is mostly EXISTS/PARTIAL (CRUD + RBAC enforcement + activity already live, only
|
||||
`/invite` alias + `/audit` alias + the genuinely net-new `/api/share` + a governance read route),
|
||||
but the **frontend is a from-zero rework of a 45-line placeholder into an 8-tab surface** with a
|
||||
new hook, ~8 new components, ~10 new adapter methods, UI-layer RBAC gating, the shared
|
||||
share/permission-denied components, and an unresolved role-model decision. Not XL because no new
|
||||
data store and no `.mind` migration is required for v1; not M because of the tab/share/RBAC breadth
|
||||
+ cross-screen coupling.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **RBAC role model (blocking):** adopt PRD §17.2 (`Owner/Admin/Contributor/Viewer`), blueprint
|
||||
(`+Member +Guest`), or keep the live `teams.db` 4-role union (`owner/admin/member/viewer`)?
|
||||
Recommendation: keep live union for v1, map Contributor→Member, defer Guest. Needs founder sign-off.
|
||||
2. **PUT vs PATCH role-gate inconsistency (real bug):** `PUT /api/teams/:id/members/:userId` is
|
||||
**owner-only** (`team.ts:624`) but `PATCH` on the same path is **owner/admin** (`:649`). The PRD
|
||||
uses PATCH. Which gate is correct — owner-only role changes, or owner+admin? Align both.
|
||||
3. **`/api/share` scope semantics in solo/local mode:** memory frames have **no structured
|
||||
`scope`/`teamId` column** (implicit per-`.mind`, substrate-types §c). For v1, model team-share as
|
||||
(a) implicit via workspace `teamId` + remote sync, or (b) a `memory_frames` metadata migration?
|
||||
Recommendation: (a) for v1, defer (b).
|
||||
4. **Local vs remote team source of truth on this screen:** `/api/teams/*` (local `teams.db`, always
|
||||
works) vs `/api/team/*` (remote proxy, TEAMS-gated). Does Team Workspace render local teams in solo
|
||||
mode and switch to remote when connected, or remote-only? (Affects whether members/activity come
|
||||
from `teams.db` or the team server.)
|
||||
5. **Governance tabs depth for v1:** do the Skills/Agents/MCPs/Automations tabs render full
|
||||
per-role capability policies (`team_capability_*` cloud tables) now, or ship read-only shared-item
|
||||
lists first and defer the policy editor?
|
||||
6. **Postgres no-cascade (02b:44):** deleting a team is blocked by FKs if `team_entities`/resources/
|
||||
members reference it. The local `DELETE /api/teams/:id` deletes members first (`team.ts:563`) — does
|
||||
the cloud delete path need an explicit child-cleanup order, and should the UI warn before delete?
|
||||
249
docs/ux-refactor/gap-cards/S11-automation-center.md
Normal file
249
docs/ux-refactor/gap-cards/S11-automation-center.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Gap Card — S11 Automation Center
|
||||
|
||||
> UX-refactor implementation planning. Screen 11 of the 21-screen inventory.
|
||||
> Execution model is LOCKED: in-place incremental refactor of `apps/web` + targeted
|
||||
> backend extensions. The mockup is DIRECTIONAL; PRD acceptance criteria win over pixels.
|
||||
> Every claim below is grounded in a real file (path + line where load-bearing).
|
||||
>
|
||||
> PRD: `docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`
|
||||
> §12.10 (lines 597-611), §16.10 (lines 1143-1150). Blueprint screen spec: `_blueprint_extracted.txt:354-359`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Automation Center + Automation Builder** — manage scheduled and event-driven work so
|
||||
overnight/background runs are visible, reviewable, and stoppable (PRD §12.10; Definition of
|
||||
Done #7 "automations… have coherent IA"; principle #9 "Automation with trust").
|
||||
|
||||
The PRD object is **Automation** (§11 glossary): "Scheduled/event-driven workflow using
|
||||
triggers/actions/agents. Must be visible/stoppable." In the live repo this is **cron**:
|
||||
`packages/core/src/cron-store.ts` (`CronStore`) + `packages/server/src/local/cron.ts`
|
||||
(`LocalScheduler`) + `packages/server/src/local/routes/cron.ts` (`/api/cron/*`). The backend-map
|
||||
is explicit: "Automations = a rename/extension of cron" (`_inventory/backend-routes.md:531`).
|
||||
|
||||
Two intelligence-layer surfaces overlap here and must NOT be double-built:
|
||||
- **`ScheduledJobsApp.tsx`** (dock id `scheduled-jobs`) — the existing cron CRUD UI; this IS the
|
||||
Automation Center seed.
|
||||
- **`EventsApp.tsx`** (dock id `events`) — agent event/step stream + agent tree; this is the
|
||||
closest existing analog for the Builder's "actions = agent task" and for History/Logs detail,
|
||||
but it streams live `AgentStep`s, NOT cron execution rows. Different data source.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
**PRD §12.10 tabs:** Overview, Running, Scheduled, Triggers, History, Logs.
|
||||
**Automation Builder steps:** Trigger → Condition → Actions → Review & Activate.
|
||||
**Automation fields:** name, trigger, condition, actions, agent, notification, schedule, workspace, status.
|
||||
**Actions:** run now, pause, edit, view logs, retry, disable.
|
||||
**Cross-screen:** "Failed or risky automations surface in Home Cockpit attention required" (§12.10,
|
||||
also Journey 16 / J17 `_blueprint_extracted.txt:224-226`, S01 Home Cockpit gap card dependency).
|
||||
|
||||
**States** (PRD §12.10 + §14.6 Automation states, lines 895-904; blueprint `:356-357`):
|
||||
Draft, Scheduled, Running, Success, Failed, Paused, Awaiting approval, Disabled — plus the §14.1
|
||||
global mandatory states: Loading, Empty, Populated, Error, Offline/local-only, Permission denied.
|
||||
|
||||
**Acceptance:** "Overnight work is visible, reviewable and stoppable" (PRD line 610; blueprint `:358-359`).
|
||||
|
||||
**Mockup (directional only):** header stat tiles (Active 8 / Scheduled 24 / Triggers 12 / Success
|
||||
Rate 98.6% / Hours Saved 156h), an "Automation Health" donut (Healthy/Warning/Failed/Paused), a
|
||||
"Running Automations" list with progress bars, a "Scheduled Automations" table (name / schedule /
|
||||
next-run / workspace / status), a "Recent Activity" feed, and a "Popular Templates" rail. These map
|
||||
to existing substrate (see §5) EXCEPT the analytics tiles (success-rate / hours-saved) and live
|
||||
progress bars, which have no backing today — treat as derived/aspirational, not v1 blockers.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `rework`** (promote + extend `ScheduledJobsApp` into Automation Center; the cron
|
||||
substrate is solid and reused wholesale — this is the textbook in-place refactor case).
|
||||
|
||||
### Frontend (exists)
|
||||
- **`apps/web/src/components/os/apps/ScheduledJobsApp.tsx`** — full cron CRUD UI. Single flat list +
|
||||
inline create form. Calls `adapter.getCronJobs / createCronJob / updateCronJob / deleteCronJob /
|
||||
triggerCronJob`. Create form already collects `jobType` (6 types via `CRON_JOB_TYPES`), schedule
|
||||
(presets + custom cron via `CRON_SCHEDULE_PRESETS`), and an output channel (log/telegram). Per-row:
|
||||
toggle enable/disable, Run-now (`triggerCronJob`, auto-enable handled), delete. **No tabs, no
|
||||
history/logs view, no "Builder" stepper, no triggers concept.**
|
||||
- **`apps/web/src/lib/cron-presets.ts`** — `CRON_JOB_TYPES` (6 job types w/ labels+descriptions),
|
||||
`CRON_SCHEDULE_PRESETS` (6 cadences), `describeCronExpr`, `isPlausibleCronExpr`. Reuse as-is for
|
||||
the Builder's schedule step.
|
||||
- **`apps/web/src/components/os/apps/EventsApp.tsx`** — agent step stream (live/tree/replay tabs),
|
||||
`buildAgentTree`. NOT cron-backed; relevant only as a visual pattern for History/Logs, not a data
|
||||
source.
|
||||
- **`apps/web/src/lib/types.ts:230-238`** — `CronJob { id, name, schedule, workspaceId, enabled,
|
||||
lastRun?, nextRun? }`. Lossy projection (drops `jobType`, `jobConfig`, `nextRun` exists but
|
||||
`lastRunAt`/`nextRunAt` are remapped to `lastRun`/`nextRun` by `normalizeCronJob` in `adapter.ts`).
|
||||
- **`apps/web/src/lib/adapter.ts:811-851`** — cron methods. **Two defects to fix in the rework:**
|
||||
1. `updateCronJob` (`:836`) issues **`PUT /api/cron/:id`**, but the server registers ONLY
|
||||
`PATCH /api/cron/:id` (`routes/cron.ts:115-124`) — there is **no PUT cron route**
|
||||
(grep-confirmed). The toggle in `ScheduledJobsApp.handleToggle` goes through this and would
|
||||
404. Change to PATCH.
|
||||
2. **No `getCronHistory` adapter method** exists, even though the route does (`/api/cron/:id/history`,
|
||||
`notifications.ts:198-210`). History/Logs tabs need it added.
|
||||
|
||||
### Backend (exists, reused wholesale)
|
||||
- **`packages/server/src/local/routes/cron.ts`** — POST/GET(list)/GET(:id)/PATCH/DELETE/POST(:id/trigger).
|
||||
Trigger calls `server.scheduler.executeJob` and auto-enables disabled jobs (`:188-191`), emits a
|
||||
`cron`-category notification (`:197-202`).
|
||||
- **`packages/server/src/local/routes/notifications.ts:198-210`** — `GET /api/cron/:id/history`
|
||||
→ `cronStore.getExecutionHistory(id, limit)` → `{ history, count }`.
|
||||
- **`packages/core/src/cron-store.ts`** — `CronStore`: schedules table (`cron_schedules`), execution
|
||||
history (`cron_execution_history` w/ `success`, `duration_ms`, `result_summary`, `error` — `:88-101`),
|
||||
notifications. `recordExecution` (`:281`), `getExecutionHistory` (`:294`). 6 `CronJobType`s (`:15`):
|
||||
`agent_task | memory_consolidation | workspace_health | proactive | prompt_optimization | monthly_assessment`.
|
||||
- **`packages/server/src/local/cron.ts`** — `LocalScheduler`: tick loop, concurrency guard,
|
||||
per-job failure tracking, **auto-disable after 5 consecutive failures** (`MAX_CONSECUTIVE_FAILURES`,
|
||||
`:23,138-141`) — this maps directly to the §14.6 "Failed"/"Disabled" states.
|
||||
- **`packages/server/src/local/setup-crons.ts`** + `executeJob` wiring (job-type dispatch) — confirm
|
||||
which job types actually execute when planning the Builder's "Actions" step.
|
||||
|
||||
### What's MISSING entirely (net-new frontend, no backend store needed)
|
||||
- **Trigger concept.** Cron is time-only (`cronExpr`). PRD §12.10 "Triggers" tab + Builder "Trigger"
|
||||
step imply event-driven triggers (not just schedules). No event-trigger substrate exists; v1 should
|
||||
scope Trigger = schedule-only, with event-trigger as an open question (see §9).
|
||||
- **Condition concept.** No `condition` field on `CronSchedule`. Builder "Condition" step is net-new
|
||||
(model as optional `jobConfig.condition`, no schema change).
|
||||
- **Analytics** (success-rate %, hours-saved, health donut) — derivable from `cron_execution_history`
|
||||
but no aggregation endpoint/UI today.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
### Components to create / rework
|
||||
| Component | Action | Notes |
|
||||
|---|---|---|
|
||||
| `ScheduledJobsApp.tsx` → **`AutomationCenterApp.tsx`** | **rework/rename** | Promote to tabbed shell: Overview / Running / Scheduled / Triggers / History / Logs (PRD §12.10). Keep the proven cron CRUD wiring; lift the existing flat list into the "Scheduled" tab. Add an Automation Health summary (counts by enabled/disabled/failed from existing data). Keep dock id `scheduled-jobs` OR add an alias (don't break `dock-tiers.ts` `AppId` union + `Desktop.renderAppContent` switch — `frontend.md:142-151`). Update `appConfig` title/icon in `Desktop.tsx`. |
|
||||
| `AutomationBuilder.tsx` (overlay) | **create-new** | 4-step stepper (Trigger → Condition → Actions → Review & Activate) per PRD §12.10. Reuse the existing create-form fields (jobType, schedule presets, output channel) from `ScheduledJobsApp` + `cron-presets.ts`. Use the shadcn stepper-style pattern (PRD §19 "Builder stepper"); align with sibling builders (Agent/Skill builders, other S-cards). |
|
||||
| `AutomationCard.tsx` | **create-new (small)** | Per-automation row: name, schedule summary (`describeCronExpr`), next-run, workspace, status badge (Running/Scheduled/Paused/Failed/Disabled), actions (run-now/pause/edit/logs/delete). Extract from current inline `ScheduledJobsApp` row JSX. |
|
||||
| History/Logs tab content | **create-new** | List `cron_execution_history` rows (executed_at, duration, success/error, result_summary). Visual pattern can borrow `EventsApp` StepCard expand/collapse, but data = cron history, not `AgentStep`. |
|
||||
|
||||
### Reuse targets
|
||||
- `cron-presets.ts` (schedule presets + job-type catalog + `describeCronExpr`) — as-is.
|
||||
- `HintTooltip`, shadcn `tabs`/`table`/`badge`/`skeleton`/`dialog` (`components/ui/*`, `frontend.md:336-342`).
|
||||
- Status-badge + skeleton/empty/error patterns already used across apps.
|
||||
- `EventsApp` StepCard pattern (visual only) for the Logs detail rows.
|
||||
|
||||
### Adapter methods / hooks
|
||||
- **Fix** `adapter.updateCronJob` → `PATCH` (currently `PUT`, 404s — `adapter.ts:837`).
|
||||
- **Add** `adapter.getCronHistory(id, limit?)` → `GET /api/cron/:id/history` (route exists,
|
||||
`notifications.ts:202`).
|
||||
- **Add** `adapter.pauseCronJob(id)` thin helper → `PATCH /api/cron/:id { enabled:false }`
|
||||
(PRD calls it "pause"; backend models it as `enabled:false` — `backend-routes.md:540`).
|
||||
- **(Optional) Add** an `automations` alias namespace in the adapter so the new components read
|
||||
PRD vocabulary while pointing at `/api/cron/*` — keeps the rename cosmetic, zero backend churn.
|
||||
- **New hook `useAutomations`** (mirror `useWaggleDance`/`useNotifications` shape): wraps
|
||||
list/create/update/delete/trigger/pause/history; optionally subscribes to the existing
|
||||
`/api/notifications/stream` (`cron`-category events) for live status. No new SSE channel needed.
|
||||
- Cross-screen: emit failed-automation count into the S01 Home Cockpit "attention required" feed
|
||||
(PRD §12.10; consumes `cron_execution_history.success=0` + scheduler auto-disable signal).
|
||||
|
||||
### Props / state
|
||||
- `AutomationCenterApp`: `activeTab`, `jobs: CronJob[]`, `loading`, `creating`, per-row `triggering`.
|
||||
- `AutomationBuilder`: `step`, `{name, jobType, scheduleExpr, workspaceId, condition?, outputChannel}`,
|
||||
`testing?` (Review step "test" before activate, per PRD §12.10 / Journey 12 step 5).
|
||||
- Extend `CronJob` FE type with `jobType` + `jobConfig` (currently dropped by `normalizeCronJob`) so
|
||||
the Builder can round-trip edits without re-deriving.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.10 endpoint-by-endpoint)
|
||||
|
||||
> Verdict for the whole section: **the cron substrate is complete; §16.10 is a rename/alias job, NOT
|
||||
> net-new backend.** Backend-routes inventory: §16.10 = 0 EXISTS / 6 PARTIAL / 0 MISSING
|
||||
> (`backend-routes.md:595`). All six "Automation" endpoints map onto existing `/api/cron/*` in
|
||||
> `routes/cron.ts` + the history route in `notifications.ts`. Substrate touched: `cron_schedules` +
|
||||
> `cron_execution_history` (in the personal/workspace `.mind` DB via `CronStore`). **No `.mind`
|
||||
> migration required** (both tables already exist with lazy creation, `cron-store.ts:135-157`).
|
||||
|
||||
| PRD §16.10 endpoint | Status | EXTEND vs NET-NEW · substrate |
|
||||
|---|---|---|
|
||||
| `GET /api/automations` | **PARTIAL** | EXTEND: alias of `GET /api/cron` (`cron.ts:94`). Either register an `/api/automations` alias plugin that re-exports the cron handlers, or just point the new UI at `/api/cron`. Substrate: `cron_schedules`. |
|
||||
| `POST /api/automations` | **PARTIAL** | EXTEND: alias of `POST /api/cron` (`cron.ts:67`). To support PRD `condition`, stash it in `jobConfig.condition` (no schema change — `job_config TEXT`). |
|
||||
| `PATCH /api/automations/:id` | **PARTIAL** | EXTEND: alias of `PATCH /api/cron/:id` (`cron.ts:115`). |
|
||||
| `POST /api/automations/:id/run` | **PARTIAL** | EXTEND: alias of `POST /api/cron/:id/trigger` (`cron.ts:172`; auto-enables + executes + emits notification). |
|
||||
| `POST /api/automations/:id/pause` | **PARTIAL** | EXTEND: no dedicated `/pause`; equivalent is `PATCH /api/cron/:id { enabled:false }`. Add a thin `/pause` route OR have the adapter call PATCH (`backend-routes.md:540`). |
|
||||
| `GET /api/automations/:id/logs` | **PARTIAL** | EXTEND: alias of `GET /api/cron/:id/history` (`notifications.ts:202` → `cronStore.getExecutionHistory`). Substrate: `cron_execution_history`. |
|
||||
|
||||
**Implied additions NOT in §16.10 (flag for plan):**
|
||||
- **Overview/analytics aggregation** (mockup tiles: active/scheduled/success-rate/hours-saved + health
|
||||
donut). No endpoint sums `cron_execution_history` today. Either compute client-side from per-job
|
||||
history (cheap for small N) OR add a thin `GET /api/automations/summary`. Hours-saved has no source —
|
||||
drop or stub. **Not a v1 blocker.**
|
||||
- **Builder "test" before activate** (PRD §12.10 / Journey 12). `POST /api/cron/:id/trigger` runs a
|
||||
real (auto-enabling) execution; there is no dry-run. Either reuse trigger as the "test run" (accepting
|
||||
it really runs) OR scope test-run as an open question.
|
||||
|
||||
**.mind migration flag:** NONE. `cron_schedules` + `cron_execution_history` + `notifications` all
|
||||
self-create (`cron-store.ts:135-157`). The only schema-adjacent change is storing `condition` inside
|
||||
the existing `job_config` JSON blob.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
PRD §15 has **no dedicated Automation type** (the §15.2 unions cover Workspace/Memory/Artifact/Agent
|
||||
but not Automation). So the type work is FE-local:
|
||||
|
||||
- **Extend `CronJob`** (`lib/types.ts:230-238`) with `jobType: CronJobType` and `jobConfig?:
|
||||
Record<string, unknown>` (both currently dropped on the FE side by `normalizeCronJob` —
|
||||
`adapter.ts:818`). Keep the legacy `schedule`/`lastRun`/`nextRun` remap for back-compat.
|
||||
- **Add `CronExecutionRow`** FE mirror of the server type (`cron-store.ts:48-57`:
|
||||
`id, schedule_id, schedule_name, executed_at, duration_ms, success, result_summary, error`) for the
|
||||
History/Logs tab — camelCased in the adapter.
|
||||
- **Add an `AutomationStatus` union** for badge rendering: derived (not stored) from
|
||||
`enabled` + last-history-row success + scheduler disabled-set: `'scheduled' | 'running' | 'paused' |
|
||||
'failed' | 'disabled'`. Maps to PRD §14.6.
|
||||
- The 6-member `CronJobType` union is **duplicated** in two places (`cron-presets.ts:15` and
|
||||
`core/cron-store.ts:15`) — reuse `cron-presets.ts`'s for FE; do NOT add a third copy.
|
||||
- No PRD §15.2 union (`AutonomyLevel`, `ExtensionType`, etc.) is required for this screen.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **Phase 3 — Intelligence layer** (PRD §8 / Roadmap Sprint 6, lines 236-240, 1344-1351). Same sprint
|
||||
as Agent Center/Builder + Skills Hub/Builder; the Automation Builder's "Actions = run an agent task"
|
||||
step benefits from Agent Center existing first, but cron's `agent_task` job type already works
|
||||
standalone, so it is **not a hard block**.
|
||||
- **S01 Home Cockpit** (downstream consumer): failed/risky automations must surface in Home "attention
|
||||
required" (PRD §12.10, §12.1). Coordinate the failed-count signal contract with the S01 gap card.
|
||||
- **AppShell / dock IA** (Phase 0): the rename/relocation of `scheduled-jobs` into the Intelligence
|
||||
bucket touches `dock-tiers.ts` `AppId` + `Desktop.tsx` `appConfig`/`renderAppContent`
|
||||
(`frontend.md:142-151`). Sequence after the Phase-0 IA freeze.
|
||||
- Builder stepper pattern should be shared across Agent/Skill/Automation builders — coordinate the
|
||||
common stepper primitive (PRD §19.1 "Builder stepper") rather than three bespoke steppers.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort
|
||||
|
||||
**M.** The backend is essentially free (6 PARTIAL endpoints = thin aliases over existing cron routes +
|
||||
two real adapter fixes). Frontend is a moderate rework: promote the existing single-list
|
||||
`ScheduledJobsApp` to a 6-tab Center, build a 4-step Builder overlay, add a History/Logs tab over an
|
||||
existing route, and wire the Home-Cockpit failed-automation signal. No new data store, no `.mind`
|
||||
migration. Pushes toward L only if event-driven Triggers (vs schedule-only) are pulled into v1.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Triggers tab scope.** PRD §12.10 lists "Triggers" as a tab and a Builder step, but cron is
|
||||
schedule-only. Is v1 schedule-only (Trigger = cron cadence), or must event-driven triggers (e.g.
|
||||
"on harvest complete", "on memory conflict") ship? No event-trigger substrate exists today.
|
||||
2. **Condition step.** Is a real condition engine required, or is storing an advisory
|
||||
`jobConfig.condition` string (no evaluation) acceptable for v1?
|
||||
3. **Builder "test run".** Only `POST /api/cron/:id/trigger` exists and it really executes (and
|
||||
auto-enables). Is reusing trigger as "test" acceptable, or do we need a dry-run path?
|
||||
4. **Rename vs alias.** Register a true `/api/automations/*` alias plugin (PRD vocabulary in the
|
||||
network tab) or keep `/api/cron/*` and rename only in the UI/adapter? (Affects the backend-map
|
||||
contract surface.)
|
||||
5. **Dock relocation.** Move `scheduled-jobs` into a new Intelligence dock zone, or keep its current
|
||||
dock slot and just retitle? Confirm against the Phase-0 IA freeze.
|
||||
6. **Analytics tiles.** Mockup shows success-rate % and "Hours Saved 156h". Success-rate is derivable
|
||||
from `cron_execution_history`; hours-saved has no source. Drop hours-saved, or define a heuristic?
|
||||
7. **Scope of automation = workspace vs global.** Cron supports `workspaceId:'global'` → `'*'`
|
||||
(`cron.ts:73-74`). Does the Center show all-workspace automations, or filter to the active workspace?
|
||||
225
docs/ux-refactor/gap-cards/S12-first-launch.md
Normal file
225
docs/ux-refactor/gap-cards/S12-first-launch.md
Normal file
@@ -0,0 +1,225 @@
|
||||
# Gap Card — S12 First Launch (Onboarding Step 1)
|
||||
|
||||
> Screen S12 of the Waggle OS UX-refactor. PRD source of truth: §12.12 (Onboarding Flow,
|
||||
> step 1 "First Launch - promise and privacy"), §13 Journey 1 step 2, §14.1 global states.
|
||||
> Blueprint screen spec: `_blueprint_extracted.txt:365-372` (PAGE 14, row 12). Mockup
|
||||
> (directional only, PRD §24): `Waggle_OS_Handoff_Assets/screen_12_first_launch.png`.
|
||||
> Execution model: **in-place incremental refactor** of `apps/web` — KEEP the wizard shell,
|
||||
> redesign the welcome step per PRD §20.2.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
The very first thing a fresh-install user sees: a minimal **promise + privacy reassurance**
|
||||
splash before any setup. It is onboarding step 1 of the 7-step flow (PRD §12.12: First Launch →
|
||||
Who Are You → Tool Discovery → Memory Import → Memory Review → Workspace Creation → Home Cockpit).
|
||||
|
||||
Purpose (blueprint S12): "Minimal promise and privacy reassurance." Acceptance (blueprint S12):
|
||||
**"No infrastructure overload before user intent"** — i.e. no API-key/tier/model questions on
|
||||
this screen; only the brand promise, a privacy note, a language affordance, and a single
|
||||
Continue action.
|
||||
|
||||
Mockup content (directional): Waggle "W" logo + wordmark top-left; centered hero logo;
|
||||
`Welcome to Waggle`; tagline `Your work. Your memory. Your agents.`; primary `Continue →`
|
||||
button; a 4-dot progress indicator; footer-left privacy line ("Your data is private. Stored
|
||||
locally."); footer-right language selector showing `English (US)`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
Blueprint S12 names exactly four states (`_blueprint_extracted.txt:369-370`), plus the PRD §14.1
|
||||
global-state baseline that "every major screen must implement":
|
||||
|
||||
| State | Source | What it means on S12 |
|
||||
|---|---|---|
|
||||
| **Fresh install** | Blueprint S12 | Default: brand promise + privacy + Continue. The only state the current code renders. |
|
||||
| **Resumed setup** | Blueprint S12 | User dismissed/closed mid-onboarding and returns; wizard re-opens at the saved step (not necessarily step 0). Resume should land on the persisted step, and First Launch should communicate "picking up where you left off" rather than re-greeting cold. |
|
||||
| **Offline** | Blueprint S12 + PRD §14.1 | Sidecar unreachable. Screen must still render (it is pre-network) and must not block; surface a non-alarming offline indicator and keep Continue usable (downstream steps degrade, not this one). |
|
||||
| **Local-only** | Blueprint S12 + PRD §6/§18.1 (local-first default) | Privacy promise must be truthful and visible: "your data is private / stored locally." This is the trust hook the whole onboarding leans on (PRD §12.12 acceptance: "Nothing imports without explicit review/approval"). |
|
||||
| Interaction: **Continue** | Blueprint S12 | Advance to step 2 (Who Are You). |
|
||||
| Interaction: **Change language** | Blueprint S12 + mockup | A language affordance. **No i18n infra exists** (see §3) — scope decision required (§9). |
|
||||
| Interaction: **View privacy note** | Blueprint S12 + mockup | A privacy note / link, inline or expandable. |
|
||||
|
||||
PRD §14.1 also lists Loading / Error / Permission-denied as universal — for a pre-network welcome
|
||||
splash these collapse to: render immediately (no loading gate), and offline == the only "error-ish"
|
||||
state that matters here.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo (exact files + what they do)
|
||||
|
||||
**Disposition: `rework`** (KEEP the wizard shell per PRD §20.2 "Onboarding wizard → simplify";
|
||||
redesign the step-0 component and add the missing affordances).
|
||||
|
||||
### The shell (KEEP)
|
||||
- `apps/web/src/components/os/overlays/OnboardingWizard.tsx` — full-screen early-return wizard
|
||||
(`OnboardingWizard.tsx:399-576`). Holds 8 steps (`STEP_NAMES`, `:35`):
|
||||
`welcome / why-waggle / tier / memory-import / template / persona / api-key / ready`.
|
||||
Step 0 auto-advances after **3 s** (`:136-141`), Escape → Skip (`:86-96`), a top progress bar +
|
||||
step dots + Back + "Skip setup" chrome (`:421-493`). Rendered by `Desktop.tsx:262-272` when
|
||||
`!onboardingState.completed`. This shell is the reuse target.
|
||||
- `apps/web/src/hooks/useOnboarding.ts` — `OnboardingState` (`:10-19`: `completed, step, tier?,
|
||||
workspaceId?, apiKeySet?, templateId?, personaId?, tooltipsDismissed?`), persisted to
|
||||
`localStorage` key `waggle:onboarding` (`:21`). Auto-completes for returning users via Tauri
|
||||
first-launch flag (`:102-131`) and a sidecar `getWorkspaces().length > 0` check (`:139-176`).
|
||||
`?forceWizard=true` (DEV) forces step 0 (`:47-57`). **This is the "resumed setup" backbone** —
|
||||
`state.step` already persists, so resume is half-built.
|
||||
|
||||
### The screen itself (REWORK)
|
||||
- `apps/web/src/components/os/overlays/onboarding/WelcomeStep.tsx` — the current step-0 component
|
||||
(54 lines). Renders the Waggle logo, eyebrow `Your AI Operating System`, `Welcome to the Hive`,
|
||||
tagline `Persistent memory. Workspace-native. Built for knowledge work.`, a `Continue →` button
|
||||
(`:39-45`) + "or click anywhere". **Gaps vs S12:** copy differs from mockup ("Welcome to the
|
||||
Hive" vs "Welcome to Waggle"; tagline differs); **no privacy note**, **no language selector**,
|
||||
**no offline/local-only awareness**. It is a pure presentational component (props
|
||||
`WelcomeStepProps` = `goToStep` + `onClickAnywhere`, `onboarding/types.ts:10-12`).
|
||||
- `apps/web/src/components/os/overlays/onboarding/WhyWaggleStep.tsx` (step 1, the current 2nd
|
||||
screen) carries the 3 `VALUE_PROPS` (constants `:111-115`) + the "Skip — quick setup" escape
|
||||
hatch. Mentioned because the mockup's tagline-style promise overlaps WelcomeStep/WhyWaggle; the
|
||||
rework should not duplicate value-prop content across both.
|
||||
- `apps/web/src/components/os/overlays/onboarding/constants.ts` — `VALUE_PROPS` (`:111-115`),
|
||||
`fadeSlide` motion preset (`:118-123`), `STEP_NAMES`. Reuse `fadeSlide` for the rework.
|
||||
- `apps/web/src/components/os/overlays/onboarding/types.ts` — `WelcomeStepProps` (`:10-12`).
|
||||
|
||||
### Supporting state already present (REUSE)
|
||||
- `apps/web/src/hooks/useOfflineStatus.ts` — `useOfflineStatus()` returns `offline:boolean`
|
||||
(`:36-99`, 2-consecutive-failure tolerance). Backs the **offline** state. Already consumed by
|
||||
`Desktop.tsx:112` and `StatusBar.tsx`; thread it into the welcome step.
|
||||
- Privacy/local-only copy: **no dedicated component exists**, but the claim is true per PRD §6
|
||||
("Local-first by default") / §18.1. EraseDataDialog / data-erase surfaces exist elsewhere
|
||||
(`overlays/EraseDataDialog.tsx`) but there is no reusable "privacy note" primitive — net-new
|
||||
small UI.
|
||||
- **No i18n infrastructure** — grep for `i18next | react-i18next | useTranslation |
|
||||
LanguageSelector | changeLanguage | navigator.language` over `apps/web/src` returns **0 matches**.
|
||||
All UI copy is hardcoded English. The mockup's `English (US)` selector has **no backing system**.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Reuse the wizard shell; rework step 0 into a proper First-Launch screen.**
|
||||
|
||||
### Components to create / rework
|
||||
1. **REWORK `WelcomeStep.tsx`** (or rename to `FirstLaunchStep.tsx` keeping the same step-0 slot)
|
||||
- Align copy to mockup: `Welcome to Waggle` + tagline `Your work. Your memory. Your agents.`
|
||||
(PRD §24: mockup is directional — keep current eyebrow/brand voice if it reads better, but
|
||||
the privacy promise + language + Continue affordances are required by blueprint, not optional).
|
||||
- Add **privacy note** (footer-left): short line ("Your data is private. Stored locally.") with
|
||||
a "view privacy note" expand (inline `Popover`/`HoverCard` from `components/ui/`, or a small
|
||||
details disclosure). Maps blueprint interaction "view privacy note" + state "local-only".
|
||||
- Add **offline awareness**: consume `useOfflineStatus()`; when offline, show a subtle indicator
|
||||
(reuse the StatusBar offline visual language) and keep Continue enabled (this step is pre-network).
|
||||
- Add **language affordance** (footer-right): see §9 open question — recommended v1 = a static,
|
||||
disabled-looking `English (US)` chip (honest: only English ships) OR a minimal selector wired to
|
||||
a new `OnboardingState.locale` that only persists the choice. Do **not** build full i18n in this
|
||||
card's scope.
|
||||
- Keep the existing **Continue** (`goToStep(1)`) + **click-anywhere** + **autoFocus** +
|
||||
keyboard-reachable button (WelcomeStep already satisfies WCAG 2.1.1/2.2.1 — preserve).
|
||||
2. **(Optional, recommended) reconsider the 3 s auto-advance** (`OnboardingWizard.tsx:136-141`).
|
||||
A privacy-reassurance screen that auto-dismisses in 3 s undercuts the "read the privacy note"
|
||||
intent. Rework: keep auto-advance only when no interaction, or drop it for S12. (Surgical change,
|
||||
one `useEffect`.)
|
||||
3. **"Resumed setup" copy hook** — when `state.step > 0` on mount (returning mid-flow), the shell
|
||||
already restores the step; ensure First Launch isn't re-shown cold. Minimal: the existing
|
||||
`state.step` restore (`OnboardingWizard.tsx:39`) already handles navigation; add a one-line
|
||||
"Welcome back — picking up where you left off" variant if `state.step` was persisted > 0. Low
|
||||
priority; the resume mechanic exists.
|
||||
|
||||
### Reuse targets
|
||||
- Shell: `OnboardingWizard.tsx` (progress bar, dots, Skip, Back, AnimatePresence step swap).
|
||||
- Motion: `fadeSlide` (`constants.ts:118-123`).
|
||||
- Offline: `useOfflineStatus()`.
|
||||
- UI primitives: `components/ui/{button,popover,hover-card,badge}.tsx` (shadcn set already present).
|
||||
- Brand assets: `assets/waggle-logo.{png,jpeg}` (already imported by WelcomeStep, theme-aware via
|
||||
`useIsLightTheme`).
|
||||
|
||||
### Props / state
|
||||
- `WelcomeStepProps` (extend): add `offline: boolean` (from `useOfflineStatus`), and — if a
|
||||
language chip is wired — `locale?: string` + `onLocaleChange?: (l: string) => void`.
|
||||
- `OnboardingState` (`useOnboarding.ts:10-19`): optionally add `locale?: string` (additive,
|
||||
localStorage-only, no backend). Resume already covered by existing `step` field.
|
||||
|
||||
### Adapter methods / hooks
|
||||
- **None new required.** This screen is pre-network. `useOfflineStatus` already wraps the health
|
||||
probe; `adapter.trackTelemetry('onboarding_step', …)` already fires on step change
|
||||
(`OnboardingWizard.tsx:31-33, :132`). No new adapter method.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work
|
||||
|
||||
**This screen needs effectively NO backend.** It is the pre-intent splash; every interaction is
|
||||
local UI + localStorage. Cross-referenced against backend-routes inventory and backend-map §03c —
|
||||
nothing on S12 maps to a missing PRD §16 endpoint.
|
||||
|
||||
| Capability needed | PRD §16 endpoint | Status | Note / what to EXTEND vs NET-NEW | Substrate / migration |
|
||||
|---|---|---|---|---|
|
||||
| Continue / advance step | — (none) | **EXISTS (client-only)** | Step state is `OnboardingState.step` in `localStorage` (`useOnboarding.ts`). No server call. | none |
|
||||
| Offline / local-only state | — (no PRD §16 row) | **EXISTS** | Reuse `GET /api/offline/status` (`offline.ts`) + the health probe already used by `useOfflineStatus`. No new route. | none |
|
||||
| Privacy note / local-first claim | — (none) | **EXISTS (static)** | Truthful per PRD §6/§18.1; copy-only. No endpoint. | none |
|
||||
| Language change | — (none) | **MISSING (no infra)** | No i18n/locale backend anywhere (grep-confirmed). If a real selector is wanted, locale persists client-side in `OnboardingState.locale` (localStorage) — **net-new client field, NOT a server route**. Could later piggy-back on `PUT /api/profile` (`profile.ts`, EXISTS) or `PATCH /api/settings` (`settings.ts`, EXISTS) if locale must sync, but that is out of scope for v1. | none (no `.mind` migration) |
|
||||
| Returning-user / resume detection | — (none) | **EXISTS** | `useOnboarding` already calls `adapter.getWorkspaces()` (`GET /api/workspaces`, EXISTS) + Tauri first-launch flag to auto-complete returning users. No change. | none |
|
||||
|
||||
**No `.mind` migration. No net-new route. No substrate touch.** S12 is the single cleanest screen
|
||||
in the refactor from a backend standpoint.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs lib/types.ts)
|
||||
|
||||
- **None of the PRD §15.2 unions are touched by S12** (no WorkspaceType/Scope/MemoryKind/etc. on a
|
||||
welcome splash).
|
||||
- The only candidate type change is local: add optional `locale?: string` to `OnboardingState`
|
||||
(`hooks/useOnboarding.ts:10-19`) **if** the language affordance is wired beyond a static chip.
|
||||
This is a frontend-only interface field, not a PRD §15 shared type, and needs no `packages/shared`
|
||||
change.
|
||||
- `WelcomeStepProps` (`onboarding/types.ts:10-12`) gains `offline` (+ optional locale props). Local
|
||||
to the onboarding folder.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **Phase placement:** PRD Sprint 5 / Release Phase 2 ("Onboarding flow: Welcome, Profile, Tool
|
||||
Discovery, Memory Import, Memory Review"). S12 is the **entry** of that sequence.
|
||||
- **Hard dependency:** the wizard shell (`OnboardingWizard.tsx`) must remain the host — already
|
||||
exists, so no blocker. S12 can be reworked independently of the later steps.
|
||||
- **Sibling screens that share the shell** (do these in the same sprint to keep the rework
|
||||
coherent): S13 Who Are You, S14 Tool Discovery, S15 Memory Import, S16 Memory Review, S17 Create
|
||||
Workspace. S12 should land first because it sets the privacy/local-first framing the rest rely on.
|
||||
- **No dependency on Home Cockpit / Workspace Desktop / Command Center backend work** — S12 is
|
||||
pre-workspace and pre-network.
|
||||
- **Decision dependency:** the language-selector scope question (§9) should be answered before
|
||||
implementation so the footer affordance isn't built twice.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort
|
||||
|
||||
**S** — Single presentational component rework inside an existing, working shell; reuse
|
||||
`useOfflineStatus` + existing motion/UI primitives; **zero backend, zero migration, zero shared-type
|
||||
churn**. The only thing that could push it toward **M** is electing to build a real (even minimal)
|
||||
language selector + `locale` plumbing instead of a static `English (US)` chip.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Language selector — real or honest-stub?** No i18n exists (grep-confirmed: 0 matches for
|
||||
i18next/useTranslation/etc.). Options: (a) static disabled `English (US)` chip (truthful, S
|
||||
effort, recommended v1); (b) minimal selector that persists `OnboardingState.locale` but only
|
||||
English is wired (cosmetic); (c) defer the affordance entirely. PRD §24 lets us treat the mockup
|
||||
as directional, and PRD §4.4 lists native/i18n work as out-of-first-phase scope — so (a) or (c)
|
||||
align with the PRD. **Recommend (a).** Confirm before building.
|
||||
2. **Keep the 3 s auto-advance on a privacy screen?** Current step-0 auto-advances after 3 s
|
||||
(`OnboardingWizard.tsx:136-141`), which fights the "read the privacy note" intent. Drop it for
|
||||
S12, or keep-but-pause-on-interaction?
|
||||
3. **Copy alignment:** mockup says "Welcome to Waggle" / "Your work. Your memory. Your agents.";
|
||||
current ships "Welcome to the Hive" / "Persistent memory. Workspace-native…". Which brand voice
|
||||
wins? (PRD §24: pixels don't, but the privacy promise must be present either way.)
|
||||
4. **"Resumed setup" UX:** the step is restored from `localStorage` already — is a distinct
|
||||
"welcome back" treatment on First Launch wanted, or is silent step-restore sufficient?
|
||||
5. **Privacy note content:** inline expandable text vs link to a privacy doc/URL? No privacy-policy
|
||||
route or doc is wired today — needs copy + destination decided.
|
||||
231
docs/ux-refactor/gap-cards/S13-who-are-you.md
Normal file
231
docs/ux-refactor/gap-cards/S13-who-are-you.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# Gap Card — S13 · Who Are You (Onboarding Profile)
|
||||
|
||||
> UX-refactor planning artifact. Execution model is **in-place incremental refactor** of `apps/web` +
|
||||
> targeted backend extension. Every claim below is grounded in a real file. PRD = source of truth;
|
||||
> mockup is directional (PRD §24). Screen index: **Onboarding Flow PRD §12.12 step 2** ("Who Are You").
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
The second onboarding step: capture the user's professional context so Waggle can personalize and
|
||||
recommend. Per PRD §12.12 and the blueprint screen-spec table (PAGE 14, row 13): "Capture role,
|
||||
industry, work type, team size and goals." Blueprint acceptance: **"Profile drives recommendations
|
||||
but can be edited later."** Blueprint states: `Empty; partially complete; validation; saved`.
|
||||
|
||||
The mockup (`screen_13_who_are_you.png`) shows a single-step form inside the onboarding shell:
|
||||
left rail step list (Welcome / Who are you / Tool discovery / Memory import / Review & confirm) + a
|
||||
"Your data is private" reassurance card; center form with **Name, Role, Industry, Work type, Team
|
||||
size, and a "What are your goals with Waggle?" multi-select chip group** (Build a second brain /
|
||||
Improve team productivity / Automate recurring work / Make better decisions / Scale the business) +
|
||||
free-text "Add other goal"; a right-hand live **"Your profile" preview panel** that mirrors the form
|
||||
as the user types; Back / Continue footer. Mockup is directional — PRD/blueprint acceptance wins.
|
||||
|
||||
This profile drives downstream screens: Tool Discovery (S14) recommendations, Memory Import (S15)
|
||||
source suggestions, Workspace Creation (S17) template/persona pre-fill, and the Home Cockpit greeting.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD/Blueprint)
|
||||
|
||||
From the blueprint screen-spec (PAGE 14, row 13) + PRD §14.1 global-state mandate:
|
||||
|
||||
- **Empty** — fresh form, nothing entered.
|
||||
- **Partially complete** — some fields filled (Continue still allowed; profile is editable later).
|
||||
- **Validation** — surface invalid/missing required input (blueprint lists "validation" as a state;
|
||||
PRD acceptance says it must not block — soft validation, not a hard gate).
|
||||
- **Saved** — profile persisted; advance to Tool Discovery.
|
||||
- **Loading** (PRD §14.1) — restoring a resumed/partial profile on re-entry.
|
||||
- **Offline / local-only** (PRD §14.1, §6.7) — sidecar unreachable; the step must still let the user
|
||||
type and continue (write deferred / retried), mirroring the wizard's existing
|
||||
"created locally, sync later" fallback (`OnboardingWizard.tsx:286-288`).
|
||||
|
||||
PRD §12.12 acceptance criteria that bind this screen: "Onboarding asks user questions, not
|
||||
infrastructure questions"; "Profile drives recommendations." PRD §7.1 activation metric: "% of new
|
||||
users who complete onboarding."
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo — disposition: **partial** (rework + create-new)
|
||||
|
||||
There is **no onboarding "Who Are You" profile step today.** The profile-capture *capability* exists
|
||||
post-onboarding as a separate app, and the onboarding wizard does NOT collect it. Two surfaces are
|
||||
relevant; both partially overlap the requirement:
|
||||
|
||||
**(a) `OnboardingWizard.tsx`** (`apps/web/src/components/os/overlays/OnboardingWizard.tsx`) — the
|
||||
first-launch wizard. Its actual step order (`STEP_NAMES`, line 35) is:
|
||||
`welcome → why-waggle → tier → memory-import → template → persona → api-key → ready` (8 steps,
|
||||
0-indexed; rendered via the `step===N` switch at lines 499-571). **There is no profile/identity-capture
|
||||
step at all.** Steps map to `onboarding/` components: `WelcomeStep, WhyWaggleStep, TierStep, ImportStep,
|
||||
TemplateStep, PersonaStep, ModelTierStep, ReadyStep` (`onboarding/index.ts`). The wizard's data model
|
||||
(`OnboardingState` in `hooks/useOnboarding.ts`) tracks `step/tier/templateId/personaId/workspaceId/
|
||||
apiKeySet` — **no profile fields.** Disposition for the wizard: **rework** — insert a new profile step
|
||||
and a `WhoAreYouStep` component; widen `OnboardingState` minimally (or post directly to `/api/profile`).
|
||||
|
||||
> Note: the wizard's IA (tier picker, API-key step, model-tier step) is the OLD onboarding, not the
|
||||
> PRD §12.12 flow (Welcome → Who Are You → Tool Discovery → Memory Import → Memory Review → Workspace
|
||||
> Creation → Home). PRD §20.2 explicitly lists "Onboarding wizard → simplify to user-oriented 5-step
|
||||
> setup plus workspace creation." S13 is one slice of that larger onboarding rework; this card scopes
|
||||
> only the profile step, but the planner must sequence it inside the onboarding-IA rework (see §7).
|
||||
|
||||
**(b) `UserProfileApp.tsx`** (`apps/web/src/components/os/apps/UserProfileApp.tsx`, 533 LOC) — the
|
||||
post-onboarding "My Profile" app (dock id `profile`). A 4-tab surface: **Identity** (Name/Role/Company/
|
||||
Industry/Bio + "Research Me" + harvest-suggestion accept/dismiss banner, lines 242-343), Writing Style,
|
||||
Brand & Templates, Interests (interests chips + language, lines 483-520). Its Identity tab already
|
||||
renders the heading "**Who Are You?**" (line 245) and captures Name/Role/Company/Industry/Bio via
|
||||
`adapter.getProfile()` / `adapter.updateProfile()` (lines 86-102, 120-133). This is the **reuse target**
|
||||
for the form fields, validation pattern, and adapter wiring — but it is NOT an onboarding step, it has
|
||||
NO Work type / Team size / Goals, and it has NO live preview panel. Disposition: **keep-promote** the
|
||||
field/adapter patterns; extract a shared profile-form so the onboarding step and the app don't diverge.
|
||||
|
||||
**Net:** the requirement is **partial** — backend persistence (`/api/profile`) + a structurally similar
|
||||
form (UserProfileApp Identity tab) exist, but the *onboarding step itself*, the *3 new fields*
|
||||
(work type / team size / goals), and the *live preview panel* are net-new frontend.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Create:**
|
||||
- `apps/web/src/components/os/overlays/onboarding/WhoAreYouStep.tsx` — the new step component
|
||||
(matches the `*Step.tsx` sibling convention; default-exported from `onboarding/index.ts`). Renders:
|
||||
Name (text), Role (text), Industry (`<select>` reusing `UserProfileApp.tsx:37-41` `INDUSTRIES`),
|
||||
**Work type** (select/segmented — e.g. Strategy & Operations / Engineering / Sales / Marketing /
|
||||
Research / Other), **Team size** (select — Just me / 2-10 / 11-50 / 50+), **Goals** (multi-select
|
||||
chip group, reusing the chip-toggle pattern from `UserProfileApp.tsx:488-498` interests), free-text
|
||||
"other goal", and the **live "Your profile" preview panel** (right column; pure derived view of
|
||||
current form state). Props: `{ profile, onChange, onContinue, onBack, saving }`. Local form state
|
||||
mirrored to a single `Partial<UserProfile>` object (immutable updates per repo coding-style).
|
||||
- `apps/web/src/lib/onboarding-profile.ts` (optional helper) — the goal/work-type/team-size option
|
||||
constants + a `buildProfilePreview()` pure function (testable, co-located `.test.ts`), keeping the
|
||||
step component thin (CLAUDE.md §3.2, file-org rules).
|
||||
|
||||
**Rework:**
|
||||
- `OnboardingWizard.tsx` — insert the new step into the flow after `WelcomeStep` per PRD §12.12 order.
|
||||
Touches: `STEP_NAMES` (line 35), the step switch (lines 499-571), step-index math (`progressPct`
|
||||
line 396, dots lines 472), and a `handleProfileSave` that calls `adapter.updateProfile(...)` (same
|
||||
call UserProfileApp uses, `UserProfileApp.tsx:123-128`). Keep the existing offline "created locally"
|
||||
fallback semantics (lines 286-288) for the profile write. NOTE: the current wizard IA (tier/api-key/
|
||||
model steps) is being reworked to the PRD §12.12 5-step flow in a sibling card — coordinate the
|
||||
step-index churn with that card to avoid double-editing the switch.
|
||||
- `UserProfileApp.tsx` Identity tab — **optional consolidation**: extract the shared field set into the
|
||||
new `onboarding-profile.ts` form so onboarding and the app render the same Name/Role/Industry/Goals
|
||||
controls (avoids the documented "two parallel systems" anti-pattern,
|
||||
`feedback_grep_capability_not_feature_name.md`). At minimum, add the 3 new fields here too so a user
|
||||
can edit Work type / Team size / Goals after onboarding (blueprint: "can be edited later").
|
||||
|
||||
**Reuse targets (do not recreate):**
|
||||
- `INDUSTRIES` + `INTEREST_OPTIONS` chip-toggle pattern — `UserProfileApp.tsx:31-41, 488-498`.
|
||||
- `adapter.getProfile()` / `adapter.updateProfile()` — `lib/adapter.ts` (profile method block;
|
||||
inventory `frontend.md:232`). Already typed and used.
|
||||
- `@/components/ui/{input,select-ish}` shadcn primitives (`components/ui/*`, inventory §(e)).
|
||||
- Onboarding shell chrome (progress bar, Back/Skip, step dots) — already in `OnboardingWizard.tsx`.
|
||||
|
||||
**Adapter methods/hooks:** no NEW adapter method needed for the happy path —
|
||||
`adapter.updateProfile(partial)` (PUT `/api/profile`, partial-merge) already accepts arbitrary profile
|
||||
fields and the backend persists unknown-to-old-UI fields once the route is widened (see §5). The
|
||||
profile is read via `adapter.getProfile()`. No new hook required; step holds local state and posts on
|
||||
Continue.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work
|
||||
|
||||
PRD §16 has **no dedicated onboarding-profile endpoints** — the profile domain is served by the
|
||||
existing `/api/profile*` routes (`packages/server/src/local/routes/profile.ts`), which are NOT in the
|
||||
PRD §16 list but already exist and are the correct substrate. So there is nothing "MISSING" in PRD §16
|
||||
terms; the work is **EXTEND** of an existing route, not net-new routing.
|
||||
|
||||
| Capability | Status | Route to EXTEND vs NET-NEW · substrate |
|
||||
|---|---|---|
|
||||
| Read profile for the step | **EXISTS** | `GET /api/profile` (`profile.ts:162-164`) returns the full `UserProfile` (defaults-merged). Reusable as-is. |
|
||||
| Persist profile from the step | **PARTIAL → EXTEND** | `PUT /api/profile` (`profile.ts:167-228`) partial-merges, but its allow-list of merged fields (lines 172-196) does **not** include the 3 new fields (`workType`, `teamSize`, `goals`). EXTEND the merge block + the `UserProfile` interface (`profile.ts:41-94`) + `DEFAULT_PROFILE` (`:96-135`) to carry them. Substrate: `profile.json` under `dataDir` (NOT SQLite) — **no DB migration.** |
|
||||
| Mirror identity → memory | **EXISTS (reuse)** | `PUT /api/profile` already writes a `User identity:` P/I frame to personal memory on save (`profile.ts:201-224`). Optionally append role/industry/goals to that string so the agent picks up the new context — pure edit, no schema change. |
|
||||
|
||||
**Net-new fields (frontend + backend, additive):** `workType?: string`, `teamSize?: string`,
|
||||
`goals?: string[]`. All three are pure additive optional fields on the JSON-file `UserProfile` shape.
|
||||
The mockup's Industry/Name/Role already map 1:1 to existing fields.
|
||||
|
||||
**Substrate touched:** `profile.json` (file store, `getProfilePath()` `profile.ts:137-139`) and
|
||||
(reused) personal `.mind` `memory_frames` via the existing identity-mirror path.
|
||||
|
||||
**.mind migration:** **NONE.** Profile lives in `profile.json`, not SQLite (`profile.ts:152-156`).
|
||||
The identity-mirror writes frames through the existing append-only API — no schema change.
|
||||
|
||||
> Optional alignment (flag, not required for S13): a parallel structured identity record exists at
|
||||
> `POST /api/identity` → `identity` table (`identity.ts:104-156`, fields name/role/department/
|
||||
> personality/capabilities/system_prompt). The onboarding step writes to `/api/profile` (the richer,
|
||||
> file-backed shape the UI already uses), NOT `/api/identity`. The planner should decide whether
|
||||
> onboarding should ALSO seed `/api/identity` (it backs `adapter.getIdentity()` / the Home greeting
|
||||
> name) or leave that to the existing profile→frame mirror. Out of S13 scope but worth a one-line
|
||||
> decision to avoid two divergent identity stores.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
PRD §15.2 defines no profile/onboarding union (its unions are Workspace/Memory/Artifact/Agent-centric),
|
||||
so there is no PRD §15 type to satisfy here. The relevant types are:
|
||||
|
||||
- **Frontend:** `apps/web/src/lib/types.ts` has **no `UserProfile` type** (the shape is declared
|
||||
inline inside `UserProfileApp.tsx:45-52` and re-declared loosely in the new step). Recommend promoting
|
||||
a shared `UserProfile` (with the 3 new fields) to `lib/types.ts` so the onboarding step, the app, and
|
||||
the adapter return type share one contract (avoids the FE/BE drift the substrate inventory flags for
|
||||
other entities). Low-cost, additive.
|
||||
- **Backend:** `UserProfile` + `IdentitySuggestion` interfaces are exported from `profile.ts:31-94`.
|
||||
Extend in place with the 3 new fields. (They are not in `packages/shared` today; keeping them in
|
||||
`profile.ts` + mirroring an FE type is consistent with current layout.)
|
||||
- New small unions for the option sets (`WorkType`, `TeamSize`, goal ids) can live in
|
||||
`onboarding-profile.ts` as string-literal unions (repo coding-style prefers literal unions over enum).
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **Phase 2 (PRD §8)** — Onboarding flow is Phase 2 / Sprint 5 ("Who Are You" listed explicitly,
|
||||
PRD §21 Sprint 5 + §8 Phase 2).
|
||||
- **Onboarding-IA rework first.** S13 is one step inside the PRD §12.12 flow (Welcome → **Who Are You**
|
||||
→ Tool Discovery → Memory Import → Memory Review → Workspace Creation → Home). The current wizard's
|
||||
step order/IA differs from PRD §12.12; the step-index/switch churn in `OnboardingWizard.tsx` must be
|
||||
coordinated with the sibling onboarding cards (S12 First Launch, S14 Tool Discovery, S15 Memory
|
||||
Import, S16 Memory Review, S17 Workspace Creation) so the switch is rewired once, not per-card.
|
||||
- **Downstream consumers of this profile:** S14 Tool Discovery (recommendations from work type/role —
|
||||
PRD §12.12 acceptance "recommended from user selections"), S15 Memory Import (source suggestions),
|
||||
S17 Workspace Creation (template/persona pre-fill), Home Cockpit greeting (name). S13 should land
|
||||
before or with S14 since S14 consumes its output.
|
||||
- **No backend prerequisite** — `/api/profile` GET/PUT already exist; the field extension is
|
||||
self-contained and can ship independently of the Home/Workspace/Memory backend work.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **M**
|
||||
|
||||
One net-new step component + a live-preview panel + a thin backend field extension (3 additive JSON
|
||||
fields, no migration) + onboarding wizard step insertion. The form fields, validation pattern, chip
|
||||
toggles, and adapter wiring already exist in `UserProfileApp.tsx` to copy from, which keeps it out of
|
||||
L. The "M" (not S) reflects: the live-preview panel is new UI, the wizard step-index/switch rewire is
|
||||
fiddly and must be coordinated with the broader onboarding-IA rework, and a shared `UserProfile` type +
|
||||
optional UserProfileApp consolidation add surface.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Identity store of record:** does onboarding write profile via `/api/profile` only (current UI
|
||||
path), or ALSO seed `/api/identity` (the `identity` table that backs the Home greeting name /
|
||||
`adapter.getIdentity()`)? Two identity stores exist; pick one to avoid drift. (§5 note.)
|
||||
2. **Goal taxonomy:** are the 5 mockup goals (second brain / team productivity / automate recurring
|
||||
work / better decisions / scale business) the canonical set, or directional? They likely feed S14
|
||||
tool/connector recommendations — confirm the mapping owner.
|
||||
3. **Work type vs Industry vs Persona/Template:** the existing onboarding already picks a template
|
||||
(`TEMPLATE_PERSONA`, `constants.ts:28-44`) which implies a work domain. Does "Work type" duplicate
|
||||
that signal, or is it a distinct axis used only for personalization? Resolve to avoid asking the
|
||||
user the same thing twice (PRD §12.12: "asks user questions, not infrastructure").
|
||||
4. **Validation hardness:** blueprint lists a "validation" state but PRD says profile is editable
|
||||
later. Confirm Continue is never hard-blocked (soft-validate only) — assumed soft per PRD acceptance.
|
||||
5. **Required vs optional fields:** which of Name/Role/Industry/Work type/Team size/Goals (if any) are
|
||||
required to proceed? Assumed all optional (partial-complete is an allowed state).
|
||||
6. **Pre-fill from harvest:** UserProfileApp already accepts harvest-extracted `identitySuggestions`
|
||||
(`profile.ts:31-39`, `UserProfileApp.tsx:252-298`). Should the onboarding step pre-fill from those
|
||||
if Memory Import (S15) ran first? In PRD §12.12 order, import comes AFTER Who Are You — so likely no
|
||||
on first pass, but confirm whether a returning/resumed user sees suggestions here.
|
||||
250
docs/ux-refactor/gap-cards/S14-tool-discovery.md
Normal file
250
docs/ux-refactor/gap-cards/S14-tool-discovery.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Gap Card — S14 Tool Discovery
|
||||
|
||||
> Screen 14 of the Waggle OS UX-refactor. Onboarding step 3 of 7 (PRD §12.12).
|
||||
> Execution model: **in-place incremental refactor** of `apps/web` + targeted backend
|
||||
> extension. Every claim below is grounded in repo source (paths cited).
|
||||
> PRD = `docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
"What tools do you use?" — the third onboarding step. The user selects the tools they
|
||||
already use day-to-day (in plain product language, **not** an infra/connector setup
|
||||
screen) so Waggle can recommend the right connectors, MCPs and skills downstream and
|
||||
seed the Memory Import step (S15).
|
||||
|
||||
- **PRD §12.12** (step 3 of the 7-step onboarding flow): "Tool Discovery — ask which
|
||||
tools are used." Acceptance: "Onboarding asks user questions, not infrastructure
|
||||
questions"; "Connectors/MCPs/skills are **recommended from user selections**."
|
||||
- **Blueprint** (`_blueprint_extracted.txt`):
|
||||
- Screen index row 14 (`:381-384`): *"Ask what tools the user uses. Select tools, add
|
||||
other, continue/back. States: No selection; selected; recommended; unsupported tool.
|
||||
User-oriented language, not infra setup."*
|
||||
- Mental model (`:54`): *"Tool discovery and harvest are core onboarding steps."*
|
||||
- Journeys J01 (`:169`) and J02 (`:173`): Tool Discovery sits between *Who Are You*
|
||||
(S13) and *Memory Import* (S15) on both the import and skip-import paths.
|
||||
- **Mockup** (`screen_14_tool_discovery.png`, directional): two-column layout —
|
||||
left = grouped selectable tool grid in **"Collaboration & Productivity"** (Gmail,
|
||||
Calendar, Slack, GitHub, Notion, Jira, Salesforce) and **"AI & Developer Tools"**
|
||||
(Claude, Claude Code, Cursor, Hermes, Codex, "Other tool"), each card with a
|
||||
checkbox + "Select all" per group; right rail = **"You selected"** chip list +
|
||||
**"What's next"** ("We'll suggest relevant connectors and MCPs", "Your data is
|
||||
private"); left rail = the 7-step onboarding progress rail with "Tool discovery"
|
||||
active; footer = Back / Continue.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
Blueprint S14 enumerates four states; the mockup adds layout affordances:
|
||||
|
||||
1. **No selection** — nothing chosen; "You selected" empty; Continue still allowed
|
||||
(tool discovery is non-blocking, consistent with J02 skip-import).
|
||||
2. **Selected** — one or more tools toggled; reflected in the "You selected" chip rail.
|
||||
3. **Recommended** — "What's next" surfaces connectors/MCPs/skills derived from the
|
||||
selections (PRD acceptance: recommendations come *from user selections*). This is a
|
||||
silent default-ordering surface, not a friction step (per the project rule
|
||||
`feedback_silent_recommendations_dont_ask.md`).
|
||||
4. **Unsupported tool** — user adds a tool ("Other tool" / free-text) that Waggle has
|
||||
no native connector for; captured as a profile signal, surfaced as "we'll watch for
|
||||
this / available via MCP," never a hard error.
|
||||
5. Plus: **Select-all per group**, **Back/Continue** nav, and per-group sections
|
||||
("Collaboration & Productivity" vs "AI & Developer Tools").
|
||||
|
||||
Acceptance (PRD §12.12): user-oriented language; selections drive recommendations;
|
||||
**nothing imports here** (import/consent is S15's job — this screen only records intent).
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `create-new`** (the step does not exist; substrate to feed it largely does).
|
||||
|
||||
### The onboarding wizard has NO Tool Discovery step today
|
||||
`apps/web/src/components/os/overlays/OnboardingWizard.tsx` is an **8-step** wizard whose
|
||||
`STEP_NAMES` (`:35`) are: `welcome, why-waggle, tier, memory-import, template, persona,
|
||||
api-key, ready`. The step switch (`:498-572`) renders `WelcomeStep / WhyWaggleStep /
|
||||
TierStep / ImportStep / TemplateStep / PersonaStep / ModelTierStep / ReadyStep`. **There
|
||||
is no "what tools do you use?" step** anywhere in the flow. Step components live in
|
||||
`apps/web/src/components/os/overlays/onboarding/` (`WelcomeStep, WhyWaggleStep, TierStep,
|
||||
ImportStep, TemplateStep, PersonaStep, ModelTierStep, ReadyStep, constants.ts, types.ts,
|
||||
index.ts`) — none is a tool picker.
|
||||
|
||||
> Note: the live wizard's step ordering (tier/template/persona/api-key) does **not** match
|
||||
> the PRD's 7-step IA (Welcome → Who Are You → Tool Discovery → Memory Import → Memory
|
||||
> Review → Workspace Creation → Home Cockpit). The refactor inserts Tool Discovery; the
|
||||
> broader re-sequencing is a cross-screen concern (see §7).
|
||||
|
||||
### `OnboardingState` cannot persist a tool selection
|
||||
`apps/web/src/hooks/useOnboarding.ts:10-19` — `OnboardingState` = `{ completed, step,
|
||||
tier?, workspaceId?, apiKeySet?, templateId?, personaId?, tooltipsDismissed? }`. **No
|
||||
`toolsUsed` / `selectedTools` field.** Persisted to `localStorage` key `waggle:onboarding`.
|
||||
|
||||
### Two SEPARATE substrates map to the mockup's two columns — neither is a "what tools do you use" survey
|
||||
1. **SaaS / productivity column** → native **connector registry**.
|
||||
`packages/agent/src/connector-registry.ts` `getDefinitions()` (`:59`) returns
|
||||
`ConnectorDefinition[]` with live vault status. ~31 connectors are registered
|
||||
(`packages/agent/src/connectors/index.ts`): GitHub, Slack, Jira, Gmail, Google
|
||||
Calendar, Notion, Salesforce, HubSpot, Linear, Asana, Trello, Monday, Confluence,
|
||||
Discord, Dropbox, etc. — a direct match for the mockup's Gmail/Calendar/Slack/GitHub/
|
||||
Notion/Jira/Salesforce cards. `ConnectorDefinition` (`packages/shared/src/types.ts:276-302`)
|
||||
already carries `id, name, displayName(via name), description, category` (`'productivity'
|
||||
| 'development' | 'crm' | 'data' | 'communication' | 'storage' | 'integration'`),
|
||||
`status`, `logoUrl`, `setupGuide` — exactly the fields a grouped, iconed tool grid needs.
|
||||
Surfaced over HTTP at `GET /api/connectors` (`packages/server/src/local/routes/connectors.ts:6`).
|
||||
2. **AI & Developer column** → **AI-OS tool detection** (DIFFERENT subsystem).
|
||||
`packages/agent/src/tool-detection.ts` + `packages/shared/src/tool-detection.ts`:
|
||||
`SUPPORTED_TOOLS` (`:23-31`) = `claude-code, claude-desktop, cursor, codex,
|
||||
codex-desktop, hermes, openclaw`; `TOOL_DISPLAY_NAMES` (`:58`). Surfaced at
|
||||
`GET /api/tools/detect` (`packages/server/src/local/routes/tools.ts`). This *detects
|
||||
what is installed on the machine*; the mockup's AI/Developer column ("Claude, Claude
|
||||
Code, Cursor, Hermes, Codex") aligns with this set but as a **self-report picker**, not
|
||||
an install scan. The two can be merged: pre-check tools `tool-detection` already found.
|
||||
|
||||
### Recommendation engine — partial precedent, no connector recommender yet
|
||||
`apps/web/src/lib/skill-recommendations.ts` is the existing template for "silent default
|
||||
ordering from a user signal" (persona → 3-5 starter skills, `recommendSkills()` `:103`).
|
||||
**There is no `connector-recommendations.ts`** (grep: only a *comment* in
|
||||
skill-recommendations references the pattern; no file). The "What's next" rail needs a
|
||||
new tool→connector/MCP/skill mapper following this same shape. `GET /api/skills/suggestions`
|
||||
exists (`skills.ts`) but is context-driven, not tool-selection-driven.
|
||||
|
||||
**Reuse-not-rebuild verdict:** the *data* for both columns exists (`/api/connectors` +
|
||||
`/api/tools/detect`); the *step UI*, the *state field*, and the *recommendation mapper*
|
||||
are net-new. No new data store.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
### Components to CREATE
|
||||
| Component | Location | Role |
|
||||
|---|---|---|
|
||||
| `ToolDiscoveryStep.tsx` | `apps/web/src/components/os/overlays/onboarding/` | The step shell: grouped tool grid + "You selected" rail + "What's next" rail + Back/Continue. Mirrors the `*Step.tsx` prop contract (`goToStep`, controlled selection props). |
|
||||
| `ToolGroup.tsx` (or inline) | same dir | One titled group ("Collaboration & Productivity" / "AI & Developer Tools") with a Select-all toggle and a grid of `ToolCard`s. |
|
||||
| `ToolCard.tsx` | same dir | Single selectable tool tile (icon/logo + label + checkbox + selected ring). Reuse `components/ui/checkbox` + `card` + existing `connectors/BrandTile.tsx` icon pattern. |
|
||||
|
||||
### Reuse targets
|
||||
- `components/os/apps/connectors/BrandTile.tsx` — already renders a branded connector
|
||||
tile with logo; lift its icon/logo resolution into `ToolCard`.
|
||||
- `components/ui/*` (`card`, `checkbox`, `badge`, `button`, `separator`) — DS primitives.
|
||||
- `lib/skill-recommendations.ts` shape — model `connector-recommendations.ts` on it.
|
||||
- Onboarding chrome (progress rail, Back/Continue/Skip) already exists in
|
||||
`OnboardingWizard.tsx` (`:438-493`) — the step plugs into the existing AnimatePresence
|
||||
switch; no new chrome.
|
||||
|
||||
### New lib helper
|
||||
- `apps/web/src/lib/tool-recommendations.ts` — pure mapper
|
||||
`recommendFromTools(selectedToolIds: string[]) => { connectors: string[]; mcps: string[];
|
||||
skills: string[] }`, with a co-located `.test.ts` (matches `skill-recommendations.test.ts`).
|
||||
Feeds the "What's next" rail. Tool ids reconciled across connector ids and `ToolId`.
|
||||
|
||||
### Wizard wiring (edits to existing files)
|
||||
- `OnboardingWizard.tsx`: insert the step into `STEP_NAMES` (`:35`) and the step switch
|
||||
(`:498-572`); thread `selectedTools` step-local state + `onUpdate({ toolsUsed })`; bump
|
||||
the progress denominator (currently hard-coded `/7` `:396`, dots `[1..6]` `:472`,
|
||||
`aria-valuemax={7}` `:426`) — these counts become inconsistent once a step is added and
|
||||
the PRD re-sequence lands, so treat the step-count constants as a single thing to fix.
|
||||
- `useOnboarding.ts`: add optional `toolsUsed?: string[]` (and optionally
|
||||
`unsupportedTools?: string[]`) to `OnboardingState` (`:10-19`); additive, no migration
|
||||
(localStorage).
|
||||
|
||||
### Data hooks
|
||||
- Connector column: `adapter.getConnectors()` (`GET /api/connectors`) — already on the
|
||||
adapter (`lib/adapter.ts`). No new hook strictly needed; a thin `useConnectors`-style
|
||||
fetch in the step is fine, or reuse `ConnectorsApp`'s fetch pattern.
|
||||
- AI/Dev column: `adapter.detectTools()` (`GET /api/tools/detect`) — already on the
|
||||
adapter — to PRE-CHECK locally-installed AI tools.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16 cross-reference)
|
||||
|
||||
S14 has **no dedicated PRD §16 endpoint** — it is an onboarding capture screen that reads
|
||||
existing catalogs and writes the selection into onboarding/profile state. Required backend:
|
||||
|
||||
| Need | Status | Existing route/builder to EXTEND vs NET-NEW | Substrate |
|
||||
|---|---|---|---|
|
||||
| List SaaS/productivity tools (grid) | **EXISTS** | `GET /api/connectors` (`connectors.ts:6` → `connectorRegistry.getDefinitions()`). Already returns `category` + `logoUrl` for grouping/icons. No change. | connector registry (in-memory defs + vault status) |
|
||||
| List AI/developer tools (+ pre-check installed) | **EXISTS** | `GET /api/tools/detect` (`tools.ts`, AI-OS). Returns per-tool `installed`. No change. | `tool-detection.ts` (FS/PATH probes) |
|
||||
| Recommend connectors/MCPs/skills from selection ("What's next") | **PARTIAL** | Closest is `GET /api/skills/suggestions` (`skills.ts`, context-driven, wrong input). Recommended approach: do the mapping **client-side** in `lib/tool-recommendations.ts` (pure, testable, no round-trip) — no new endpoint. If server-side later: NET-NEW thin `GET /api/recommendations?tools=` over connector catalog + `mcp-catalog.ts` + starter-skills. | connector defs + `@waggle/shared` `mcp-catalog.ts` + starter skills |
|
||||
| Persist the tool selection | **PARTIAL** | Two options: (a) **client-only** in `localStorage` via `OnboardingState.toolsUsed` (lowest risk, matches current onboarding persistence) — **recommended**; (b) write into the user profile via existing `PUT /api/profile` (`profile.ts:218`, partial-merge) so recommendations survive re-onboarding and feed S15. Prefer (a) for the step; optionally also (b) to thread selections into Memory Import. | `localStorage` and/or `profile` store |
|
||||
|
||||
**No net-new sidecar route is strictly required** for S14 itself, and **no `.mind`
|
||||
migration** — both source catalogs already have HTTP routes, and the selection is small
|
||||
UI state. (Contrast: S15 Memory Import and the Home/Artifacts screens DO need net-new
|
||||
routes; S14 is the cheap one.)
|
||||
|
||||
> Adjacent (not S14-blocking): if the team later wants the "unsupported tool" free-text to
|
||||
> become a real demand signal, the cheapest home is `PUT /api/profile` `unsupportedTools[]`
|
||||
> — no new substrate.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
- **`OnboardingState.toolsUsed?: string[]`** (+ optional `unsupportedTools?: string[]`) —
|
||||
add to `apps/web/src/hooks/useOnboarding.ts:10-19`. Additive, optional.
|
||||
- **`ToolRecommendation`** interface in the new `lib/tool-recommendations.ts` (mirrors
|
||||
`SkillRecommendation` in `skill-recommendations.ts:20-27`).
|
||||
- **Reuse, do not redefine:** `ConnectorDefinition` (`packages/shared/src/types.ts:276-302`)
|
||||
for the SaaS column; `ToolId` / `DetectedTool` / `TOOL_DISPLAY_NAMES`
|
||||
(`packages/shared/src/tool-detection.ts`) for the AI/Dev column.
|
||||
- **PRD §15 note:** S14 does **not** introduce any of the missing §15.2 unions
|
||||
(`WorkspaceType`, `Scope`, `MemoryKind`, `ArtifactKind`, …). It only needs the additive
|
||||
onboarding field above, so it is **not** gated on the §15 type-alignment work that other
|
||||
screens (Memory, Artifacts, Agents) require.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **Onboarding re-sequence (cross-screen):** the live wizard order (tier/template/persona/
|
||||
api-key) differs from the PRD 7-step IA. S14 slots between **S13 Who Are You** and
|
||||
**S15 Memory Import** (J01/J02). Coordinate step insertion + progress-count fix once,
|
||||
alongside the other onboarding screens (S12 First Launch, S13, S15, S16, S17) rather than
|
||||
in isolation — they all touch `OnboardingWizard.tsx` + `useOnboarding.ts`.
|
||||
- **Feeds S15 Memory Import:** selected tools should bias which import sources/connectors
|
||||
S15 surfaces (PRD: recommendations from selections). S14 must land before/with S15.
|
||||
- **Feeds Extend / Marketplace (S/connectors):** "What's next" recommendations point at
|
||||
the same connector/MCP catalog those screens own.
|
||||
- **No backend dependency** — `/api/connectors` and `/api/tools/detect` already exist, so
|
||||
S14 frontend is **not blocked on any backend phase**.
|
||||
- **Soft dependency:** `lib/tool-recommendations.ts` reuses the `skill-recommendations.ts`
|
||||
pattern (already shipped).
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **M**
|
||||
|
||||
Net-new step component + group/card subcomponents + a pure recommendation mapper + one
|
||||
additive `OnboardingState` field + wizard-switch/progress-count wiring. **Zero net-new
|
||||
backend routes and zero `.mind` migration** (both catalogs already have HTTP routes), which
|
||||
keeps it out of L/XL. Larger than S (real new UI surface, two data sources to merge and
|
||||
de-dupe, recommendation logic with tests, and the progress-count/step-sequence cleanup that
|
||||
ripples through the wizard).
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Catalog scope for the grid.** Mockup shows ~7 SaaS + ~5 AI tools, but the registry
|
||||
has ~31 connectors. Show a **curated subset** (most common, matching the mockup) with a
|
||||
"more" affordance, or the full grouped catalog? PRD says "user-oriented, not infra" →
|
||||
leans curated. Needs a product call on the curated list.
|
||||
2. **Two columns, two id-spaces.** SaaS column uses connector `id`s; AI/Dev column uses
|
||||
`ToolId`. The "You selected" rail and `toolsUsed[]` need a unified id scheme (namespaced
|
||||
e.g. `connector:gmail` / `tool:cursor`) so the recommender and S15 can disambiguate.
|
||||
3. **Pre-check installed AI tools?** Should `GET /api/tools/detect` results pre-select the
|
||||
AI/Dev cards (lower friction, "we already see Cursor") or stay unchecked until the user
|
||||
opts in? J01 implies a populated, trusted start; lean pre-check + visible "detected" badge.
|
||||
4. **Where do selections live long-term?** `localStorage` only (simplest), or also
|
||||
`PUT /api/profile` so recommendations persist and feed Home/Extend after onboarding?
|
||||
Profile-write is cheap and reusable but adds a server round-trip to the step.
|
||||
5. **Recommendation placement: client vs server.** Recommend client-side
|
||||
(`lib/tool-recommendations.ts`, pure + testable, no round-trip) per §5 — confirm the team
|
||||
is fine deferring a `GET /api/recommendations` endpoint until a server-side consumer needs it.
|
||||
6. **"Unsupported tool" handling.** Captured as a profile/demand signal only, or also
|
||||
surfaced as "available via MCP" with a marketplace deep-link? Affects whether the free-text
|
||||
needs any backend at all (recommend: capture-only for v1).
|
||||
144
docs/ux-refactor/gap-cards/S15-memory-import.md
Normal file
144
docs/ux-refactor/gap-cards/S15-memory-import.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# Gap Card — S15 Memory Import
|
||||
|
||||
> UX-refactor planning artifact. Execution model is **in-place incremental refactor** of `apps/web`
|
||||
> + targeted backend extensions. Every claim is grounded in a real file (cited inline).
|
||||
> Mockup is **directional** (PRD §24) — PRD acceptance criteria win over pixels.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**S15 = the Memory Import step of onboarding** (PRD §12.12 step 4; Blueprint screen 15, `_blueprint_extracted.txt:727`). It is the "Capture" surface where a new user connects/imports their existing AI history (Claude, Claude Code, Cursor, Hermes, Codex, ChatGPT, Gemini, Perplexity…) and work tools (Files, Notion, Google Drive, Slack) so Waggle's persistent memory starts non-empty.
|
||||
|
||||
PRD anchors:
|
||||
- §12.12 step 4 "Memory Import — connect/import from AI tools, files, and work tools" (PRD:637).
|
||||
- §16.5 Harvest API: `POST /api/harvest/preview`, `POST /api/harvest/commit`, `GET /api/harvest/sources`, `POST /api/harvest/sources/:id/sync` (PRD:1098-1101).
|
||||
- Privacy gate: "No memory import without review/approval" (PRD:1207) and "Nothing imports without explicit review/approval" (PRD:646). **S15 produces the preview; the approval/commit decision is S16 Memory Review** (`_blueprint_extracted.txt:731`).
|
||||
|
||||
Mockup `screen_15_memory_import.png` (directional) shows a 3-region layout:
|
||||
- **Left rail** — onboarding step list (Welcome / Why us / Tool discovery / Import memory [active] / Review & confirm) + a "Your data is private" footer card.
|
||||
- **Center** — "Import your memory" with two grids: **AI Assistants & Coding Tools** (Claude, Claude Code, Cursor, Hermes, Codex, Other AI Tools — each a **Connect** action) and **Files & Workspace Tools** (Files, Notion, Google Drive, Slack — **Connect** actions). A primary **Continue** button bottom-right.
|
||||
- **Right rail** — "What we import" (Conversations / Documents / Decisions / Artifacts / Code) and "You're in control" (review-before-import, never-shared, disconnect-anytime).
|
||||
|
||||
> Note the mockup is **Connect-centric** (OAuth/connector tiles) whereas the live onboarding step is **upload-centric** (file pickers). This is the core directional gap (see §2/§3).
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD/Blueprint)
|
||||
|
||||
PRD §14.1 global states apply (Loading / Empty / Populated / Error / Offline-local-only / Syncing / Permission-denied / Partial / Approval-required). Concretely for S15:
|
||||
|
||||
| State | Trigger | Source of truth |
|
||||
|---|---|---|
|
||||
| Idle / source grid | step entered, no source picked | mockup center grids |
|
||||
| Source auto-detected | sidecar finds local Claude Code at `~/.claude` | `POST /api/harvest/scan-claude-code` → `{found,itemCount,path}` |
|
||||
| Preview / parsed | file uploaded or source connected; items parsed | `POST /api/harvest/preview` → `{itemCount, types, preview[]}` |
|
||||
| Importing (live progress) | commit running | SSE `GET /api/harvest/progress` `{phase∈saving\|cognifying\|wiki-compile, current, total}` |
|
||||
| Imported / done | commit returned | `POST /api/harvest/commit` `{saved, cognified, wikiCompiled, ...}` |
|
||||
| No real embedder (degraded) | commit returns `cognifySkippedReason\|wikiSkippedReason = 'no_real_embedder'` | harvest commit response (05c §8) |
|
||||
| Resume available | prior run interrupted | `GET /api/harvest/runs/latest-interrupted` |
|
||||
| Connector consent / OAuth | "Connect" on Notion/Drive/Slack/Gmail | `GET /api/connectors`, `POST /api/connectors/:id/connect`, `GET /api/oauth/:provider/authorize` |
|
||||
| Error | parse/commit/connect failure | per-call catch |
|
||||
| Skipped | user declines (Journey 2, PRD:683-686) | navigates to next onboarding step |
|
||||
| Privacy headline | always | mockup right rail; existing copy in `HarvestTab.tsx:362-365` |
|
||||
|
||||
**Approval boundary (load-bearing):** S15 ends at *preview*; the explicit approve-before-persist gate is **S16 Memory Review**. The live code today commits directly from S15 (no separate review screen) — a PRD-compliance gap, see §3.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo (exact files + what they do)
|
||||
|
||||
Two existing surfaces already implement most of S15's *upload* path against the harvest substrate:
|
||||
|
||||
### 3a. Onboarding step (the real S15)
|
||||
- `apps/web/src/components/os/overlays/OnboardingWizard.tsx` — step index **3** = `memory-import` (`STEP_NAMES` at `:35`). Holds import state (`importSource`/`importPreview`/`importing`/`importDone` `:64-72`), handlers `handleFileImport` (`:156-166` → `adapter.harvestPreview`), `handleImportCommit` (`:168-177` → `adapter.harvestCommit`, then `goToStep(4)`), Claude-Code auto-detect on mount (`:182-191` → `adapter.scanClaudeCode`) and `handleClaudeCodeHarvest` (`:193-202`). Renders `<ImportStep step===3>` at `:519-531`.
|
||||
- `apps/web/src/components/os/overlays/onboarding/ImportStep.tsx` (192 LOC) — the actual S15 UI. 6 `SOURCE_TILES` (chatgpt/claude/gemini/perplexity/cursor/unknown, `:18-33`) as **file pickers** (`accept=".json,.txt,.md,.csv"` `:113`), Claude-Code detect banner (`:62-86`), preview list + "Import N items" (`:125-155`), done state (`:157-162`), and a text pointer to "Memory → Harvest for 14+ more sources" (`:167-173`).
|
||||
- `apps/web/src/components/os/overlays/onboarding/types.ts:38-52` — `ImportStepProps`.
|
||||
|
||||
### 3b. Post-onboarding harvest hub (the "Memory → Harvest" surface)
|
||||
- `apps/web/src/components/os/apps/MemoryApp.tsx` → **Harvest tab** → `apps/web/src/components/os/apps/memory/HarvestTab.tsx` (685 LOC). The full-featured sibling: connected-sources list with auto-sync toggle/remove (`:505-559`), 15-source selector (`SOURCE_ICONS :35-51`), upload **and** paste modes (`:582-639`), preview with type-count chips (`:642-679`), **live SSE progress bar** (`:420-447`), **resume/discard banner** (`:330-360`), **identity-suggestion nudge** post-commit (`:452-470`), dedup/enrichment summary (`:472-502`), `no_real_embedder` not yet surfaced as a distinct affordance.
|
||||
- `apps/web/src/components/os/apps/memory/ImportReminderBanner.tsx` — nudge to revisit import.
|
||||
|
||||
### 3c. Adapter methods (the contract surface, `apps/web/src/lib/adapter.ts`)
|
||||
`harvestPreview` (`:1684`), `harvestCommit` (`:1689`), `getHarvestSources` (`:1694`), `scanClaudeCode` (`:1699`), `extractHarvestIdentity` (`:1710`), plus (per frontend inventory) `subscribeHarvestProgress`, `getLatestInterruptedHarvestRun`, `resumeHarvestRun`, `abandonHarvestRun`, `removeHarvestSource`, `toggleHarvestAutoSync`. Connector side: `getConnectors`, `getConnectorHealth`, `connectConnector`, `disconnectConnector`.
|
||||
|
||||
### Disposition: **rework** (promote-and-extend, do NOT create-new)
|
||||
The harvest *engine* and most of the *upload* UX already exist and are wired to the right endpoints. S15 needs to be **reworked** to (a) match the mockup's connect-grid IA, (b) **split commit out into S16 Memory Review** to satisfy the PRD "no import without review/approval" gate, and (c) reconcile the two near-duplicate surfaces (`ImportStep` vs `HarvestTab`) onto one shared component so onboarding and the standalone hub don't drift. This is squarely the locked in-place refactor model — reuse `harvest.ts` substrate, extend the frontend.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Reuse targets (do not rebuild):** `HarvestTab.tsx` is the canonical, feature-complete harvest UI — its SSE progress, resume banner, identity nudge, and dedup summary should be the shared core. `ImportStep.tsx` is the lighter onboarding shell. The connector tiles in the mockup map to the existing `ConnectorsApp`/`connectors/BrandTile.tsx` patterns.
|
||||
|
||||
Concrete components:
|
||||
|
||||
1. **Extract a shared `MemorySourcePicker` + `HarvestPreview` + `HarvestProgress`** from `HarvestTab.tsx` (currently a 685-LOC monolith; CLAUDE.md §coding-style favors small files) into `components/os/apps/memory/` so both `HarvestTab` and the onboarding `ImportStep` consume one source of truth.
|
||||
- Props: `sources: SourceTile[]`, `onPreview(data,source)`, `onConnect(connectorId)`, `preview`, `progress`, `result`, `mode: 'onboarding'|'hub'`.
|
||||
2. **Rework `ImportStep.tsx`** to the mockup's two-grid layout: **AI Assistants & Coding Tools** (Claude, Claude Code, Cursor, Hermes, Codex, ChatGPT, Gemini, Perplexity, Other) + **Files & Workspace Tools** (Files=`/api/ingest` upload, Notion/Drive/Slack/Gmail = connector "Connect"). Add the right-rail "What we import" + "You're in control" panels (copy already exists in `HarvestTab.tsx:362-369`).
|
||||
- Distinguish tile *kind*: `upload` (harvest file picker), `scan` (Claude Code local), `connect` (connector OAuth). Hermes/Codex are AI-tool launch/hook surfaces (`LauncherApp`/tool-detect) — for v1 they can be `upload`/"Other" or marked "coming soon" (open question OQ-1).
|
||||
3. **Move the commit decision to S16 Memory Review.** S15's "Continue" should carry the parsed preview forward; the explicit **approve → `harvestCommit`** happens on S16. Update `OnboardingWizard.handleImportCommit` (`:168-177`) to defer commit, or have S15 stage previews and S16 commit them. (Satisfies PRD:646/1207.)
|
||||
4. **Surface `no_real_embedder`** as a soft inline affordance ("semantic search/wiki won't update until you add an embedding key") using the `cognifySkippedReason`/`wikiSkippedReason` already in the commit response — currently unsurfaced in both UIs.
|
||||
5. **Connector consent flow** for Files & Workspace Tools tiles: reuse `adapter.connectConnector` + OAuth redirect (`/api/oauth/:provider/authorize`); show "review permissions" before connect (PRD:1207, Journey 8).
|
||||
6. **State coverage:** add explicit Loading/Error/Offline/Permission-denied/Approval-required renders per PRD §14.1 (HarvestTab has most; ImportStep is missing loading/offline).
|
||||
|
||||
Adapter: **no new methods required for the upload path** — all harvest + connector methods already exist (§3c). New methods needed only if backend adds `/api/harvest/sources/:id/sync` and a connector `/sync` (see §5).
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.5 + adjacent)
|
||||
|
||||
Cross-referenced against `docs/backend-map/sections/05c-subsystem-harvest.md`, `03b-api-memory.md`, and the route inventory.
|
||||
|
||||
| PRD §16 endpoint | Status | Note / what to EXTEND vs NET-NEW | Substrate |
|
||||
|---|---|---|---|
|
||||
| `POST /api/harvest/preview` | **EXISTS** | `routes/harvest.ts` — `{data,source}` → `{itemCount,types,preview[]}` (05c §6). No change. | personal `.mind` |
|
||||
| `POST /api/harvest/commit` | **EXISTS** | `routes/harvest.ts` — full ingest: parse → `FrameStore.createIFrame(gop='harvest',src='import')` → cognify → wiki recompile; SSE heartbeats; resumable run-store (05c §4). No change. | `memory_frames` (personal.mind) + KG + wiki |
|
||||
| `GET /api/harvest/sources` | **EXISTS** | `routes/harvest.ts` → `{sources: HarvestSource[]}`. No change. | `harvest_sources` |
|
||||
| `POST /api/harvest/sources/:id/sync` | **PARTIAL → EXTEND (net-new thin route)** | No per-source `/sync` action exists (route-inventory §16.5). Sources are registered via `POST /api/harvest/sources` and toggled via `PATCH /api/harvest/sources/:source`; actual re-sync runs through `POST /api/harvest/commit`. **Add a thin `POST /api/harvest/sources/:source/sync`** that resolves the source row and calls the existing commit path. **Path-key mismatch to resolve:** PRD uses `:id`; current sources are keyed by `:source` **name** (DELETE/PATCH both use `:source`, harvest.ts). Pick `:source` for consistency with siblings. | `harvest_sources` + commit pipeline |
|
||||
|
||||
**Adjacent endpoints S15 actually calls (not in §16.5 but load-bearing — all EXIST):**
|
||||
- `POST /api/harvest/scan-claude-code` — local `~/.claude` dry-run scan (EXISTS, `harvest.ts`).
|
||||
- `GET /api/harvest/progress` — **SSE** progress (EXISTS).
|
||||
- `GET /api/harvest/runs/latest-interrupted`, `POST /api/harvest/runs/:id/abandon`, resume via `commit {resumeFromRun}` (all EXIST).
|
||||
- `POST /api/harvest/extract-identity` — post-commit identity suggestions (EXISTS) — feeds S16/Profile, not S15 proper.
|
||||
- `POST /api/ingest` — for the "Files" tile (base64 file → text + frames, EXISTS, `ingest.ts`).
|
||||
- `GET /api/connectors`, `POST /api/connectors/:id/connect`, `GET /api/oauth/:provider/authorize|callback` — for Notion/Drive/Slack/Gmail "Connect" tiles (all EXIST; connectors defined under `packages/agent/src/connectors/*` incl. `notion-connector.ts`, `gdrive-connector.ts`, `slack-connector.ts`, `gmail-connector.ts`). A connector `POST /api/connectors/:id/sync` is **MISSING** (route-inventory §16.9) — net-new if the workspace-tools tiles must pull data immediately, but for S15 a "Connected" state without immediate pull is acceptable for v1.
|
||||
|
||||
**`.mind` migration flag:** **None required for S15's import path.** Harvest writes plain `memory_frames` via the existing `createIFrame` signature — no schema change. (The broader Memory Center migration for explicit `confidence`/`provenance`/`kind` per PRD §15.4 — a single additive `metadata TEXT` column on `memory_frames` — is owned by the **Memory Center / S16 Review** cards, not S15. Provenance today is a text prefix `[Harvest:<source>]` in `content`, per 05c §4, which S16 can parse for the "source/confidence" review chips.)
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
S15's wire shapes are harvest-specific and live in `packages/hive-mind-core/src/harvest/types.ts` (`UniversalImportItem`, `HarvestSource`, `HarvestRun`, `ImportSourceType`, `ImportItemType` — 05c §1). The frontend currently re-declares lossy local interfaces (`HarvestSource`/`PreviewResult` in `HarvestTab.tsx:16-33`; `claudeCodeDetected` shape inline). Gaps vs `apps/web/src/lib/types.ts`:
|
||||
|
||||
- **No shared `HarvestSource`/`HarvestRun`/`PreviewResult`/`ImportSourceType` in `lib/types.ts`** — each surface hand-rolls them. Promote a canonical `HarvestSource`, `HarvestPreview`, `HarvestCommitResult`, `ImportSourceType` into `lib/types.ts` (mirror the hive-mind-core shapes) so `ImportStep` and `HarvestTab` share one contract.
|
||||
- PRD §15.2 `Scope` / `MemoryKind` / `Confidence` unions are **not** S15's concern (they belong to S16/Memory Center) — S15 only needs the import-item/source/run types.
|
||||
- `adapter.harvestPreview`/`harvestCommit` are typed `Promise<any>` (`adapter.ts:1684,1689`) — tighten to the new shared result types (CLAUDE.md / ts-rules forbid `any`).
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **Belongs to PRD Phase 2 "Work layer" → Onboarding flow** (PRD:233) / Roadmap **Sprint 5** (PRD:1335-1342). Sequenced after S12 First Launch, S13 Who Are You, S14 Tool Discovery.
|
||||
- **Tightly coupled to S16 Memory Review** — S15 produces the preview; **S16 owns the approve→commit gate**. The commit-deferral rework (§4.3) cannot land without S16 existing. Plan S15 + S16 as a pair.
|
||||
- **Feeds S17 Create Workspace** (Journey 18: Memory Review finds projects → pre-fill workspace, PRD:812-817) — downstream, not blocking.
|
||||
- **Connector tiles depend on the Extend-layer Connector Hub** patterns (Phase 4) for OAuth/consent; for S15 v1 the existing `connectors.ts` + `oauth.ts` routes are sufficient (no Connector Hub UI dependency).
|
||||
- Shared-component extraction (§4.1) should land **before** reworking `ImportStep` so both surfaces converge rather than diverge further.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **M**
|
||||
|
||||
Backend is near-zero (one thin `/sync` alias; everything else EXISTS). The weight is frontend: extract a shared harvest component from a 685-LOC monolith, rebuild `ImportStep` to the two-grid connect layout, wire connector-OAuth tiles, surface `no_real_embedder`, and re-sequence commit into S16. Not L because no new substrate/migration and the engine is done; not S because it touches two surfaces + the S15/S16 approval split + type promotion.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Hermes / Codex / Cursor tiles** (mockup) — these are AI-coding *tools* surfaced via the AI-OS launcher/tool-detect + hooks (`LauncherApp`, `/api/tools/detect`), not harvest export adapters (Cursor falls through `UniversalAdapter`; Hermes/Codex have no harvest adapter, 05c §1 union). For v1: render as `upload`/"Other" file pickers, or as "Connect via hooks" using the AI-OS launcher, or "coming soon"? PRD §12.12 says "connect/import from AI tools" without specifying the mechanism.
|
||||
2. **S15/S16 commit split** — confirm the intended boundary: does S15 commit-and-S16-reviews-the-result, or S15-stages-previews-and-S16-commits? PRD:646/1207 ("nothing imports without review/approval") argues for the latter; current code commits at S15. (PRD Open Question — not listed in §23 but implied.)
|
||||
3. **Connector "Connect" without immediate pull** — for Notion/Drive/Slack in S15, is a "Connected" state (creds stored) sufficient, or must data pull happen in-onboarding (requires net-new `POST /api/connectors/:id/sync`, §16.9 MISSING)? Maps to PRD §23 Q4 ("which connectors/MCPs are real in v1 vs seeded/mock").
|
||||
4. **Two-surface reconciliation** — should the post-onboarding harvest surface stay in `MemoryApp → Harvest tab`, or be promoted to the PRD's Memory Center "Sources" tab (PRD §12.4 tabs include "Sources")? Affects where the shared component lives.
|
||||
5. **`no_real_embedder` UX** — block import, warn-and-proceed, or prompt for an embedding key inline? (Cognify/wiki silently skip today; semantic recall degrades.)
|
||||
278
docs/ux-refactor/gap-cards/S16-memory-review.md
Normal file
278
docs/ux-refactor/gap-cards/S16-memory-review.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# Gap Card — S16 Memory Review
|
||||
|
||||
> Screen 16 in the UX-refactor blueprint deck. Onboarding step 5 ("Memory Review — approve
|
||||
> before importing") AND the standing low-confidence review queue (Journey J08). Execution
|
||||
> model is LOCKED: in-place incremental refactor of `apps/web` + targeted backend extension.
|
||||
> Every claim below is grounded in repo source.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Purpose (PRD §12.12 step 5; Blueprint screen 16, line 392-399):** the **trust gate before
|
||||
memory becomes active**. After the user connects/imports sources (S15 Memory Import), Waggle has
|
||||
parsed-but-not-committed items. S16 lets the user *review what was found, by category, with source
|
||||
and confidence, and explicitly approve (or edit/skip) before anything is written to active memory.*
|
||||
|
||||
- Blueprint mental model (line 55-57): the **Understand** layer "classify, deduplicate, extract
|
||||
entities, map relationships and **score confidence**" — "Memory Review shows categories, source
|
||||
and confidence."
|
||||
- Two entry points, same surface:
|
||||
1. **Onboarding** (J01 First-time setup): `… → Memory Import → Memory Review → Workspace Creation`.
|
||||
2. **Standing low-confidence queue** (J08): `Home alert → Memory Review queue → inspect evidence
|
||||
→ approve/edit/reject` so "uncertain memory does not silently influence work."
|
||||
- Mockup (`screen_16_memory_review.png`, directional only): header "Review before importing";
|
||||
5 category stat tiles **Memories 342 / Decisions 56 / Tasks 32 / Artifacts 41 / Projects 17**;
|
||||
a category-tabbed table (Memories | Decisions | Tasks | Artifacts | Projects) of rows with
|
||||
Content / Type / Source / Confidence columns; search + Filters; right rail "Import summary"
|
||||
(per-category counts), "Top sources" (Claude/ChatGPT/Google Docs/Notion with item counts), and
|
||||
a "Confidence guide" legend; footer actions **Back / Skip for now / Edit selections /
|
||||
Approved & import ABS items**.
|
||||
|
||||
**PRD acceptance criteria win over pixels (§24).** The load-bearing ACs:
|
||||
- §12.12: "Nothing imports without explicit review/approval."
|
||||
- Blueprint S16 acceptance: "Trust gate before memory becomes active."
|
||||
- J08: "Uncertain memory does not silently influence work."
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
Blueprint S16 states (line 396-397) + onboarding row (line 474-476):
|
||||
|
||||
| State | Meaning |
|
||||
|---|---|
|
||||
| **Empty** | No items found / nothing to review (source had no recognizable content). |
|
||||
| **Preview found** | Parsed items shown, grouped by category, awaiting approval. |
|
||||
| **Low confidence** | Subset surfaced as uncertain — must be inspectable (evidence) and individually approve/edit/reject (J08). |
|
||||
| **Source error** | A source failed to parse/connect; offer retry-source. |
|
||||
| **Approved** | User approved; commit runs (progress) → items become active memory. |
|
||||
| **Import partial** | Onboarding state: some sources imported, some failed/skipped. |
|
||||
|
||||
Required **interactions** (line 394-395, 476): Filter, expand (inspect evidence), **edit selection**,
|
||||
**approve import**, skip, **back**, **retry source**.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `rework` (frontend) + `partial` (backend).** A trust-gate preview→commit flow EXISTS
|
||||
in two places but neither delivers the categorized, per-item-confidence, per-item-selectable review
|
||||
the screen requires. The existing surfaces are a *thin preview list + commit-all*, not a review gate.
|
||||
|
||||
### 3a. Onboarding ImportStep (the closest match to S16)
|
||||
- `apps/web/src/components/os/overlays/OnboardingWizard.tsx` — step index 3 is "memory-import"
|
||||
(`STEP_NAMES` `:35`). It calls `adapter.harvestPreview(data, source)` and stores
|
||||
`result.preview` (`:163-164`), then `adapter.harvestCommit(data, source)` on approve
|
||||
(`:168-177`), then auto-advances to the template step. **There is no separate "Memory Review"
|
||||
step** — preview and commit are folded into the single ImportStep.
|
||||
- `apps/web/src/components/os/overlays/onboarding/ImportStep.tsx` — renders a flat
|
||||
`importPreview.slice(0,10)` list (`:125-155`) with one "Import N items" button. **No category
|
||||
tiles, no per-item Type/Source/Confidence columns, no per-row selection, no evidence expand, no
|
||||
source-error/low-confidence states.** Just "preview found → commit all → Memories imported!".
|
||||
- `STEP_NAMES`/dots are hardwired to 8 steps (`:35`, dots `:472`). Inserting a dedicated Review step
|
||||
shifts the step indices (template/persona/api-key/ready are `4/5/6/7`) — a refactor touch-point.
|
||||
|
||||
### 3b. MemoryApp HarvestTab (post-onboarding harvest surface)
|
||||
- `apps/web/src/components/os/apps/memory/HarvestTab.tsx` — the richer harvest UI: connected-source
|
||||
chips, Claude Code auto-detect, SSE progress, resume banner. Its `PreviewResult` interface
|
||||
(`:28-33`) is `{ source, itemCount, types: Record<string,number>, preview: {id,title,type}[] }`.
|
||||
`types` gives a **per-`type` count map** (the seed for category tiles) but `preview` is capped at
|
||||
10 items, carries **no confidence**, and commit is **all-or-nothing** (no selection).
|
||||
- Reached today via `MemoryApp.tsx:272` (`<HarvestTab />`, the "Harvest" tab).
|
||||
|
||||
### 3c. Backend preview/commit contract (what the UI can rely on)
|
||||
- `packages/server/src/local/routes/harvest.ts`:
|
||||
- `POST /api/harvest/preview` (`:221-236`) returns `{ source, itemCount, types: countByField(items,'type'),
|
||||
preview: items.slice(0,10).map(i => ({id,title,type,source})) }`. **No confidence field; first-10 only.**
|
||||
- `POST /api/harvest/commit` (`:243`+) parses with the adapter and writes **every** item as a raw
|
||||
frame: `FrameStore.createIFrame('harvest', label+content, 'normal', 'import', ts)` (`:403-409`).
|
||||
**No selection input, no confidence, no per-item categorization beyond the adapter's `type`.**
|
||||
- `adapter.harvestPreview/harvestCommit` (`apps/web/src/lib/adapter.ts:1684-1692`) — both typed `any`,
|
||||
signature `(data, source)`. No `selectedIds`/`approve` params exist.
|
||||
- **The 4-pass `HarvestPipeline` (classify→extract→synthesize w/ `confidence` + dedup/contradiction
|
||||
flags) EXISTS but is NOT called by the commit route** (backend-map `05c` §3, §4, line 7, 110,
|
||||
117). `DistilledKnowledge.provenance.confidence` is produced only inside that unused pipeline.
|
||||
So **the confidence + categorization data the mockup shows has no production producer today** —
|
||||
this is the central backend gap (see §5).
|
||||
|
||||
**Net:** the *trust-gate intent* (preview before commit, nothing imports without consent) is partly
|
||||
honored; the *categorized, confidence-scored, per-item-approvable review surface* the screen
|
||||
specifies is **not built**.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
Create a real Review surface usable from BOTH onboarding and the standing J08 queue.
|
||||
|
||||
### Components to create
|
||||
- **`overlays/onboarding/MemoryReviewStep.tsx`** (NEW) — onboarding step 5. Replaces the inline
|
||||
preview block currently embedded in `ImportStep.tsx`. Renders the full review layout.
|
||||
- **`apps/web/src/components/os/apps/memory/MemoryReview.tsx`** (NEW, shared) — the reusable review
|
||||
panel (category tiles + tabbed table + right rail + footer actions). Both the onboarding step and
|
||||
a Memory-app "Review" surface mount this so there is ONE review implementation.
|
||||
- **`components/os/apps/memory/review/`** sub-parts (small files per CLAUDE.md file-org rule):
|
||||
`CategoryStatTiles.tsx`, `ReviewTable.tsx` (Content/Type/Source/Confidence cols + row checkbox +
|
||||
expand-for-evidence), `ImportSummaryRail.tsx` (per-category counts + Top sources + Confidence
|
||||
guide legend), `ReviewFooter.tsx` (Back / Skip / Edit selections / Approve & import N).
|
||||
|
||||
### Reuse targets (do not rebuild)
|
||||
- `ContextMenu.tsx`, `components/ui/{table,tabs,checkbox,badge,input,scroll-area,skeleton}.tsx`
|
||||
(shadcn set per frontend inventory §e) for the table/tabs/empty/loading states.
|
||||
- `ContextRail.tsx` (`overlays/ContextRail.tsx`, exports `ContextRailTarget`) as the evidence/inspect
|
||||
pattern for "expand → inspect evidence" (J08) rather than inventing a new evidence panel.
|
||||
- HarvestTab's SSE progress wiring (`/api/harvest/progress`, phases saving/cognifying/wiki-compile)
|
||||
for the "Approved → importing" state — lift the subscription into the shared panel.
|
||||
- Color semantics from Blueprint design system (line 485): green=healthy/high-confidence,
|
||||
orange=attention/low-confidence, red=risk/error — map to existing Hive DS semantic tokens
|
||||
(no `hive-950` literals; tokens in `waggle-theme.css`).
|
||||
|
||||
### Props / state (shared `MemoryReview`)
|
||||
```ts
|
||||
interface ReviewItem {
|
||||
id: string; title: string; content: string;
|
||||
kind: MemoryKind; // mapped from ImportItemType (see §6)
|
||||
source: ImportSourceType; // for the Source column + Top-sources rail
|
||||
confidence?: number; // 0-100; undefined until backend produces it (see §5)
|
||||
selected: boolean; // default true for high-confidence, false/uncertain otherwise
|
||||
evidence?: string[]; // for the expand/inspect row
|
||||
}
|
||||
interface MemoryReviewProps {
|
||||
mode: 'onboarding' | 'queue';
|
||||
items: ReviewItem[]; loading: boolean; error?: string;
|
||||
onApprove: (selectedIds: string[]) => Promise<void>; // → harvestCommit w/ selection
|
||||
onSkip: () => void; onBack?: () => void;
|
||||
onRetrySource?: (source: ImportSourceType) => void;
|
||||
}
|
||||
```
|
||||
- Local state: per-category selection, search query, active category tab, expanded-row id.
|
||||
- `selected` semantics enforce the AC: low-confidence rows start **unselected** so nothing uncertain
|
||||
imports silently (J08).
|
||||
|
||||
### Adapter / hook changes
|
||||
- Extend `adapter.harvestCommit` to accept an optional `{ selectedIds }` (or `approvedIds`) param;
|
||||
type the preview response properly (drop `any`): `HarvestPreview { source; itemCount;
|
||||
types: Record<MemoryKind, number>; items: ReviewItem[] }` — note the preview must return **all**
|
||||
items (or paginate), not `slice(0,10)`, for a real review (see §5).
|
||||
- NEW hook `useMemoryReview(source, data)` (or fold into a `useHarvest` hook) owning preview fetch +
|
||||
selection state + commit; consumed by both mount points.
|
||||
- Onboarding `STEP_NAMES`/dots/`progressPct` in `OnboardingWizard.tsx` must add the Review step
|
||||
(8→9 steps OR split import/review and re-key 4-7). Update `displayStep`, dot array (`:472`),
|
||||
Back-button range (`:444`), and the `goToStep(4)` auto-advances in `handleImportCommit`/
|
||||
`handleClaudeCodeHarvest` to land on Review, not Template.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work
|
||||
|
||||
### PRD §16.5 Harvest endpoints
|
||||
| PRD §16 endpoint | Status | Extend vs net-new / substrate |
|
||||
|---|---|---|
|
||||
| `POST /api/harvest/preview` | **EXISTS (extend)** | `harvest.ts:221`. Today returns `types` + first-10 `preview` with **no confidence** and **no full item list**. EXTEND to (a) return **all** items (or `?limit/offset` paging) and (b) attach a per-item `confidence` + normalized `kind`. Substrate: `UniversalImportItem` (already carries `type`); confidence must come from a classifier (see below). No `.mind` migration for preview (in-memory parse). |
|
||||
| `POST /api/harvest/commit` | **EXISTS (extend)** | `harvest.ts:243`. Today commits **all** parsed items. EXTEND body to accept `{ selectedIds?: string[] }`; when present, filter `items` before the `createIFrame` loop (`:382-409`). Honors the trust-gate AC ("nothing imports without approval"). Substrate: writes to `memory_frames` in personal.mind via `FrameStore.createIFrame` — unchanged shape, just a filtered set. |
|
||||
| `GET /api/harvest/sources` | **EXISTS** | `harvest.ts` (`HarvestSourceStore`). Powers the right-rail "Top sources" + connected-source chips. |
|
||||
| `POST /api/harvest/sources/:id/sync` | **PARTIAL** | No per-source `/sync` route (backend-routes.md §16.5; grep-confirmed absent). Sync today = `POST /api/harvest/commit`. Add a thin `/sources/:source/sync` that resolves the registered source + re-runs commit. NOTE PRD path uses `:id`; current sources are keyed by `:source` **name** (`DELETE/PATCH /api/harvest/sources/:source`) — keep the name key or alias. Net-new thin route, no new substrate. |
|
||||
|
||||
### Confidence + categorization (the real backend gap — implied addition to §16.5)
|
||||
The mockup's **Confidence column / Confidence guide / low-confidence queue (J08)** and the **5
|
||||
category tiles** have **no production producer**:
|
||||
- `memory_frames` has **NO `confidence` column** (substrate-types §c, line 137: confidence exists
|
||||
only on `knowledge_relations.confidence`). Frames are written `importance='normal', source='import'`.
|
||||
- The classifier that yields `{categories, value, confidence}` is the unused `HarvestPipeline`
|
||||
(Pass 1 Classify Haiku + Pass 3 Synthesize → `DistilledKnowledge.provenance.confidence`),
|
||||
bypassed by the commit route (05c line 7).
|
||||
- Category counts beyond the adapter's raw `type` (8 `ImportItemType` values) require a
|
||||
classify step; the adapter `type` alone gives a coarse grouping but not the mockup's
|
||||
Memories/Decisions/Tasks/Artifacts/Projects taxonomy 1:1.
|
||||
|
||||
**Decision needed (Open Q):** to deliver real confidence, the refactor must either
|
||||
(a) wire `HarvestPipeline`'s classify/synthesize into a **preview-time** scoring pass (cost: Haiku/
|
||||
Sonnet LLM calls per import — slow + paid; gated on a real embedder/key), or
|
||||
(b) ship a **cheap heuristic confidence** at preview time (source-trust × adapter-type × dedup
|
||||
signal) and reserve LLM scoring for an opt-in deep pass. Given onboarding latency budgets, (b) is
|
||||
the pragmatic v1; (a) for the standing J08 queue.
|
||||
|
||||
**`.mind` migration flag:** if confidence becomes a queryable/filterable frame property (Filters in
|
||||
the mockup; J08 "low-confidence review queue" off Home), add a nullable `metadata TEXT DEFAULT '{}'`
|
||||
column to `memory_frames` (substrate-types §c line 156-164: the migration runner already does
|
||||
idempotent `ADD COLUMN` on `memory_frames`; pattern at `mind/db.ts:116-124`) and store
|
||||
`{kind, confidence, sourceId, status}` there, OR promote `confidence REAL` to a real column if it's
|
||||
a primary filter axis. **This is the only potential schema migration on this screen.** Preview-only
|
||||
confidence (not persisted) needs no migration.
|
||||
|
||||
### Standing low-confidence queue (J08) backend
|
||||
- "Home alert → Memory Review queue" needs a way to *list already-imported low-confidence frames*.
|
||||
No route exists. Closest: `GET /api/memory/frames` (`memory.ts:188`) + a confidence/status filter.
|
||||
This depends on confidence being persisted (above). Implied addition to §16.4 Memory, not §16.5.
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
- **`MemoryKind`** (PRD §15.2): MISSING in `apps/web/src/lib/types.ts` (substrate-types §e). FE
|
||||
`MemoryFrame.type` exists but mismatches PRD (`event`/`insight` vs PRD `preference`/`strategy`/
|
||||
`learning`/`goal`). The review needs a `MemoryKind` union AND a **mapping from the harvest
|
||||
`ImportItemType`** (8 values: conversation/memory/instruction/preference/artifact/rule/decision/
|
||||
document — 05c §1) to the review categories (Memories/Decisions/Tasks/Artifacts/Projects). That
|
||||
map is a new pure helper (`lib/harvest-kind-map.ts`).
|
||||
- **`Confidence`** (PRD §15.2, 0-100): MISSING. Add the type; add `confidence?: number` to the
|
||||
review item shape (and later `MemoryFrame` once persisted).
|
||||
- **`ReviewItem` / `HarvestPreview`** response types: NEW (replace the `any` on
|
||||
`adapter.harvestPreview/harvestCommit`). Mirror the server contract; co-locate or add to
|
||||
`lib/types.ts`.
|
||||
- `ImportSourceType` already documented in 05c (24-value union) — import/re-declare for the Source
|
||||
column + Top-sources rail.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **Upstream (must precede):** S15 Memory Import — supplies the `{data, source}` (or connected
|
||||
source) that S16 reviews. The `harvestPreview` call + source selection live there.
|
||||
- **Shares substrate with:** S-Memory Center (§12.4) — the persisted-frame edit/archive/merge +
|
||||
the J08 low-confidence queue read from the same `memory_frames` + confidence field. Build the
|
||||
confidence-on-frames decision once and reuse.
|
||||
- **Feeds:** S-Home Cockpit (§12.1) — the "low-confidence review" Home alert (J08) deep-links into
|
||||
this surface; needs `/api/home/*` (separate gap) + the persisted-confidence read.
|
||||
- **Onboarding flow (S-Onboarding):** inserting the Review step re-keys `OnboardingWizard` step
|
||||
indices — coordinate with whoever owns the onboarding-flow gap card so step numbering is changed
|
||||
once.
|
||||
- **Phase hint:** core trust-gate (preview-with-selection + commit-selected + cheap heuristic
|
||||
confidence) is an **early phase** (it gates onboarding J01, a day-0 flow). The persisted-confidence
|
||||
column + LLM classify pass + standing J08 queue are a **later phase** (depend on the Memory Center
|
||||
confidence decision).
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort
|
||||
|
||||
**L.** The reusable review panel + onboarding step re-keying + adapter/preview type-tightening +
|
||||
commit selection are M on their own; the confidence/categorization producer (heuristic v1 now,
|
||||
optional LLM-classify + possible `memory_frames` metadata migration later) plus the J08 standing
|
||||
queue push it to **L**. It is NOT XL because no new data store is required — everything writes to the
|
||||
existing `memory_frames` substrate via `FrameStore`, and the preview/commit routes already exist to
|
||||
extend rather than build net-new.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Confidence source (blocking design):** heuristic-at-preview (source-trust × type × dedup) vs
|
||||
wiring the existing `HarvestPipeline` classify/synthesize for real LLM confidence? Latency/cost vs
|
||||
fidelity. Recommendation: heuristic for onboarding v1, LLM for the J08 queue. (§5)
|
||||
2. **Persist confidence?** If "Filters" + J08 queue need to query confidence on already-imported
|
||||
frames, we need the `memory_frames` metadata/`confidence` migration. If review is preview-only
|
||||
(pre-commit), no migration. Which scope is v1? (§5)
|
||||
3. **Category taxonomy mapping:** mockup shows Memories/Decisions/Tasks/Artifacts/Projects, but the
|
||||
adapter emits 8 `ImportItemType`s and PRD §15.2 `MemoryKind` is a different set. Which is canonical,
|
||||
and what is the exact `ImportItemType → category` map (esp. "Projects" — Claude `projects[].docs`
|
||||
land as `artifact` items per 05c §2)? (§6)
|
||||
4. **Per-item edit:** the footer "Edit selections" + interaction "edit selection" — is this just
|
||||
include/exclude (checkbox), or inline content/kind editing pre-commit? Pre-commit edit has no
|
||||
route today (frames don't exist yet); would need to mutate the in-memory item set before commit.
|
||||
5. **Onboarding step count:** split Import↔Review into two steps (8→9) or keep one screen with a
|
||||
review sub-state? Affects `STEP_NAMES`, dots, progress, and the "Step N" label already softened to
|
||||
avoid a fixed total (`OnboardingWizard.tsx:456-467`).
|
||||
6. **Source-error / retry-source state:** S15 owns connect/parse; does S16 only *display* the partial
|
||||
state and offer "retry source" (re-invoking S15's connect), or own retry itself?
|
||||
223
docs/ux-refactor/gap-cards/S17-workspace-creation.md
Normal file
223
docs/ux-refactor/gap-cards/S17-workspace-creation.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# Gap Card — S17 · Workspace Creation
|
||||
|
||||
> Execution model: **in-place incremental refactor** of `apps/web` + targeted backend extension.
|
||||
> Mockup is **directional only** (PRD §24); PRD acceptance criteria win over pixels.
|
||||
> Every claim below cites a real file. PRD = `docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Screen 17 — Workspace Creation.** "Create a workspace with suggested capabilities" — the screen that
|
||||
turns onboarding (or a manual "+ New Workspace") into the user's **first useful context**.
|
||||
|
||||
- PRD §12.12 step 6 ("Workspace Creation — create first useful context") + Journeys 1/2 (PRD lines 678,
|
||||
685: "Creates first workspace" / "Creates workspace manually" → lands in Home Cockpit).
|
||||
- Blueprint screen-table row 17 (`_blueprint_extracted.txt:402-409`): Purpose "Create a workspace with
|
||||
suggested capabilities"; Interactions "Enter name/type, add suggestions, review, create"; States
|
||||
"Empty; recommended; validation error; created"; Acceptance "Workspace becomes first useful context
|
||||
after onboarding."
|
||||
- Mockup `screen_17_create_workspace.png` (directional): a **4-step wizard** (Basic info → Preferences →
|
||||
Review & create) with a **left form column** (Description, Workspace type = Project/Client/Research/
|
||||
Personal cards), a **center "Suggested capabilities" column** (Suggested Skills / Suggested Agents /
|
||||
Suggested Connectors / Suggested MCPs, each with "Add all"), and a **right "Workspace summary" card**
|
||||
(name, type, description, "Includes: Skills/Agents/Connectors/MCPs" counts). Footer: Cancel /
|
||||
"Next: Preferences →".
|
||||
|
||||
**Net:** the mockup reframes the existing single-pane modal as a **stepped wizard whose center of gravity
|
||||
is recommended capabilities + a live summary**, driven by workspace *type* and persona — not just a name +
|
||||
template picker.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
From the blueprint state list + PRD acceptance criteria:
|
||||
|
||||
1. **Empty** — fresh form, no name/type, nothing suggested yet.
|
||||
2. **Recommended** — once type/persona/template chosen, Suggested Skills/Agents/Connectors/MCPs populate
|
||||
(silent default ordering, per `feedback_silent_recommendations_dont_ask.md` — recommend, don't interrogate).
|
||||
3. **Validation error** — name required (PRD acceptance: validation surfaced inline); tier workspace-limit
|
||||
reached (FREE = 5; existing 403 path); local storage-path missing.
|
||||
4. **Created** — workspace persisted, becomes active, transitions to Home Cockpit (S?) / Workspace Desktop.
|
||||
5. (Onboarding-embedded variant) — same flow but rendered as **step 6 of the wizard**, no modal chrome,
|
||||
handing off to step 7 Home Cockpit (PRD §12.12).
|
||||
|
||||
PRD §12.12 acceptance that bears on this screen: "Connectors/MCPs/skills are **recommended from user
|
||||
selections**" and "Onboarding asks user questions, not infrastructure questions."
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `rework`** (keep + heavily extend the existing component; do not create-new — the modal,
|
||||
its template/persona/storage/agent-group machinery, and the live folder picker are all reusable substrate).
|
||||
|
||||
| File | What it does today |
|
||||
|---|---|
|
||||
| `apps/web/src/components/os/overlays/CreateWorkspaceDialog.tsx` (1129 LOC) | The live create-workspace modal. Single scrolling pane (NOT stepped). Fields: Template picker (category filter + search + 15 built-ins + custom CRUD via `TemplateCreatorModal`), Workspace **Name**, **Group** (`STANDARD_GROUPS`), **Storage Type** (virtual/local/team) + path + `FolderPickerModal` (live `adapter.browseLocal`/`browseLocalMkdir`), **Agent** (single persona grid from `PERSONAS` OR agent-group picker), **Share with team** toggle. Calls `onCreate({ name, group, persona?, agentGroupId?, shared?, storageType?, storagePath?, templateId? })`. |
|
||||
| `apps/web/src/components/os/overlays/WorkspaceSwitcher.tsx` | Quick-switch list only (filters E2E/test names). **Not a creation surface** — adjacent, unchanged by S17. |
|
||||
| `apps/web/src/hooks/useWorkspaces.ts:27-51` | `createWorkspace(data)` → `adapter.createWorkspace(data)`, optimistic add + select, local fallback on error. |
|
||||
| `apps/web/src/lib/skill-recommendations.ts:103` | `recommendSkills(personaId)` → persona→skill-chip map w/ universal fallback. **Already the seed for "Suggested Skills".** |
|
||||
| `apps/web/src/components/os/overlays/OnboardingWizard.tsx:278,314` | Onboarding's own inline `adapter.createWorkspace(...)` call (the embedded variant) — bypasses this dialog today. |
|
||||
|
||||
**Gaps vs mockup/PRD in the current component:**
|
||||
- **No stepped wizard** — it's one scrolling modal, not Basic info → Preferences → Review.
|
||||
- **No "Workspace type"** concept (Project/Client/Research/Personal) — only `Group` (free-string chips) +
|
||||
`templateId`. (`WorkspaceType` is a missing schema field — see §5/§6.)
|
||||
- **No Suggested Agents / Suggested Connectors / Suggested MCPs panels.** Suggested *skills* logic exists
|
||||
(`recommendSkills`) but is **not wired into this dialog**. Connectors are fetched but only used for the
|
||||
template editor's chip list, not surfaced as workspace-scoped recommendations.
|
||||
- **No "Workspace summary" / Includes-counts card** (the right rail in the mockup).
|
||||
- **No `description` field.**
|
||||
|
||||
**Two real FE bugs to fix in passing (surgical, in-scope):**
|
||||
1. `useWorkspaces.createWorkspace` typed signature (`useWorkspaces.ts:27`) **omits `storageType`,
|
||||
`storagePath`, `agentGroupId`** even though the dialog passes them — they're silently dropped at the
|
||||
type boundary (the object still flows through to `adapter.createWorkspace`, but the local-fallback path
|
||||
`:35-46` and the type contract lose them). Widen the param type.
|
||||
2. `CreateWorkspaceDialog.onCreate` payload uses `persona`/`shared` (FE display names) while the backend
|
||||
POST expects `personaId` and has **no `shared`/`group→team` mapping** — `shared` is accepted by the
|
||||
dialog but never reaches `POST /api/workspaces` (which has no `shared` field; team linkage is `teamId`).
|
||||
Reconcile during rework.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Rework `CreateWorkspaceDialog.tsx` into a 3-step wizard** (keep the file; restructure internals). Prefer
|
||||
extracting step bodies into small co-located components to respect the 800-LOC ceiling (current file is
|
||||
already 1129 LOC — this rework should *reduce* it by extracting).
|
||||
|
||||
Components to create (new, under `overlays/workspace-create/`):
|
||||
- `WorkspaceTypePicker.tsx` — Project/Client/Research/Personal cards (drives type + default suggestions).
|
||||
Maps to new `WorkspaceType` union (§6).
|
||||
- `SuggestedCapabilities.tsx` — 4 sections (Skills/Agents/Connectors/MCPs) each with per-item add +
|
||||
"Add all". Reuse `recommendSkills(personaId)` for Skills; derive Agents from `PERSONAS` +
|
||||
`adapter.getAgentGroups()`; Connectors from `adapter.getConnectors()` (already fetched here); MCPs from
|
||||
a new MCP list source (§5). Silent-default ordering (no "which tools?" prompt).
|
||||
- `WorkspaceSummaryCard.tsx` — live right-rail: name, type, description, "Includes" counts (skills/agents/
|
||||
connectors/mcps selected).
|
||||
- `WizardStepper.tsx` — Basic info / Preferences / Review header (or reuse a stepper primitive if added).
|
||||
|
||||
Reuse targets (do NOT rebuild): `TemplateCreatorModal`, `FolderPickerModal`, `ChipPicker`, `Tooltip`,
|
||||
`STORAGE_OPTIONS`, the persona grid, `STANDARD_GROUPS`, `LockedFeature` (tier gate), `useFeatureGate`
|
||||
(`multi-workspace`), `useWorkspaces`.
|
||||
|
||||
State/props additions:
|
||||
- Local state: `step` (0|1|2), `description`, `type` (`WorkspaceType`), `selectedSkills[]`,
|
||||
`selectedAgentIds[]`, `selectedConnectorIds[]`, `selectedMcpIds[]`.
|
||||
- Extend `onCreate(...)` payload to include `description, type, skills, agentIds, connectorIds, mcpIds`.
|
||||
- Wire `recommendSkills` + a new `recommendCapabilities(type, personaId)` helper in
|
||||
`apps/web/src/lib/` (extends existing skill-recommendations pattern) so suggestions react to
|
||||
type/persona without a backend call where possible.
|
||||
|
||||
Adapter/hook:
|
||||
- Widen `useWorkspaces.createWorkspace` param type (fix §3 bug 1) to carry the new fields + storage fields.
|
||||
- `adapter.createWorkspace` already POSTs the body through (`lib/adapter.ts` `createWorkspace` → `POST
|
||||
/api/workspaces`); new body fields ride along once the route accepts them (§5).
|
||||
- For Suggested MCPs, add `adapter.getMcps()` if a `/api/mcps` (or capabilities-derived) source is built (§5).
|
||||
|
||||
Onboarding parity: route `OnboardingWizard` step 6 through the **same** step bodies (render without modal
|
||||
chrome) instead of its bespoke `adapter.createWorkspace` calls (`OnboardingWizard.tsx:278,314`) — single
|
||||
source of truth for the create flow.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.2 + suggestion sources)
|
||||
|
||||
§16.2 endpoints used by/adjacent to this screen:
|
||||
|
||||
| PRD endpoint | Status | Extend vs net-new / substrate |
|
||||
|---|---|---|
|
||||
| `POST /api/workspaces` | **EXISTS** | `workspaces.ts:116-135`. **EXTEND** the Body type + `WorkspaceManager.create` to accept `description`, `type` (`WorkspaceType`), `skills[]`, `agentIds[]`, `connectorIds[]`, `mcpIds[]`, `status`. These are additive fields on `WorkspaceConfig`/`CreateWorkspaceOptions` (`workspace-manager.ts:5-95`). Substrate: `workspace.json` (file, not SQLite). |
|
||||
| `GET /api/workspaces` | **EXISTS** | `workspaces.ts:101`. Unchanged. |
|
||||
| `GET /api/workspaces/:id` | **EXISTS** | `workspaces.ts`. Unchanged. |
|
||||
| `PATCH /api/workspaces/:id` | **EXISTS** | Used post-create for edits; should accept the new fields too. |
|
||||
| `GET /api/workspace-templates` | **EXISTS** (not in §16 but central here) | `workspace-templates.ts:33` (15 built-ins). Templates already carry `persona`, `connectors`, `suggestedCommands`, `starterMemory` — the seed for Suggested panels. May **EXTEND** the `WorkspaceTemplate` shape to add `skills[]`/`mcps[]`/`type` so a chosen template can pre-populate all 4 suggestion lists. |
|
||||
|
||||
Suggested-capabilities data sources (mockup center column):
|
||||
- **Skills** — no new endpoint; FE `recommendSkills` + starter-pack catalog (`GET
|
||||
/api/skills/starter-pack/catalog`, EXISTS). On create, the route already auto-installs starter skills
|
||||
(`workspaces.ts:198-211`) and seeds template starter-memory frames (`:223-249`).
|
||||
- **Agents** — `GET /api/personas` (EXISTS) + `GET /api/agent-groups` (EXISTS). No new endpoint.
|
||||
- **Connectors** — `GET /api/connectors` (EXISTS). No new endpoint.
|
||||
- **MCPs** — **PARTIAL/MISSING**: per backend-routes inventory §16.9, there is **no `GET /api/mcps`**.
|
||||
MCP servers surface inside `GET /api/capabilities/status` (`mcpServers[]`) and the static catalog
|
||||
`@waggle/shared` `mcp-catalog.ts`. **EXTEND** rather than net-new store: add a thin `GET /api/mcps`
|
||||
read route that composes `capabilities/status` + `mcp-catalog`, OR have the FE read the existing catalog
|
||||
+ capabilities/status directly for the Suggested-MCPs panel (lower-risk for S17; defer the dedicated
|
||||
route to S20/MCP screen).
|
||||
|
||||
**No new data store needed.** Persisting `agentIds`/`connectorIds`/`mcpIds` on the workspace = additive
|
||||
optional fields on the `workspace.json` config (substrate-types §a confirms: **no DB migration** — these
|
||||
live in JSON, not SQLite). `WorkspaceManager.create/update` (`workspace-manager.ts:124-244`) writes the
|
||||
JSON; `update()` should also stamp `updatedAt` (currently missing, substrate-types §a) — fold that in here
|
||||
since we're extending the write path anyway.
|
||||
|
||||
**.mind migration:** **None required for S17.** (The memory-side `metadata`/`confidence` migration in
|
||||
substrate-types §c belongs to the Memory screen, not workspace creation.)
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
- **`WorkspaceType`** (PRD §15.2/§15.3) — **MISSING** everywhere. New literal union; mockup implies
|
||||
`'project' | 'client' | 'research' | 'personal'` (confirm exact set — see Open Questions). Add to FE
|
||||
`apps/web/src/lib/types.ts` AND backend `WorkspaceConfig` (`workspace-manager.ts`). Keep the existing
|
||||
free-string `group` (don't drop — substrate-types §a "keep, do not drop").
|
||||
- **`WorkspaceConfigV2` deltas** (PRD §15.3, lines 961-985) touched by this screen: add `description`,
|
||||
`type`, `status` (`'active'|'paused'|'archived'`, default `'active'`), `agentIds[]`, `connectorIds[]`,
|
||||
`mcpIds[]`, `updatedAt`. (`storageType`/`storagePath`/`teamId`/`teamRole`/`riskLevel`/`created` already
|
||||
present.) Per substrate-types §a these are **pure additive optional fields, no DB migration**.
|
||||
- FE `Workspace` (`lib/types.ts:22-40`) is a lossy projection (`persona` string vs config `personaId`;
|
||||
derived `memoryCount`/`lastActive`/etc.). Add the same `type`/`description`/`status` + capability-id
|
||||
arrays; reconcile `persona`→`personaId` mapping at the adapter boundary (don't break the switcher,
|
||||
which reads `ws.persona`).
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **Schema additions are foundational** — `WorkspaceType` + the V2 config fields are shared by S?? Home
|
||||
Cockpit, S?? Workspace Desktop (header shows type/status, substrate-types §b), and the Agent/Artifact
|
||||
screens. Land the type + `POST/PATCH /api/workspaces` extension as a **shared early phase** before the
|
||||
S17 UI rework so downstream screens consume the same fields.
|
||||
- **Suggested capabilities** depends on the Skills (recommendSkills, exists), Personas/Agent-groups
|
||||
(exist), Connectors (exists) sources — all available now. Only **Suggested MCPs** has a missing/partial
|
||||
backend; gate that panel behind the MCP source decision (don't block S17 on the dedicated `/api/mcps`).
|
||||
- **Onboarding (S?? §12.12)** consumes this flow as step 6 — unify after the standalone modal rework so
|
||||
both paths share one implementation.
|
||||
- PRD roadmap places this in **Sprint 5 — Onboarding and workspace creation** (PRD line 1335).
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort
|
||||
|
||||
**M.** The modal, template/persona/storage/agent-group machinery, folder picker, and skill-recommendation
|
||||
helper all exist — the work is restructuring into a 3-step wizard, adding 3-4 small components (type picker,
|
||||
suggested-capabilities, summary card), wiring existing suggestion sources, and additive (no-migration)
|
||||
backend field extension on the workspace config. Pushed toward the high end of M by the Suggested-MCPs
|
||||
source ambiguity, the onboarding-parity unification, and the two FE bugs to reconcile; not L because there
|
||||
is no new data store and no `.mind` migration.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Exact `WorkspaceType` values.** Mockup shows Project/Client/Research/Personal. PRD §15.2 names
|
||||
`WorkspaceType` but does not enumerate. Confirm the canonical set (and whether `type` replaces or
|
||||
coexists with the existing free-string `group`/`STANDARD_GROUPS`). Recommendation: coexist — `type` is
|
||||
the new structured axis, `group` stays as the user's organizational label.
|
||||
2. **Suggested MCPs source for S17.** Build the thin `GET /api/mcps` now, or read `capabilities/status` +
|
||||
`mcp-catalog` directly from the FE and defer the dedicated route to the MCP screen? (Lower-risk =
|
||||
defer.)
|
||||
3. **`shared`/team handoff.** The dialog has a "Share with team" toggle that currently never reaches the
|
||||
backend (no `shared` field on `POST /api/workspaces`). For S17, does "share" set `teamId`/`teamRole`
|
||||
(requires a team to exist + TEAMS tier), or is it deferred to the Team screen? Today it's a dead toggle.
|
||||
4. **Wizard vs modal in onboarding.** Confirm the embedded onboarding variant should render the *same*
|
||||
step bodies (preferred, single source of truth) vs keeping `OnboardingWizard`'s bespoke create call.
|
||||
5. **Does S17 install/connect on create, or only stage selections?** PRD says recommend; mockup "Add all"
|
||||
implies staging. Confirm that selected connectors/MCPs are *recorded as workspace intent* (ids on the
|
||||
config) vs *actually installed/connected* at create time (the latter pulls in install-audit + connector
|
||||
OAuth flows — much larger scope; recommend record-only for S17).
|
||||
205
docs/ux-refactor/gap-cards/S18-agent-builder.md
Normal file
205
docs/ux-refactor/gap-cards/S18-agent-builder.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# Gap Card — S18 Agent Builder
|
||||
|
||||
> UX-refactor planning artifact. Execution model: **in-place incremental refactor** of
|
||||
> `apps/web` + targeted backend extensions. Every claim is grounded in a real file.
|
||||
> PRD = `docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`
|
||||
> (§12.9 + §16.7). Mockup (directional only) =
|
||||
> `Waggle_OS_Handoff_Assets/screens_18_21_builders_and_marketplace.png`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
Create an **agent as an explicit work actor** through a guided stepper, with no hidden
|
||||
memory/tool access. PRD §12.9: "Manage agents as explicit work actors… User can explain
|
||||
what an agent can see and do before enabling it." Blueprint Screen 18 (`_blueprint_extracted.txt:410-416`):
|
||||
"Create an agent with goal, model, autonomy, memory, skills and permissions. Configure,
|
||||
test, review, create. States: Draft; validation; approval needed; created. Acceptance:
|
||||
No agent has hidden memory/tool access."
|
||||
|
||||
The Agent Builder is the create-flow companion to the **Agent Center** (Screen 9, a
|
||||
separate gap card). S18 is one of four builder/marketplace screens grouped in the same
|
||||
mockup (18 Agent Builder, 19 Skill Builder, 20 Automation Builder, 21 Marketplace).
|
||||
|
||||
PRD §16.7 names the agent CRUD/run/traces API surface this builder writes to.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
**Builder steps (PRD §12.9, line 589):** `Basic Info → Capabilities → Memory & Tools → Permissions → Review & Create` (5-step stepper).
|
||||
|
||||
**Agent fields the builder must collect (PRD §12.9 line 590 + §15.5 lines 1015-1036):**
|
||||
name, goal, description, persona/avatar, model, autonomyLevel (`manual|guided|medium|high`,
|
||||
PRD §15.2), type (`personal|workspace|team|autonomous`), memoryScopes, skillIds,
|
||||
connectorIds, mcpIds, permissions, status, workspaceIds, createdBy, lastRunAt, successRate.
|
||||
|
||||
**Per-screen states (blueprint `:413-414` + `:461-463`):**
|
||||
- `Draft` — partially-filled builder, save without activating.
|
||||
- `Validation` — required-field gating (goal, model, memory scope, skills/tools/MCPs, autonomy per blueprint `:134`).
|
||||
- `Approval needed` — elevated permission selections require an approval prompt before create (PRD §12.9 "all elevated access is reviewed"; §17.3 "elevated actions require human approval").
|
||||
- `Created` — agent appears in Agent Center + target workspace (Journey 10, PRD lines 746-753).
|
||||
- Plus global states (PRD §14.1): Loading, Error, Offline/local-only, Permission denied.
|
||||
|
||||
**Hard acceptance gate (blueprint `:415-416`, `:134`):** "No agent has hidden memory/tool access" and "Agent Builder must require model, goal, memory scope, skills/tools/MCPs and autonomy level." The Review step must render the full effective permission/memory/tool surface before the user confirms.
|
||||
|
||||
**Test affordance:** blueprint `:413` "Configure, **test**, review, create" — a dry-run before create (analogous to Skill Builder's test). PRD §12.9 does not list test as a functional requirement; treat as optional (open question §9).
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo — disposition: **create-new** (builder) + **partial** (substrate reuse)
|
||||
|
||||
There is **no Agent Builder and no first-class Agent entity** today. The closest existing
|
||||
surface is a **persona/agent-group** model, which is a strict subset of the PRD Agent.
|
||||
|
||||
**Existing frontend (the seed):**
|
||||
- `apps/web/src/components/os/overlays/SpawnAgentDialog.tsx` — a 2-step dialog (`config → confirm`, `:29`) that spawns a **transient sub-agent** (task + persona + model + parent workspace) via `adapter.spawnAgent` → `POST /api/fleet/spawn`. It collects only `task/persona/model/workspaceMode` (`:37-44`); it has model-fetch with 3-tier fallback (`:50-99`) and a cost-estimate review pane (`:388-421`). It does **not** persist an agent definition — it fires a one-shot fleet job. This is the seed named in the task, but it is a *spawn* dialog, not a *builder*.
|
||||
- `apps/web/src/components/os/apps/AgentsApp.tsx` — titled **"Personas"** (`:175`), two tabs (`agents`/`groups`, `:16`). Lists personas via `adapter.getPersonas()` (`:36`) and agent-groups via `adapter.getAgentGroups()` (`:38`). "New Persona" → `CreateAgentForm`.
|
||||
- `apps/web/src/components/os/apps/agents/CreateAgentForm.tsx` — a **single-page** form (not a stepper) collecting `name/description/icon/systemPrompt/tools[]` (`:9`), with an "Generate with AI" box (`:58-81`) → `adapter.generatePersona`. This is the closest existing "create-an-agent" UI, but it maps to a **persona** (no goal/autonomy/model/memoryScope/connectors/mcps/permissions).
|
||||
- `apps/web/src/components/os/apps/agents/AgentDetail.tsx` — read view showing icon/name/description/tools/commands/affinity (`:49-103`). No goal/model/autonomy/permissions/successRate/status.
|
||||
|
||||
**Existing backend (the substrate to reuse, not rebuild):**
|
||||
- `packages/server/src/local/routes/personas.ts` — `GET/POST/PATCH/DELETE /api/personas` + `POST /api/personas/generate`. POST persists a custom `AgentPersona` to disk (`saveCustomPersona`, `:57`); fields are `id/name/description/icon/systemPrompt/modelPreference/tools/workspaceAffinity/suggestedCommands/defaultWorkflow` (`:45-56`). **No** goal/autonomy/memoryScopes/connectorIds/mcpIds/permissions/status. PRO-tier gated (`:32`).
|
||||
- `AgentPersona` interface (`packages/agent/src/personas.ts`; full field list in `docs/backend-map/sections/05a-subsystem-agent-runtime.md:198-218`) already carries `suggestedSkills?/suggestedConnectors?/suggestedMcpServers?/disallowedTools?/isReadOnly?` — useful seed columns for the richer Agent.
|
||||
- `packages/server/src/local/routes/agent-groups.ts` — `GET/POST/PATCH/DELETE /api/agent-groups` + `POST /api/agent-groups/:id/run` (the run is a **placeholder stub**, `:105-127` — returns a queued jobId, does not execute). Groups persist to `{dataDir}/agent-groups.json` (`:29`). This is the closest "agent persistence on disk via JSON file" precedent.
|
||||
- `packages/server/src/local/routes/fleet.ts` — `POST /api/fleet/spawn` is the **only real agent-execution path** (`:66-257`): creates a session, emits Waggle signals, and **fire-and-forgets `runAgentLoop`** in the background (`:142-245`). `POST /api/fleet/:workspaceId/pause|resume|kill` (`:260-290`). This is what `/api/agents/:id/run` and `/pause` must wire onto.
|
||||
- `execution_traces` table (`packages/hive-mind-core/src/mind/schema.ts:199`) is written from `chat.ts` and `evolution.ts` (grep-confirmed) but has **no HTTP read route** — `/api/agents/:id/traces` is net-new over this store.
|
||||
|
||||
**Why create-new, not rework:** the PRD Agent (§15.5) is a **superset** of persona — it
|
||||
adds goal, autonomy, type, memoryScopes, connectorIds, mcpIds, permissions, status,
|
||||
workspaceIds, lastRunAt, successRate. A persona is the *behavioral template*; a PRD Agent
|
||||
is an *instantiated, scoped, governed actor*. Bending `CreateAgentForm`/persona-POST to
|
||||
carry all of that would corrupt the persona contract (shared by the runtime prompt
|
||||
composer). The clean model: **net-new Agent entity + 5-step builder**, with a persona
|
||||
selected *as one field* of the agent. Heavy reuse of persona catalog, model picker,
|
||||
tool catalog, fleet-spawn, and execution-traces underneath.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Create new — `apps/web/src/components/os/overlays/AgentBuilder.tsx`** (or
|
||||
`components/os/apps/agents/AgentBuilder.tsx` if hosted inside the Agents app shell).
|
||||
A 5-step stepper matching PRD §12.9:
|
||||
|
||||
1. **Basic Info** — name, goal (required), description, persona/avatar picker, type (`AgentType`).
|
||||
2. **Capabilities** — model (required), autonomyLevel (required), defaultWorkflow.
|
||||
3. **Memory & Tools** — memoryScopes (required), skillIds, tools[] (required-ish per blueprint `:134`).
|
||||
4. **Permissions** — connectorIds, mcpIds, explicit permissions; surface elevated-access warnings here (drives the "approval needed" state).
|
||||
5. **Review & Create** — full effective surface (the "no hidden access" gate), then create.
|
||||
|
||||
**Reuse targets (do not rebuild):**
|
||||
- **Stepper chrome:** there is no shared `BuilderStepper` primitive yet (PRD §19.1 lists "Builder stepper" as a design-system component to create). S18/S19/S20 all need it — build it **once** as `components/ui/builder-stepper.tsx` and share. (Cross-screen dependency — flag to the Skill Builder & Automation Builder cards.)
|
||||
- **Model picker:** reuse `components/os/ModelSelector.tsx` (already used by Spawn/Settings/onboarding) instead of `SpawnAgentDialog`'s bespoke model-button grid (`SpawnAgentDialog.tsx:328-344`).
|
||||
- **Persona picker:** reuse the persona grid pattern from `SpawnAgentDialog.tsx:258-276` + `PERSONAS` from `@/lib/personas`; or fetch live via `adapter.getPersonas()` (as `AgentsApp.tsx:36`).
|
||||
- **Tool picker:** reuse the searchable checkbox list from `CreateAgentForm.tsx:104-145` (tools sourced from `adapter.getCapabilityStatus()` → flattened `ToolDef[]`, `AgentsApp.tsx:49-55`).
|
||||
- **Skills/connectors/MCPs pickers:** `adapter.getSkills()`, `adapter.getConnectors()`, and MCP list (see §5 — MCP list endpoint is itself partial). Persona's `suggestedSkills/suggestedConnectors/suggestedMcpServers` (05a `:216-218`) can pre-seed recommendations.
|
||||
- **Cost-estimate review pane:** lift from `SpawnAgentDialog.tsx:388-421` (`adapter.getModelPricing()`).
|
||||
- **Approval prompt:** reuse the approvals pattern (`ApprovalsApp` / inline chat approvals) for the elevated-access gate; do not invent a new modal.
|
||||
|
||||
**Adapter methods to add (`apps/web/src/lib/adapter.ts` — the single sidecar gateway, ~150 methods):**
|
||||
`getAgents()`, `createAgent(def)`, `getAgent(id)`, `patchAgent(id, partial)`, `runAgent(id, {task?})`,
|
||||
`pauseAgent(id)`, `getAgentTraces(id)`. (Today the adapter has `getPersonas/createPersona/…`,
|
||||
`spawnAgent`, `getFleet/fleetAction`, `getAgentGroups/…` — none of these is the PRD Agent CRUD.)
|
||||
|
||||
**Wiring:** register the builder open path through the existing `waggle:open-app` CustomEvent
|
||||
/ overlay state (`useOverlayState`); add it as a "New Agent" entry in the Agents app (which
|
||||
should be renamed/retitled from "Personas" to host both — Agent Center card decides final IA).
|
||||
**Reuse SpawnAgentDialog** as the quick "run now without saving" path — keep it; it is a
|
||||
different verb (ephemeral spawn) from the builder (persisted definition).
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.7 endpoints)
|
||||
|
||||
> Naming collision (backend-routes inventory `:486-492`): the **Cloud** server has a
|
||||
> Clerk-gated `routes/agents.ts` with `/api/agents` CRUD — but that is NOT the sidecar.
|
||||
> The desktop frontend talks only to the **local sidecar**, where the sidecar agent surface
|
||||
> is `/api/agent/*` (singular) + `/api/agents/active` + `/api/agent-groups/*` + `/api/fleet/*`.
|
||||
> So in the desktop context every §16.7 row below is MISSING-or-PARTIAL **locally**.
|
||||
|
||||
| PRD §16.7 endpoint | Status (sidecar) | Extend vs net-new + substrate |
|
||||
|---|---|---|
|
||||
| `GET /api/agents` | **MISSING** | Net-new route. Reads a net-new agent-definition store. Closest existing "definitions": `GET /api/personas` (`personas.ts:15`) + `GET /api/agent-groups` (`agent-groups.ts:49`) — neither is the PRD Agent. |
|
||||
| `POST /api/agents` | **MISSING** | Net-new. **Pattern to reuse:** agent-groups JSON-file persistence (`agent-groups.ts:29-43`, `{dataDir}/agent-groups.json`) → store agents at `{dataDir}/agents.json` (no SQLite, mirrors persona/group precedent). Validate goal/model/memoryScopes/autonomy/permissions. PRO-tier gate like `personas.ts:32`. |
|
||||
| `GET /api/agents/:id` | **MISSING** | Net-new read over the new store. |
|
||||
| `PATCH /api/agents/:id` | **MISSING** | Net-new partial-update (mirror `agent-groups.ts:74-90` PATCH shape). |
|
||||
| `POST /api/agents/:id/run` | **PARTIAL** | **Extend, do not build new execution.** Resolve agent → call the real executor `POST /api/fleet/spawn` (`fleet.ts:66`, the only path that actually runs `runAgentLoop` `:185`). Map agent's persona/model/memoryScope/workspace onto the spawn body. (`/api/agent-groups/:id/run` is a stub — `agent-groups.ts:105` — do NOT reuse it as the model.) |
|
||||
| `POST /api/agents/:id/pause` | **PARTIAL** | **Extend.** Map agent's running session → `POST /api/fleet/:workspaceId/pause` (`fleet.ts:260`). |
|
||||
| `GET /api/agents/:id/traces` | **PARTIAL / net-new route** | No HTTP route reads traces today. The `execution_traces` table exists (`schema.ts:199`) and is written by `chat.ts`/`evolution.ts`. Add a thin read route over the execution-trace store, filtered by agent/session. Fallback: session timeline `GET /api/workspaces/:wid/sessions/:sid/timeline` (`sessions.ts`). |
|
||||
|
||||
**Substrate touched:** new `{dataDir}/agents.json` (file store — no DB migration);
|
||||
`personas.ts` (read for persona field); `fleet.ts` (run/pause delegation);
|
||||
`execution_traces` (read for traces); `install_audit` (write an audit entry on agent
|
||||
create when elevated connectors/MCPs are attached — `InstallAuditStore`, see §d of the
|
||||
substrate inventory; note the `risk_level='critical'` CHECK-constraint drift bug flagged
|
||||
there `install-audit.ts:16` vs DDL `:65`).
|
||||
|
||||
**.mind migration:** **NONE required.** The agent definition lives in a JSON file (precedent:
|
||||
persona disk store + `agent-groups.json`), not in SQLite. `lastRunAt`/`successRate` are
|
||||
derived (successRate from `execution_traces.outcome` / `procedures.success_rate`
|
||||
`schema.ts:147,206`), not new columns. This matches the LOCKED in-place model and the
|
||||
backend-routes inventory verdict ("none require a new data store").
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
Per substrate inventory §(e): **none of the PRD §15.2 unions exist in `apps/web/src/lib/types.ts`.**
|
||||
S18 needs:
|
||||
- `AgentType = 'personal' | 'workspace' | 'team' | 'autonomous'` (PRD §15.2) — **MISSING**.
|
||||
- `AutonomyLevel = 'manual' | 'guided' | 'medium' | 'high'` (PRD §15.2) — **MISSING** in FE (note: a *different* `AutonomyLevel` exists in `hooks/useWindowManager.ts` for chat windows — Normal/Trusted/YOLO; do **not** reuse, they are different vocabularies — flag the collision).
|
||||
- `Scope = 'personal' | 'workspace' | 'team' | 'organization'` (for `memoryScopes`) — **MISSING**.
|
||||
- A full **`Agent`** interface (PRD §15.5 fields) — **MISSING**. Today `packages/shared/src/types.ts` has a thin `AgentDef` (`:36-47`: id/userId/teamId/name/role/systemPrompt/model/tools/config/createdAt) lacking goal/type/autonomy/memoryScopes/skillIds/connectorIds/mcpIds/permissions/status/lastRunAt/successRate. Decide: extend `AgentDef` (shared, cloud-aligned) vs add a desktop-local `Agent` in `lib/types.ts`. Given the sidecar-only execution model, a **new `Agent` interface in `lib/types.ts` + a matching server-side type** is cleanest; keep `AgentDef` for the cloud path.
|
||||
- `ExtensionType` (for the connector/mcp/skill pickers' provenance) — **MISSING** but shared with S19/S21.
|
||||
|
||||
Keep types consistent with the new `agents.json` shape (PRD §22.2 "Frontend types are
|
||||
consistent with API contracts").
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **Phase 3 (Intelligence layer)** per PRD §8 / Sprint 6 — same phase as Agent Center (S9), Skill Builder (S19), Automation Builder (S20).
|
||||
- **Agent Center (Screen 9)** — the builder's create result must land somewhere; the Agent Center list/card view + the new `Agent` type + `GET /api/agents` are a shared prerequisite. Sequence: define `Agent` type + agent store + `GET/POST /api/agents` **with** Agent Center, then layer the builder on top.
|
||||
- **Shared `BuilderStepper` primitive** (PRD §19.1) — build once, shared by S18/S19/S20. Whichever builder ships first owns it.
|
||||
- **Connector Hub (S?) / MCP Hub (S?)** — the Permissions step picks connectorIds/mcpIds; the MCP listing is itself PARTIAL (no `GET /api/mcps`; data lives in `capabilities/status.mcpServers[]` + `@waggle/shared mcp-catalog.ts`). The picker can read those existing surfaces without waiting for the full Hubs, but the canonical MCP list endpoint is a cross-card dependency.
|
||||
- **Approval prompt** component (PRD §19.1) — reuse existing approvals plumbing; not a hard blocker.
|
||||
- **AppShell / IA** (Phase 0/1) — the builder opens via the existing window-manager overlay path; no new routing (single-route windowed desktop, per frontend inventory §b).
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **L**
|
||||
|
||||
Net-new 5-step builder UI + net-new `Agent` entity, store, and CRUD routes, plus
|
||||
run/pause delegation onto fleet and a net-new traces read route — but **every backend
|
||||
piece reuses an existing substrate** (persona catalog, agent-groups JSON-file precedent,
|
||||
fleet-spawn executor, execution-traces store; no `.mind` migration). The heavy reuse and
|
||||
absence of a DB migration keep it off XL; the breadth (4 new + 3 partial endpoints, a new
|
||||
shared entity/type, a shared stepper primitive, and the elevated-access approval gate)
|
||||
keeps it above M.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Agent vs persona boundary.** Confirm the model: persona = behavioral template (one
|
||||
*field* of an agent) vs agent = scoped governed instance. This card assumes that split.
|
||||
Should creating an agent ever auto-create a backing persona, or always reference one?
|
||||
2. **Store location.** `{dataDir}/agents.json` (mirrors `agent-groups.json`) vs a new
|
||||
`agents` row in personal `.mind`. This card recommends the JSON file (no migration,
|
||||
matches precedent). Confirm.
|
||||
3. **`/run` semantics.** Does Agent Builder's eventual run mean "fleet-spawn a one-shot in
|
||||
a chosen workspace" (current real path) or "persistent agent that keeps running"? PRD
|
||||
agent states include `Running/Paused/Completed` (§14.5) — confirm the lifecycle the
|
||||
sidecar must support beyond fleet's ephemeral sessions.
|
||||
4. **Test step.** Blueprint says "configure, **test**, review, create" (`:413`); PRD §12.9
|
||||
omits test. Include a dry-run (like Skill Builder's `POST /api/skills/:id/test`) or defer?
|
||||
5. **`successRate` source.** Derive from `execution_traces.outcome` vs `procedures.success_rate`
|
||||
vs a new counter? (Both exist; pick one to avoid a third tally vocabulary.)
|
||||
6. **Autonomy vocabulary collision.** PRD `AutonomyLevel` (`manual/guided/medium/high`) vs
|
||||
the chat-window `AutonomyLevel` (Normal/Trusted/YOLO in `useWindowManager.ts`) vs the
|
||||
agent-loop's tiered-autonomy. Which governs an agent's tool execution at run time?
|
||||
7. **Cloud vs sidecar `/api/agents`.** The Cloud server already has Clerk-gated `/api/agents`
|
||||
CRUD. Should the desktop sidecar's new routes share a contract/shape with it for future
|
||||
sync, or stay independent?
|
||||
242
docs/ux-refactor/gap-cards/S19-skill-builder.md
Normal file
242
docs/ux-refactor/gap-cards/S19-skill-builder.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# Gap Card — S19 · Skill Builder
|
||||
|
||||
> UX-refactor planning artifact. Execution model is LOCKED **in-place incremental refactor**
|
||||
> of `apps/web` + targeted sidecar extensions. Mockups are directional (PRD §24);
|
||||
> PRD acceptance criteria win. Every claim below is grounded in a cited file.
|
||||
>
|
||||
> Sources: PRD §12.6 + §16.8 (`docs/.../Waggle_OS_UX_Refactor_PRD.md`), Blueprint Screen 19
|
||||
> (`_blueprint_extracted.txt:417-421`, state model `:464`), mockup
|
||||
> `Waggle_OS_Handoff_Assets/screens_18_21_builders_and_marketplace.png`, backend-map
|
||||
> `sections/03d` + `05g`, inventories under `docs/ux-refactor/_inventory/`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Skill Builder** — the *create/edit* surface for a reusable capability (Skill). PRD §12.6:
|
||||
a stepper Builder ("Basic Info → Instructions → Inputs & Outputs → Tools & Data → Review & Create"),
|
||||
with **test-run before publishing** and assignment to agents/workspaces/automations or direct use.
|
||||
Blueprint Screen 19 (`_blueprint_extracted.txt:417-421`): *"Create reusable capability. Define
|
||||
prompt, inputs/outputs, tools, memory access, test."* States: *Draft; test pass/fail; published;
|
||||
archived.* Acceptance: *"Skills are inspectable and reusable by agents/automations"* (PRD §12.6
|
||||
acceptance: *"a user can understand what a skill does, where it is used, and what access it has"*).
|
||||
|
||||
This is the **Builder half of the Intelligence layer's Skills surface** (S06 Skills Hub is the
|
||||
browse/library half). PRD Roadmap puts both in Sprint 6 / Phase 3 (`PRD:344-351`, `:236-240`).
|
||||
The mockup tile labelled "Skill Builder" shows a left form column (name/description/category +
|
||||
instructions textarea) and a right column (tools picker + memory access + a Test panel + a primary
|
||||
"Create Skill" action) — directional confirmation of the §12.6 stepper.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
PRD §14.1 global states (every screen) + Blueprint Skills row (`_blueprint_extracted.txt:464`):
|
||||
*Loading, empty, populated, error; Installed, draft, custom, workspace, marketplace, update available;
|
||||
recovery: Test, install, publish, archive, rollback.*
|
||||
|
||||
Builder-specific lifecycle (Blueprint Screen 19 `:417-421`): **Draft → test pass/fail → published →
|
||||
archived**, with PRD global add-ons: **validation error**, **approval required** (skills that request
|
||||
elevated tool/secret access), **offline/local-only**.
|
||||
|
||||
Stepper steps the Builder must implement (PRD §12.6 `:542`):
|
||||
1. **Basic Info** — name, description, category.
|
||||
2. **Instructions** — the prompt/steps body.
|
||||
3. **Inputs & Outputs** — declared input params + expected output shape.
|
||||
4. **Tools & Data** — tools/data + **memory access** (read scope).
|
||||
5. **Review & Create** — summary + **Test run** + publish-to-scope (personal/workspace/team).
|
||||
|
||||
Per-state behaviours required:
|
||||
- **Validation error** — name normalises to kebab-case; empty name/description/steps blocked
|
||||
(backend already 400s on these — §5).
|
||||
- **Test pass/fail** — inline Test panel showing the injected-prompt preview + parsed metadata.
|
||||
- **Published** — confirmation + the new skill appears in the Hub (Installed list).
|
||||
- **Approval required** — when declared tools include elevated/secret/code-exec permissions
|
||||
(frontmatter `permissions` block exists — `skill-frontmatter.ts:40-48`).
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `create-new`** (frontend Builder), **`keep-promote`** (backend structured-create
|
||||
contract). There is **no Skill Builder component anywhere in `apps/web/src`** — grep for
|
||||
`SkillBuilder|Skill Builder|skill-builder` over `apps/web/src` returns **0 matches**. Skill creation
|
||||
today is only available to the *agent* (the `create_skill` tool, `skill-creator.ts:8`), not to the
|
||||
*user* via UI.
|
||||
|
||||
What exists today:
|
||||
|
||||
- `apps/web/src/components/os/apps/CapabilitiesApp.tsx` — "Skills & Apps". A **browse/install/test**
|
||||
surface only. Tabs: `installed | starter | marketplace | tools | audit` (`:80`, `:358`). It lists
|
||||
packs (`PackCard` `:190`), shows a detail drawer (`PackDetail` `:249`), installs
|
||||
(`handleInstall`/`handleMarketplaceInstall` `:164,:174`), and **tests** an existing skill
|
||||
(`handleTestSkill` → `POST /api/skills/test` `:88-102`). **It has NO create/edit form.** This is
|
||||
the natural host to add a "+ Create Skill" entry point that launches the new Builder.
|
||||
- `apps/web/src/lib/adapter.ts:652` — `createSkill({ name, description })` exists but is **lossy**:
|
||||
it POSTs to `/api/skills/create` with only `{name,description}` (no `steps`/`tools`/`category`),
|
||||
while the backend requires a non-empty `steps[]` (`skills.ts:434`). So the one adapter method that
|
||||
targets the create route is under-specified and **has no caller** in `apps/web/src` (grep:
|
||||
`createSkill` appears only at its definition). Needs rework to the full template shape.
|
||||
- `apps/web/src/lib/types.ts:210` — `SkillPack` (`id/name/description/category/skills[]/installed/trust`).
|
||||
This is a **pack/catalog** shape, NOT a skill-authoring shape (no instructions/inputs/outputs/tools).
|
||||
- `packages/agent/src/skill-creator.ts:11` — `SkillTemplate` (`name, description, triggerPatterns[],
|
||||
steps[], tools[], category`) + `generateSkillMarkdown()` (`:26`). This is the canonical authored-skill
|
||||
model the backend writes from.
|
||||
- `packages/agent/src/skill-frontmatter.ts:53` — `ParsedSkill`/`SkillFrontmatter` (name, description,
|
||||
`scope` personal→workspace→team→enterprise `:27`, `permissions` block `:40-48`). This is the read/parse
|
||||
side; the Builder's "memory access / permissions" + "scope" map onto this.
|
||||
|
||||
Conclusion: backend authoring contract is solid and reusable; the **frontend Builder UI is greenfield**,
|
||||
and the **adapter method must be widened** to the full template.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Create (new):**
|
||||
- `apps/web/src/components/os/overlays/SkillBuilder.tsx` — a 5-step stepper modal/drawer. Place it in
|
||||
`overlays/` (consistent with `CreateWorkspaceDialog.tsx`, `SpawnAgentDialog.tsx`). Reuse the DS
|
||||
Builder-stepper pattern (PRD §19.1 lists "Builder stepper" as a core component — none exists yet, so
|
||||
this is the **first stepper**; AgentBuilder S18 + AutomationBuilder S20 should share it — see §7).
|
||||
- Step 1 Basic Info: `name`, `description`, `category` (select from the `SKILL_FAMILIES` family set
|
||||
in `skills.ts:13-31`: writing/research/decision/planning/communication/code/creative — surface
|
||||
these as the category options, not the narrower `SkillPack.category` 5-union).
|
||||
- Step 2 Instructions: ordered `steps[]` editor (add/remove/reorder) + optional `triggerPatterns[]`
|
||||
(maps directly to `SkillTemplate.steps`/`.triggerPatterns`).
|
||||
- Step 3 Inputs & Outputs: `inputs[]` / `outputs[]` param rows. **Backend does not persist these
|
||||
structured today** (§5) — render them into the markdown body for v1, or block on the §5 backend
|
||||
extension.
|
||||
- Step 4 Tools & Data: `tools[]` multiselect (source the tool catalog already enumerated in
|
||||
`CapabilitiesApp.tsx:422-434` — extract that hardcoded list to a shared module to avoid a third
|
||||
copy; the agent tool registry is `packages/agent/src/tools.ts`) + **memory access / scope**
|
||||
(`SkillScope` personal/workspace/team — `skill-frontmatter.ts:27`) + a `permissions` toggle group
|
||||
(`skill-frontmatter.ts:40-48`).
|
||||
- Step 5 Review & Create: summary card + **Test** button (calls existing test route) + Create.
|
||||
- Optional `apps/web/src/components/os/overlays/skill-builder/` substeps if the file exceeds ~400 LOC
|
||||
(CLAUDE.md file-org rule).
|
||||
|
||||
**Rework:**
|
||||
- `apps/web/src/lib/adapter.ts` — widen `createSkill` (`:652`) to
|
||||
`createSkill(t: { name; description; steps: string[]; tools?: string[]; category?: string;
|
||||
triggerPatterns?: string[] })` (full `SkillTemplate` shape, matching `skills.ts:424-430`). Add
|
||||
`updateSkill(name, content)` → `PUT /api/skills/:name` (`skills.ts:506`) for edit, and reuse the
|
||||
existing test call (CapabilitiesApp inlines `fetch('/api/skills/test')` `:91` — promote it to an
|
||||
`adapter.testSkill(skillName, testInput?)` method so the Builder and CapabilitiesApp share one path).
|
||||
- `apps/web/src/components/os/apps/CapabilitiesApp.tsx` — add a "+ Create Skill" button (header,
|
||||
next to the grid/list toggle `:340`) that dispatches `waggle:open-app` or opens the Builder overlay
|
||||
via `useOverlayState` (the overlay open/close registry — `hooks/useOverlayState.ts`, per frontend
|
||||
inventory §c). Optionally render an edit affordance on installed custom skills.
|
||||
|
||||
**Wiring:**
|
||||
- Register the overlay in `Desktop.tsx` (overlays block) + add a flag to `useOverlayState`.
|
||||
- Ctrl+K "Create → Skill" command should open it (PRD §12.3 Create category; `GlobalSearch.tsx`
|
||||
already loads skills `:129`).
|
||||
|
||||
**Props/state:** local stepper state object `{ step, name, description, category, steps[],
|
||||
triggerPatterns[], inputs[], outputs[], tools[], scope, permissions, testResult, status }`. On
|
||||
Create → `adapter.createSkill(template)`; on Test → `adapter.testSkill(name)`. No new global store;
|
||||
optimistic insert into the Skills Hub list on success (CapabilitiesApp already refetches via
|
||||
`getSkills`).
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.8)
|
||||
|
||||
| PRD §16.8 endpoint | Status | Note / what to EXTEND vs NET-NEW | Substrate |
|
||||
|---|---|---|---|
|
||||
| `GET /api/skills` | **EXISTS** | `skills.ts:164` — list installed skills. Hub/Builder list source. | `~/.waggle/skills/*.md` files |
|
||||
| `POST /api/skills` | **EXISTS** | `skills.ts:167` raw `{name,content}`; **`POST /api/skills/create` (`skills.ts:431`) is the structured one** the Builder should use (`{name,description,steps[],tools?,category?}` → `generateSkillMarkdown` + `redactSkillContent` + audit + hash). | skill files + `auditStore` + `skillHashStore` |
|
||||
| `PATCH /api/skills/:id` | **PARTIAL** | Update is `PUT /api/skills/:name` keyed by **name**, method **PUT**, body `{content}` (`skills.ts:506-510`). PRD uses `PATCH`+`:id`. **EXTEND**: accept `PATCH` alias + name↔id mapping (skills have no numeric id — name *is* the id). | skill files |
|
||||
| `POST /api/skills/:id/test` | **PARTIAL** | Test exists as **`POST /api/skills/test`** (body `{skillName, testInput?}`, `skills.ts:570-575`) — not a per-id path. **EXTEND**: add `:id`-path variant (or keep body-driven; Builder can call either). Returns injected-prompt preview + metadata. | skill files + starter dir |
|
||||
| `POST /api/skills/:id/install` | **PARTIAL** | No unified per-skill install-by-id. Closest: `POST /api/skills/starter-pack/:id` (`skills.ts:161`), `POST /api/skills/capability-packs/:id` (`:163`), marketplace `POST /api/marketplace/install` (`marketplace.ts`, **Tier: PRO**, SecurityGate). **EXTEND**: add a dispatcher `/skills/:id/install` that routes by source — OR leave install to the Hub (S06) and have the *Builder* only create (create == installed, since authored skills write straight to `~/.waggle/skills/`). | skill files / marketplace |
|
||||
|
||||
**Net-new backend gap surfaced by the PRD stepper (NOT in §16.8, but needed for §12.6 Step 3):**
|
||||
the structured-create contract (`SkillTemplate`, `skill-creator.ts:11`) has **no `inputs`/`outputs`
|
||||
and no `memoryScopes` fields**. PRD §15 skill object (and Blueprint §551 "Skill … inputs, outputs,
|
||||
requiredTools, requiredMemoryScopes") expects them. Two options, both in-place:
|
||||
- **v1 (no migration, recommended):** Builder serialises inputs/outputs/memory-scope into the
|
||||
markdown body via an extended `generateSkillMarkdown` (add optional `inputs?`/`outputs?`/`scope?` to
|
||||
`SkillTemplate`); they round-trip as `## Inputs` / `## Outputs` / frontmatter `scope:` sections.
|
||||
`parseSkillFrontmatter` already reads `scope` (`skill-frontmatter.ts:27`) and `permissions`.
|
||||
- **v2 (later):** promote to structured frontmatter if inputs/outputs become a query/filter axis.
|
||||
|
||||
**No `.mind` migration.** Skills are **filesystem markdown** (`~/.waggle/skills/*.md`), not SQLite —
|
||||
confirmed `skills.ts:46-49`. The only DB touch is the **install-audit** record on create
|
||||
(`skills.ts:476`, writes to `.mind` install_audit) + the skill-hash store. The audit `record()` call
|
||||
already passes `riskLevel:'low'` so it sidesteps the latent `risk_level` CHECK drift noted in
|
||||
substrate-types §(d) — no new exposure.
|
||||
|
||||
**Security:** create/update already pass content through `redactSkillContent()` (strips
|
||||
secrets+user-paths, `skills.ts:464,:521`) and guard path-traversal in the name (`:447`). The Builder
|
||||
must NOT bypass these — always go through `/api/skills/create` / `PUT /api/skills/:name`, never write
|
||||
files another way. Elevated `permissions` (codeExecution/secrets/network) in a skill should trigger
|
||||
the §2 "approval required" UX (PRD §12.6 / §17.3 elevated-action approval) — currently no approval
|
||||
gate fires on skill authoring; flag as an open item (§9).
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
PRD §15.2 declares `ExtensionType = 'skill' | ...` — **MISSING** in `apps/web/src/lib/types.ts`
|
||||
(substrate-types §e). For S19 specifically:
|
||||
|
||||
- **Add `SkillTemplate` (authoring) FE type** mirroring `packages/agent/src/skill-creator.ts:11`
|
||||
(currently the FE only has the catalog-shaped `SkillPack` `types.ts:210`). Fields:
|
||||
`name, description, category, steps[], triggerPatterns[], tools[]` + PRD additions
|
||||
`inputs[]`, `outputs[]`, `scope` (`SkillScope`), `permissions`. Keep `SkillPack` (catalog) distinct
|
||||
from `SkillTemplate` (authoring) — they are different objects.
|
||||
- **Reuse `SkillScope`** (`personal|workspace|team|enterprise`) from `skill-frontmatter.ts:27` rather
|
||||
than redeclaring (export it through the FE if needed; PRD §15.2 `Scope` is the broader
|
||||
personal/workspace/team/organization — reconcile: skills use the `enterprise` variant).
|
||||
- `SkillPack.category` is a narrow 5-union (`types.ts:213`) that does **not** match the backend
|
||||
`SKILL_FAMILIES` 7-set (`skills.ts:13`). Widen the FE category union to the family set so the Builder
|
||||
category picker and the Hub badges agree.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **Phase/Sprint:** PRD Sprint 6 / Phase 3 — Intelligence layer (`PRD:344-351`, `:236-240`).
|
||||
- **Hard prereq — DS Builder Stepper component** (PRD §19.1). None exists. S19 is the cheapest place
|
||||
to introduce it, but **S18 Agent Builder** and **S20 Automation Builder** share the exact same
|
||||
stepper pattern — build the stepper as a reusable primitive (`components/ui/` or
|
||||
`components/os/overlays/builder-stepper/`) so all three Builders consume it.
|
||||
- **Sibling — S06 Skills Hub** (the browse/library half, hosted today by `CapabilitiesApp.tsx`).
|
||||
S19 launches *from* the Hub and writes *into* it (created skill → Installed list). They should ship
|
||||
together or S06 first.
|
||||
- **Soft — Command Center (Ctrl+K) (S03)** for the "Create → Skill" entry (PRD §12.3); not blocking.
|
||||
- **Soft — Agent Builder (S18) / Automation Builder (S20)** consume skills via `skillIds`
|
||||
(PRD §15.5) — they depend on skills *existing*, not on the Builder UI. Skill→agent assignment is a
|
||||
downstream wiring concern.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **M**
|
||||
|
||||
Backend is mostly **EXISTS/PARTIAL** (structured-create, test, update all present — only thin aliases
|
||||
`PATCH`/`:id`-path + an optional `inputs/outputs/scope` extension to `generateSkillMarkdown`, no DB
|
||||
migration). The frontend is a net-new 5-step stepper + adapter widening + a reusable stepper primitive
|
||||
shared with S18/S20 — real but bounded UI work. Not S (greenfield component + 3 endpoint extensions);
|
||||
not L (no new substrate, no migration, backend authoring path already proven via the `create_skill`
|
||||
tool).
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Install vs create semantics.** Authored skills write straight to `~/.waggle/skills/` (create ==
|
||||
installed). Does PRD `POST /api/skills/:id/install` (§16.8) apply to the *Builder* at all, or only
|
||||
to the *Hub/Marketplace* install flow? Recommend: Builder = create-only; install-by-id lives in S06.
|
||||
2. **Inputs/Outputs persistence.** v1 markdown-body serialisation vs structured frontmatter — confirm
|
||||
whether inputs/outputs need to be *queryable* (drives the v1-vs-v2 backend choice in §5).
|
||||
3. **Approval gate on elevated skills.** Should authoring a skill that declares
|
||||
`permissions: {codeExecution|secrets|network}` trigger the PRD §17.3 approval prompt at *create*
|
||||
time, or only at *run* time? No gate fires today on authoring.
|
||||
4. **Scope vocabulary reconciliation.** `SkillScope` uses `enterprise`; PRD §15.2 `Scope` uses
|
||||
`organization`. Pick one for the Builder's publish-to-scope picker.
|
||||
5. **Test-run fidelity.** `POST /api/skills/test` returns only the *injected-prompt preview* + parsed
|
||||
metadata (`skills.ts:570`), it does **not** execute the skill against an LLM. Is preview-only
|
||||
sufficient for the PRD "test pass/fail" state, or is a live dry-run expected? (Live run would be
|
||||
net-new and lean on `/api/agent/run`.)
|
||||
6. **Category source of truth.** Reconcile `SkillPack.category` (5-union) vs `SKILL_FAMILIES` (7-set)
|
||||
vs `SkillTemplate.category` (free string) — which is canonical for the picker?
|
||||
224
docs/ux-refactor/gap-cards/S20-automation-builder.md
Normal file
224
docs/ux-refactor/gap-cards/S20-automation-builder.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# Gap Card — S20 Automation Builder
|
||||
|
||||
> UX-refactor planning artifact. Execution model: **in-place incremental refactor** of
|
||||
> `apps/web` + targeted backend extensions. Mockups are directional (PRD §24); PRD acceptance
|
||||
> criteria win. Every claim is grounded in a real file path below.
|
||||
>
|
||||
> Sources: PRD §12.10 + §16.10 (`docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md`),
|
||||
> Blueprint screen 20 (`_blueprint_extracted.txt:423-429`), mockup
|
||||
> `Waggle_OS_Handoff_Assets/screens_18_21_builders_and_marketplace.png`, baseline inventories under
|
||||
> `docs/ux-refactor/_inventory/`, backend-map `docs/backend-map/sections/03f-api-realtime-ops.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
A 4-step **stepper** builder that lets a user create a scheduled or event-driven workflow
|
||||
("automation"). PRD §12.10 / Blueprint screen 20: *"Create scheduled or event-driven workflow.
|
||||
Choose trigger/condition/actions/agent/notification, test, activate."*
|
||||
|
||||
PRD §12.10 fields (line 605): `name, trigger, condition, actions, agent, notification, schedule,
|
||||
workspace, status`. Builder steps (PRD line 604): **Trigger → Condition → Actions → Review &
|
||||
Activate**. This is the *create/edit* surface; the *list/run/pause/logs* surface is the sibling
|
||||
**Automation Center** (screen 11, PRD §12.10 tabs Overview/Running/Scheduled/Triggers/History/Logs)
|
||||
— scoped in its own card, but they share the same backend (cron) and types.
|
||||
|
||||
Mockup (directional, panel labelled "20. Automation Builder", top-right of
|
||||
`screens_18_21_builders_and_marketplace.png`): left rail = step list (Trigger/Condition/Actions/
|
||||
Review); center = config form with a **Trigger** block (schedule vs event), a **Condition** block,
|
||||
an **Actions** list, an **Agent** selector and a **Notification** target; footer = Test +
|
||||
Activate buttons.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD/Blueprint)
|
||||
|
||||
PRD §14.6 automation lifecycle states: `Draft, Scheduled, Running, Success, Failed, Paused,
|
||||
Awaiting approval, Disabled`. Blueprint screen-20 states (`_blueprint_extracted.txt:428-429`):
|
||||
**Draft; test fail; active; scheduled; approval needed.**
|
||||
|
||||
Builder-screen states (the create flow itself), composed with PRD §14.1 global states:
|
||||
- **Draft / editing** — stepper in progress, per-step validation (e.g. invalid cron expr, no action chosen).
|
||||
- **Test running / test pass / test fail** — dry-run a draft before activating (PRD line 604 "test").
|
||||
- **Activating → Scheduled/Active** — on save the automation appears in Automation Center + Home overnight summary (PRD line 607, 611).
|
||||
- **Approval required** — an automation whose actions touch an elevated/risky capability must surface an approval prompt (PRD §17.3 "Automations can only run actions allowed by the user/team role"; PRD line 607 "risky automations surface in Home").
|
||||
- **Loading / Error / Offline / Permission-denied** — global states (PRD §14.1). Offline: degrade gracefully, keep draft local.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `rework`** (promote the existing cron surface into the Trigger/Actions skeleton;
|
||||
the Condition step + event-triggers + test/approval are net-new but build on cron — no new store).
|
||||
|
||||
Current implementation is the **cron** surface, not an "Automation Builder":
|
||||
|
||||
| Layer | File | What it does today |
|
||||
|---|---|---|
|
||||
| Frontend app | `apps/web/src/components/os/apps/ScheduledJobsApp.tsx` | Single-screen "Scheduled Jobs" manager (NOT a stepper). Inline create form with 4 fields: name, **jobType** (`<select>` over `CRON_JOB_TYPES`), **schedule** (preset `<select>` + custom cron), **outputChannel** (`log`/`telegram`). List rows: enable/disable toggle, run-now (`Play`), delete. `:55` create, `:93` toggle, `:102` delete, `:112` trigger. No condition, no agent picker, no test, no review step. |
|
||||
| Form helpers | `apps/web/src/lib/cron-presets.ts` | `CRON_SCHEDULE_PRESETS` (6 cadences), `CRON_JOB_TYPES` (6 types), `describeCronExpr` / `isPlausibleCronExpr` cron→English. Reusable as-is for the Trigger step. |
|
||||
| Adapter | `apps/web/src/lib/adapter.ts:815-845` | `getCronJobs`, `createCronJob`, `updateCronJob`, `deleteCronJob`, `triggerCronJob` → `/api/cron*`. |
|
||||
| FE type | `apps/web/src/lib/types.ts:230-238` | `CronJob { id, name, schedule, workspaceId, enabled, lastRun?, nextRun? }` — **lossy**: drops `jobType`, `jobConfig`, `createdAt`; renames `cronExpr`→`schedule`, `nextRunAt`→`nextRun`. No `trigger`/`condition`/`actions`/`agent`/`notification`/`status` (the PRD §15/§12.10 automation shape). |
|
||||
| Route | `packages/server/src/local/routes/cron.ts` | Full CRUD + `POST /api/cron/:id/trigger` (auto-enables on run, `:188`). |
|
||||
| Store | `packages/core/src/cron-store.ts` | `CronStore` over `.mind`: `cron_schedules` (`:71`), `cron_execution_history` (`:88`), `notifications` (`:104`). Schedule row: `name, cron_expr, job_type, job_config, workspace_id, enabled, last_run_at, next_run_at`. **No trigger-type, condition, or actions columns** — `job_config` is a free JSON blob. |
|
||||
| Scheduler | `packages/server/src/local/cron.ts` | `LocalScheduler` — 60 s tick, `getDue()` poll, `executeJob()` for manual trigger, auto-disable after 5 consecutive failures (`MAX_CONSECUTIVE_FAILURES`), `onJobComplete` notify callback. **Time-driven only.** |
|
||||
| Executor (the real "actions") | `packages/server/src/local/index.ts:1379-1751` | `new LocalScheduler(cronStore, async (schedule) => switch(schedule.job_type){…})`. Cases: `memory_consolidation` (`:1381`, incl. `action:'index_reconcile'` marketplace sync), `workspace_health` (`:1504`), `proactive` (`:1525`, sub-actions morning_briefing/stale_workspace_check/task_reminder/capability_suggestion `:1561-1582`), `prompt_optimization` (`:1590`), `agent_task` (`:1722`, runs a `jobConfig.prompt` in a workspace), `monthly_assessment`. |
|
||||
| History route | registered in `notifications.ts` | `GET /api/cron/:id/history` (backend-map `03f:147`) → execution rows. |
|
||||
|
||||
**Grep confirmation:** no `Automation`/`AutomationBuilder` component, no event-trigger code, no
|
||||
`condition` field. The 13 frontend hits for "Automation|trigger|condition" are the word "trigger"
|
||||
used in unrelated contexts (`triggerCronJob`, MCP triggers, marketplace) — confirmed none is an
|
||||
automation builder. (`ScheduledJobsApp.tsx` is the only real match.)
|
||||
|
||||
**Mental model match:** what the user calls "trigger" today is **only a cron schedule** (time).
|
||||
What the user calls "actions" today is **a single `jobType`** chosen from a fixed catalog (one
|
||||
action per job). There is **no condition, no multi-action sequence, no per-automation agent
|
||||
binding (except via `agent_task` prompt), and no event triggers.**
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**New components (`apps/web/src/components/os/apps/automations/`):**
|
||||
|
||||
- `AutomationBuilder.tsx` — 4-step stepper (reuse the **Builder stepper** DS pattern from Agent/Skill
|
||||
builders — PRD §19.1; align with `CreateAgentForm.tsx` in `apps/web/src/components/os/apps/agents/`).
|
||||
Owns draft state `{ name, trigger, condition, actions[], agentId?, notification, schedule,
|
||||
workspaceId, status:'draft' }`; submits via adapter (see §5).
|
||||
- `TriggerStep.tsx` — radio: **Schedule** (reuse `cron-presets.ts` preset/custom `<select>` +
|
||||
`describeCronExpr` preview — lift the schedule block straight out of `ScheduledJobsApp.tsx:195-223`)
|
||||
vs **Event** (gated/"coming soon" if event triggers aren't built — see §5/§9). Workspace scope picker
|
||||
(reuse `useWorkspaces`).
|
||||
- `ConditionStep.tsx` — optional predicate (e.g. "only if N+ new frames", "only on weekdays"). **Net-new
|
||||
concept** — no substrate; ship a minimal optional condition stored in `job_config.condition` (no DB
|
||||
migration; see §5). Keep simple — PRD §3.2/§19.2 ("not forced during onboarding").
|
||||
- `ActionsStep.tsx` — choose one or more actions. v1 maps to the existing `CRON_JOB_TYPES` catalog
|
||||
(the only executable actions today, `index.ts` switch). For `agent_task`, surface an **Agent**
|
||||
selector (reuse `useWorkspaces` + persona/agent picker from `agents/`) and a prompt field; for
|
||||
`proactive`, surface the sub-action choice; plus a **Notification** target (reuse the
|
||||
`log`/`telegram` `outputChannel` selector already in `ScheduledJobsApp.tsx:225-242`).
|
||||
- `ReviewStep.tsx` — summary + **Test** button (dry-run, see §5) + **Activate** button.
|
||||
- `AutomationCenter.tsx` (sibling, may be its own card) — promote `ScheduledJobsApp.tsx` into the
|
||||
PRD §12.10 tabbed Center (Overview/Running/Scheduled/Triggers/History/Logs), wiring `GET
|
||||
/api/cron/:id/history` for the Logs tab.
|
||||
|
||||
**Reuse targets:** `cron-presets.ts` (whole module), the schedule + output-channel JSX blocks in
|
||||
`ScheduledJobsApp.tsx`, the stepper chrome from `agents/CreateAgentForm.tsx`, `useToast`,
|
||||
`useWorkspaces`, `ui/select`/`ui/input`/`ui/dialog`.
|
||||
|
||||
**Adapter methods/hooks:** extend `lib/adapter.ts` cron methods to round-trip `jobType` +
|
||||
`jobConfig` (currently `createCronJob` already accepts `jobConfig` — `:821`; the lossy FE `CronJob`
|
||||
type is what drops it on read). Add `testAutomation(draft)` and (if added) `pauseCronJob(id)`,
|
||||
`getCronHistory(id)`. New `useAutomations()` hook wrapping these (mirrors existing domain-hook
|
||||
pattern). **Register the new app** in `Desktop.tsx` `appConfig` + `renderAppContent` switch and
|
||||
the dock (`lib/dock-tiers.ts`), under the **Intelligence** IA bucket (PRD §10.3) — the existing
|
||||
`scheduled-jobs` AppId can be renamed/aliased to `automations`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.10)
|
||||
|
||||
> **Substrate verdict (matches the backend-routes inventory):** all six §16.10 endpoints are
|
||||
> **PARTIAL** — the cron surface (`/api/cron*`) already provides full CRUD + trigger + history. The
|
||||
> refactor **aliases/extends cron as "automations"**; no new store. `trigger`/`condition`/`actions`
|
||||
> ride in the existing `job_config` JSON blob → **no `.mind` migration** for v1.
|
||||
|
||||
| PRD §16.10 endpoint | Status | Extend (existing) vs net-new + substrate it touches |
|
||||
|---|---|---|
|
||||
| `GET /api/automations` | **PARTIAL** | **Extend** `GET /api/cron` (`cron.ts:94`). Alias path; reshape `toResponse` to expose `trigger/condition/actions/status` derived from `job_type` + `job_config`. Substrate: `cron_schedules`. |
|
||||
| `POST /api/automations` | **PARTIAL** | **Extend** `POST /api/cron` (`cron.ts:67`). Accept the richer automation body; persist `trigger`/`condition`/`actions` into `job_config` (and `cron_expr` for schedule triggers). Substrate: `cron_schedules`. |
|
||||
| `PATCH /api/automations/:id` | **PARTIAL** | **Extend** `PATCH /api/cron/:id` (`cron.ts:124`). Substrate: `cron_schedules`. |
|
||||
| `POST /api/automations/:id/run` | **PARTIAL** | **Extend** `POST /api/cron/:id/trigger` (`cron.ts:174`, runs `scheduler.executeJob`, auto-enables). Substrate: `cron_schedules` + executor switch (`index.ts:1379`). |
|
||||
| `POST /api/automations/:id/pause` | **PARTIAL** | No `/pause` route. **Net-new thin route OR reuse** `PATCH /api/cron/:id {enabled:false}`. Add `/pause` alias for the PRD contract. Substrate: `cron_schedules.enabled` + scheduler `resetFailure`. |
|
||||
| `GET /api/automations/:id/logs` | **PARTIAL** | **Extend/alias** `GET /api/cron/:id/history` (in `notifications.ts`; backend-map `03f:147`). Substrate: `cron_execution_history` (`cron-store.ts:88`). |
|
||||
|
||||
**Net-new beyond §16.10 (PRD requires, backend lacks):**
|
||||
|
||||
1. **Test / dry-run** (PRD line 604 "test"). No route today. **Net-new** `POST /api/automations/test`
|
||||
that runs `scheduler.executeJob` against an unsaved draft (or a draft saved `enabled:false`) and
|
||||
returns the result without scheduling. Touches the executor switch (`index.ts:1379-1751`) — must
|
||||
make it callable with an ad-hoc schedule object, not only persisted rows.
|
||||
2. **Condition evaluation** (PRD step 2). Net-new logic in the executor: before running actions, read
|
||||
`job_config.condition` and short-circuit. No new table — `condition` is a JSON sub-field of
|
||||
`job_config`. Keep predicate vocabulary minimal in v1.
|
||||
3. **Event triggers** (PRD "event-driven", §14.6 "trigger fired", Blueprint "scheduled or
|
||||
event-driven"). **Major net-new substrate gap** — `LocalScheduler` is **time-poll only**
|
||||
(`cron.ts` `getDue()` on `next_run_at`). True event triggers need an event→automation dispatch
|
||||
path (could hang off `server.eventBus` audit/notification stream — `03f:321-347`). **Recommend
|
||||
deferring event triggers to a later phase**; ship schedule-triggers only in v1 and gate the
|
||||
"Event" option (see §9, §8).
|
||||
4. **Approval gate for risky actions** (PRD §17.3, line 607). When an automation's actions touch an
|
||||
elevated capability, route through the existing approval/install-audit machinery
|
||||
(`/api/approval/*`, `install-audit.ts`). Wiring net-new; substrate exists.
|
||||
|
||||
**`.mind` migration flag:** **None required for v1** (trigger/condition/actions live in the existing
|
||||
`job_config TEXT` blob — `cron-store.ts:77`). A later migration could promote `trigger_type` /
|
||||
`status` to real columns (the migration runner already does idempotent additive `ADD COLUMN` on
|
||||
`.mind` tables — see substrate-types `§(c)` precedent) **only if** they become query/filter axes for
|
||||
Automation Center tabs (Running/Scheduled/Triggers).
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
PRD §15.2 unions `AutonomyLevel` / `AgentType` are **MISSING** from `apps/web/src/lib/types.ts`
|
||||
(per substrate-types `§(e)`). Blueprint §17 names an **Automation** entity (`_blueprint_extracted.txt:553`):
|
||||
`id, scope, trigger, condition, actions, agentId, schedule, notificationTargets, status, lastRunAt,
|
||||
nextRunAt`. None of this exists in FE types today.
|
||||
|
||||
- **Add `Automation` interface** to `lib/types.ts` (Blueprint shape above) replacing the lossy
|
||||
`CronJob` (`types.ts:230`) — or layer it as a richer superset and have the adapter map cron rows
|
||||
into it. Add `AutomationTriggerType = 'schedule' | 'event'`, `AutomationStatus = 'draft' |
|
||||
'scheduled' | 'active' | 'running' | 'paused' | 'failed' | 'awaiting_approval' | 'disabled'`
|
||||
(PRD §14.6), and an `AutomationAction` discriminated union keyed on the existing `CronJobType`
|
||||
catalog (`cron-presets.ts:15`).
|
||||
- Reconcile FE↔BE: the FE `CronJob` already mismatches the `/api/cron` response shape
|
||||
(`cronExpr`→`schedule`, drops `jobType`/`jobConfig`) — fix this drift as part of the type work.
|
||||
- Keep the canonical `CronJobType` union single-sourced (currently duplicated in
|
||||
`cron-presets.ts:15` AND `packages/core/src/cron-store.ts:15` — flag the dup; do not add a third).
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens/phases first)
|
||||
|
||||
- **PRD Phase 3 (Intelligence layer)** — same phase as Agent/Skill builders; reuse their stepper.
|
||||
- **Agent Builder (S18/agents)** must define the agent picker the `ActionsStep` agent selector binds
|
||||
to (PRD §12.9). Soft dependency — can fall back to persona/`useWorkspaces` until Agent entity lands.
|
||||
- **Automation Center (screen 11)** is the list/run/logs sibling — share types + adapter; build the
|
||||
Center's Logs tab on the same `/api/cron/:id/history`.
|
||||
- **Home Cockpit (S?)** — overnight summary + "attention required" must surface failed/risky
|
||||
automations (PRD line 607, 611). Soft consumer dependency (Home reads automation status).
|
||||
- **Approval surface (`ApprovalsApp` / `/api/approval/*`)** — for the risky-action gate (§5.4).
|
||||
- **DS:** Builder stepper + Approval prompt primitives (PRD §19.1) shared across all builders.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort: **L**
|
||||
|
||||
Rework + extend is cheap (the cron store/route/executor + `cron-presets` + output-channel UI all
|
||||
exist and are reusable), but the gap between "cron job manager" and the PRD's
|
||||
**Trigger/Condition/Actions/Agent/Notification stepper + Test + approval-gated activate** is wide:
|
||||
net-new Condition step, dry-run test endpoint, FE type reconciliation, and an Agent binding. The
|
||||
**Event-trigger half is XL on its own** (no event-dispatch substrate) and is recommended **out of
|
||||
v1 scope** — if it stays in, this becomes XL.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **Event triggers in v1 or schedule-only?** `LocalScheduler` is time-poll only; true event triggers
|
||||
need a new event→automation dispatch path off `server.eventBus`. Recommend defer + gate the "Event"
|
||||
radio option. (Ties to PRD §23 open-question spirit on scope.)
|
||||
2. **Condition vocabulary** — what predicates does v1 support? (frame-count threshold, day-of-week,
|
||||
workspace-state flag?) PRD names "condition" but specifies no predicate set.
|
||||
3. **One action or multiple per automation?** Today one `job_type` == one action. PRD says "actions"
|
||||
(plural). v1 = single action mapped to `CronJobType`, or true multi-action sequence (needs executor
|
||||
rework + ordering semantics in `job_config`)?
|
||||
4. **Does `pause` get a dedicated route or reuse `PATCH …{enabled:false}`?** Affects the §16.10
|
||||
contract surface and Automation Center.
|
||||
5. **Which `.mind`** do automations live in — personal vs per-workspace? `cron_schedules.workspace_id`
|
||||
is nullable + `'global'→'*'` normalized (`cron.ts:74`); confirm scope model for the §16.10 list.
|
||||
6. **Risky-action approval class** — which actions are "elevated" (e.g. `agent_task` writing files,
|
||||
marketplace `index_reconcile`)? Needs an approval-class mapping before the §5.4 gate can ship.
|
||||
7. **Naming/AppId** — rename `scheduled-jobs` AppId → `automations`, or keep both (alias)? Affects dock
|
||||
+ window-manager + the dead-id cleanup already flagged in the frontend inventory.
|
||||
137
docs/ux-refactor/gap-cards/S21-marketplace-extend.md
Normal file
137
docs/ux-refactor/gap-cards/S21-marketplace-extend.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Gap Card — S21 · Marketplace / Extend
|
||||
|
||||
> Execution model: **in-place incremental refactor** of `apps/web` + targeted backend extensions.
|
||||
> Mockups directional only (PRD §24). PRD acceptance criteria win over pixels.
|
||||
> Every claim grounded in real files (paths + lines below).
|
||||
|
||||
---
|
||||
|
||||
## 1. Screen & purpose
|
||||
|
||||
**Screen 21 — Marketplace / Extend Waggle** (PRD §12.13, lines 648-662; blueprint catalog row 21, `_blueprint_extracted.txt:432-439`).
|
||||
|
||||
The central, power-user extension surface. PRD §10.4 names the Extend layer as **Connectors, MCPs, Models, External tools, Marketplace** — S21 is the *Marketplace* node of that layer: a single place to discover, install, update, risk-approve, and audit **extensions of six kinds** (Skills, Agents, Connectors, MCPs, Models, Templates) without "hunting through settings" (PRD §12.13 acceptance criterion). It is the funnel for the PRO upgrade trigger (skills + connectors + marketplace are gated to PRO; `05g-subsystem-skills-marketplace-wiki.md:3`).
|
||||
|
||||
This card scopes the **Marketplace browse/install/audit** surface specifically. The sibling Extend surfaces — Connector Hub (S? / §12.7), MCP Hub (§12.8), Skills Hub (§12.6) — are separate screens that this one cross-links to; their per-domain CRUD lives in their own cards. S21 is the unified catalog + governance view across all extension types.
|
||||
|
||||
---
|
||||
|
||||
## 2. Required states (PRD / Blueprint)
|
||||
|
||||
**Functional requirements (PRD §12.13):**
|
||||
- Categories: **Skills, Agents, Connectors, MCPs, Models, Templates** (6 facets).
|
||||
- Search / filter / sort by **category, trust, popularity, source, risk**.
|
||||
- Show per-item lifecycle: **installed, update available, risk approval (required), install failed**.
|
||||
- Install actions **write to install audit**.
|
||||
- Support **workspace / team scoping** of installs.
|
||||
|
||||
**Extension lifecycle states (PRD §14.7, lines 906-914):**
|
||||
`Available · Installed · Update available · Installing · Failed install · Risk approval required · Disabled/revoked`.
|
||||
|
||||
**Acceptance criterion (PRD §12.13):** "Power users can extend Waggle without hunting through settings." Cross-cuts to §12.8 MCP acceptance ("powerful but always visible, scoped, auditable, and reversible") and the install-audit governance theme (`_blueprint_extracted.txt:43-45,95`: install audit is the "best basis for Extend governance").
|
||||
|
||||
**Journeys touching S21:** J8 install connector, J9 install MCP (audit recorded), J25 marketplace update → review changelog/risk (`_blueprint_extracted.txt:203,206,252`).
|
||||
|
||||
**Mockup (directional):** `screens_18_21_builders_and_marketplace.png` shows S21 as a card-grid catalog with a left category rail and per-card install affordances + trust badges — consistent with the spec; not a pixel target.
|
||||
|
||||
---
|
||||
|
||||
## 3. Current state in repo
|
||||
|
||||
**Disposition: `rework`** (the existing surfaces are reusable substrate but neither satisfies §12.13 — the catalog is hardwired to 3 of 6 categories and there is no unified Extend view with trust/risk/popularity/scope filters).
|
||||
|
||||
Marketplace is **doubly represented today** (frontend.md §f flags this as an IA cleanup point):
|
||||
|
||||
1. **`apps/web/src/components/os/apps/MarketplaceApp.tsx`** (258 lines, fully read) — standalone browser. Two tabs (`'search' | 'installed'`, `:26`). Calls `adapter.searchMarketplace` / `getMarketplaceInstalled` / `installMarketplacePackage` / `uninstallMarketplacePackage`. Renders a flat package list with a `scanBadge` (passed/failed Shield/AlertTriangle, `:141-145`), `installed` check, type/category/source chips (`:223-227`), and 403→`waggle:tier-insufficient` UpgradeModal routing (`:108-117`). **Registered in `Desktop.tsx`** appConfig `marketplace` (`Desktop.tsx:93`) + `renderAppContent` case (`:347`), but **NO dock entry points at it** — `dock-tiers.ts:67` comment: "Marketplace already a tab inside Skills & Apps — no separate dock entry". So `MarketplaceApp` is currently reachable only via `waggle:open-app` events, effectively orphaned.
|
||||
|
||||
2. **`apps/web/src/components/os/apps/CapabilitiesApp.tsx`** (478 lines, fully read) — "Skills & Apps", the live Extend surface (dock key `skills` → appId `capabilities`, `dock-tiers.ts:64`). 5 tabs: `installed | starter | marketplace | tools | audit` (`:80,358`). The `marketplace` tab lists `SkillPack`s via `getMarketplacePacks`; the `audit` tab (`AuditTab`, `:15-58`) reads `GET /api/audit/installs` and renders name/source/outcome/timestamp rows. It has a pack-detail drawer (`PackDetail`, `:249-334`) with trust label + bundled-skills + install. This is *pack-oriented* (curated bundles), not the faceted *package catalog* of §12.13.
|
||||
|
||||
**Backend already present (more than the inventory claimed):**
|
||||
- Full marketplace route family: `packages/server/src/local/routes/marketplace.ts` — `search` (FTS5 + faceted, `:56`), `packs`, `packs/:slug`, `enterprise-packs` (ENTERPRISE), `install` (PRO + SecurityGate, `:180`), `uninstall` (`:377`), `installed` (`:396`), `security-check`, `sources` GET/POST/DELETE (`:446,457,533`), `categories` (`:567` → `PACKAGE_CATEGORIES`), `sync`, `security-status`, `publish` (PRO, `:685`). Sort facet supported via `?sort=` (`:72`).
|
||||
- **Install-audit READ route EXISTS** — `GET /api/audit/installs` at `packages/server/src/local/routes/skills.ts:685-703` (returns normalized `{ entries: [{ capabilityName, capabilityType, source, riskLevel, trustSource, approvalClass, action, initiator, detail }] }`). **This corrects substrate-types.md §d gap #1, which claimed "No HTTP endpoint surfaces the audit trail."** The write path is live too (`marketplace.ts:224-319` records every SecurityGate verdict; `skills.ts:212,315,475`). `adapter.getAuditInstalls` already wires it (frontend.md, adapter `Misc`).
|
||||
- Install-audit store: `packages/core/src/install-audit.ts` — `AuditCapabilityType = native|skill|plugin|mcp|connector|marketplace` (`:22`), full enums for action/trust/approval/risk; read API `getRecent/getByCapability/getByAction/getAll`.
|
||||
|
||||
**Critical limitation (grounded):** the marketplace catalog **only supports 3 install kinds** — `InstallationType = 'skill' | 'plugin' | 'mcp'` (`packages/marketplace/src/types.ts:185`), confirmed by `05g-subsystem-skills-marketplace-wiki.md:11` ("three installable package kinds — `skill`, `plugin`, `mcp`"). PRD §12.13 demands **six categories incl. Agents, Connectors, Models, Templates**, which have **no catalog representation today** (connectors are a separate `/api/connectors` registry with only `connect`/`disconnect`; templates are `/api/workspace-templates`; agents are personas/groups; models are `/api/litellm/models` + providers). So S21's "one catalog, six categories" is the central rework: a **federated catalog read** over the existing marketplace catalog + connector registry + template store + persona/agent catalog + model list, NOT a new package table.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frontend work
|
||||
|
||||
**Strategy:** Consolidate the two marketplace surfaces into ONE Extend/Marketplace screen, retire the orphaned standalone, and give the new screen a federated faceted catalog. Keep `CapabilitiesApp`'s Skills-pack flow as the Skills facet; promote a real dock entry.
|
||||
|
||||
**Components to create:**
|
||||
- `apps/web/src/components/os/apps/MarketplaceApp.tsx` — **rework in place** (reuse the file/appId so Desktop wiring + tests survive). Expand from 2 tabs to the §12.13 model: a left **category rail** (Skills / Agents / Connectors / MCPs / Models / Templates / All) + a top **filter bar** (trust, popularity, source, risk dropdowns + search) + a **catalog grid**. Reuse the existing `scanBadge`, 403→UpgradeModal routing (`:108-117`), and install/uninstall handlers verbatim.
|
||||
- `apps/web/src/components/os/apps/extend/ExtensionCard.tsx` (new) — one card type rendering the §14.7 lifecycle badge (Available/Installed/Update available/Installing/Failed/Risk-approval/Disabled), trust chip, risk chip, source, popularity. Props: `{ ext: Extension; onInstall; onUpdate; onRevoke; onOpenDetail }`.
|
||||
- `apps/web/src/components/os/apps/extend/ExtensionDetail.tsx` (new, or generalize `CapabilitiesApp`'s `PackDetail` `:249-334`) — detail drawer with permissions/scope, changelog (for "update available", J25), risk approval CTA, install-audit trail for this item (via `getAuditInstalls` filtered client-side, or a new `?capability=` query — see §5).
|
||||
- `apps/web/src/components/os/apps/extend/InstallAuditPanel.tsx` (new, or lift `CapabilitiesApp`'s `AuditTab` `:15-58`) — shared Extend governance trail; reused by S21 + Connector/MCP hubs.
|
||||
- `apps/web/src/lib/extension-catalog.ts` (new pure module + co-located `.test.ts`) — client-side **federation/normalization**: merge marketplace packages (`searchMarketplace`), connectors (`getConnectors`), templates (`getWorkspaceTemplates`), personas/groups (`getPersonas`/`getAgentGroups`), models (`getModels`/`getProviders`) into one `Extension[]` with a unified `{ type: ExtensionType, trust, risk, installed, updateAvailable, source, popularity }` shape; derive lifecycle state. Keeps the fat federation logic out of the component (file-org rule: many small files).
|
||||
|
||||
**Reuse targets:** `MarketplaceApp` install/uninstall + 403 handler; `CapabilitiesApp` `PackCard`/`PackDetail`/`AuditTab` + `lib/skill-pack-display.ts` (`describeTrust`, `summariseSkills`) + `lib/dedupe-packs.ts`; `components/ui/*` (card/badge/tabs/input/select/tooltip); `HintTooltip`.
|
||||
|
||||
**Adapter methods/hooks:** existing — `searchMarketplace`, `getMarketplacePacks`, `getMarketplaceInstalled`, `installMarketplacePackage`, `uninstallMarketplacePackage`, `getAuditInstalls`, `getConnectors`, `getWorkspaceTemplates`, `getPersonas`, `getModels`, `getProviders` (all in `lib/adapter.ts`, per frontend.md §c). **New adapter methods** to add for the §16.9 gaps: `getMcps`, `installMcp`, `testMcp`, `revokeMcp`, `syncConnector`, `revokeConnector` (see §5). New hook `hooks/useExtensions.ts` wrapping `extension-catalog.ts` (catalog + filters + install/update/revoke actions + react-query caching).
|
||||
|
||||
**IA cleanup (frontend.md §f):** decide one home for marketplace. Recommended: keep `MarketplaceApp` as the dedicated S21 Extend surface, add a real dock entry under the `extend` zone-parent (`dock-tiers.ts:62-71`), and have `CapabilitiesApp`'s `marketplace` tab deep-link into it (or drop that tab) to remove the double representation.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backend work (PRD §16.9 endpoint-by-endpoint)
|
||||
|
||||
| PRD §16.9 endpoint | Status | EXTEND vs NET-NEW + substrate / .mind |
|
||||
|---|---|---|
|
||||
| `GET /api/connectors` | **EXISTS** | `connectors.ts:6`. No change. |
|
||||
| `POST /api/connectors/:id/connect` | **EXISTS** | `connectors.ts:55`. No change. |
|
||||
| `POST /api/connectors/:id/sync` | **MISSING** | **NET-NEW** thin action in `connectors.ts` (re-init/re-fetch from connected service). Touches connector credential resolution (vault) + connector definition; record an audit entry (`auditStore.record`, type `connector`). No `.mind` migration. |
|
||||
| `POST /api/connectors/:id/revoke` | **PARTIAL** | **EXTEND** — alias to existing `POST /api/connectors/:id/disconnect` (`connectors.ts:107`, removes vault creds + sub-keys). Same intent, add `/revoke` route delegating to disconnect + write a `revoked` audit entry. |
|
||||
| `GET /api/mcps` | **PARTIAL** | **EXTEND/NET-NEW** — no `/api/mcps`. MCP catalog lives in `@waggle/shared mcp-catalog.ts`; installed MCPs surface inside `GET /api/capabilities/status` (`mcpServers[]`) and the marketplace catalog (`waggle_install_type:'mcp'`, `marketplace/src/mcp-registry.ts`). Add a dedicated `GET /api/mcps` route that joins catalog + installed-state + `.mcp.json`. Reads existing substrate; no migration. |
|
||||
| `POST /api/mcps/install` | **PARTIAL** | **EXTEND** — route through the existing marketplace installer (`POST /api/marketplace/install`, which already handles `installType:'mcp'` → writes `.mcp.json`, `installer.ts:580`). Add an MCP-specific endpoint that resolves an MCP id → marketplace package → install, OR keep marketplace install as the single path and have the FE call it. Audit already recorded. |
|
||||
| `POST /api/mcps/:id/test` | **MISSING** | **NET-NEW** — no MCP health/test route. Closest analog is `GET /api/connectors/:id/health` (`connectors.ts:16`). Add an MCP test route (spawn/handshake the MCP server, report ok/error). No `.mind`. |
|
||||
| `POST /api/mcps/:id/revoke` | **MISSING** | **NET-NEW** — no MCP revoke-by-id. Closest: `DELETE /api/plugins/:name` (`skills.ts`). Add an MCP revoke route (remove from `.mcp.json` + record `revoked` audit). |
|
||||
| `GET /api/marketplace` | **PARTIAL** | **EXTEND** — bare path = alias of `GET /api/marketplace/search` with default params (`marketplace.ts:56`). Add alias or accept both. |
|
||||
| `POST /api/marketplace/install` | **EXISTS** | `marketplace.ts:180` (PRO + SecurityGate). No change. |
|
||||
|
||||
**Plus implied additions for §12.13 (not in §16.9's list but required by FRs):**
|
||||
- **Six-category catalog.** The §12.13 categories (Agents/Models/Templates) have **no marketplace representation** (`InstallationType` is `skill|plugin|mcp` only, `types.ts:185`). Two options: (a) widen `InstallationType` + catalog schema to add `agent|model|template` (a marketplace-DB change in `packages/marketplace`, NOT a `.mind` migration — `marketplace.db` is a separate SQLite store) and seed registries; or (b) **federate at read time** (preferred for incremental scope) — the FE `extension-catalog.ts` merges marketplace (skill/plugin/mcp) + `/api/connectors` + `/api/workspace-templates` + `/api/personas`+`/api/agent-groups` + `/api/litellm/models`. Option (b) needs **no backend change** beyond §16.9; option (a) is a later phase if a true unified catalog with publish/install for all 6 kinds is wanted.
|
||||
- **Install-audit read for Extend governance.** **EXISTS** — `GET /api/audit/installs` (`skills.ts:685`). (Substrate inventory §d#1 said missing; it is present.) **Optional EXTEND:** add a `?capability=` / `?type=` filter param so the detail drawer can show per-item history without client-side filtering (currently only `?limit=`). The store already has `getByCapability()` (`install-audit.ts:125`) — just expose it.
|
||||
- **Workspace/team scoping of installs (§12.13).** No per-workspace install scoping exists today (installs are global to `~/.waggle/`). This is net-new product surface; recommend deferring to Phase 4 polish (PRD §22 risk register flags "marketplace scope creep" — `PRD:1435` — "Start with catalog + install audit, postpone billing/public marketplace").
|
||||
|
||||
**`.mind` migration flag:** none required for S21. The audit table (`install_audit`) already exists in both DDL sites (`install-audit.ts:54` + `schema.ts:119`). **Latent bug to flag (substrate-types §d#2):** `AuditRiskLevel` TS includes `'critical'` (`install-audit.ts:16`) but both DDL CHECKs only allow `('low','medium','high')` (`install-audit.ts:65`, `schema.ts:130`) — a `record({riskLevel:'critical'})` throws. The marketplace route sidesteps it (maps CRITICAL→`high`+`blocked`, `marketplace.ts:228`). If S21 surfaces a true "critical risk approval" state that writes `critical`, this CHECK must be widened first (idempotent ADD/relax via the migration runner's ADD-COLUMN pattern, `mind/db.ts:116`).
|
||||
|
||||
---
|
||||
|
||||
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
|
||||
|
||||
- **`ExtensionType`** (PRD §15.2, line 953: `'skill' | 'connector' | 'mcp' | 'model' | 'template' | 'external_tool'`) — **MISSING** from `apps/web/src/lib/types.ts` (substrate-types §e). Add. Note PRD union has `external_tool` but §12.13 categories say "Agents" not "external_tool" — **reconcile**: §12.13 needs an `'agent'` category; PRD §15.2 `ExtensionType` lacks `agent` and adds `external_tool`. Flag as open question; likely the union should be `skill|agent|connector|mcp|model|template` (+ optional `external_tool`).
|
||||
- **`Extension` interface** (new) — `{ id, name, description, type: ExtensionType, trust: AuditTrustSource-ish, risk: AuditRiskLevel, source, popularity?, version?, installed, updateAvailable, lifecycle: 'available'|'installed'|'update_available'|'installing'|'failed'|'risk_approval'|'disabled', scope?: Scope }`. No PRD §15 interface spells this out; derive from §12.13 + §14.7.
|
||||
- **`Scope`** (PRD §15.2: `'personal'|'workspace'|'team'|'organization'`) — **MISSING**, needed for install scoping; add (shared with other Extend/Memory cards).
|
||||
- Existing FE `MarketplacePackage` (local interface in `MarketplaceApp.tsx:8-18`) and `SkillPack` (`types.ts`) are narrower than `Extension`; `extension-catalog.ts` normalizes both into `Extension`.
|
||||
- Reuse the canonical audit enums (`AuditTrustSource`, `AuditRiskLevel`, `AuditCapabilityType` from `packages/core/src/install-audit.ts:16-22`) rather than re-declaring trust/risk literals in the FE — export them or mirror minimally.
|
||||
|
||||
---
|
||||
|
||||
## 7. Dependencies (screens / phases first)
|
||||
|
||||
- **PRD phasing:** Extend layer = **Phase 4 / Sprint 7** (`PRD:241-245,1353-1357`). Comes after Work (Home/Workspace), Intelligence (Agents/Skills), so S21 should land in that phase.
|
||||
- **Depends on / cross-links:**
|
||||
- Connector Hub (§12.7) and MCP Hub (§12.8) screens — S21 federates their catalogs and shares the `InstallAuditPanel`; their `/sync`/`/revoke`/`/test` backend routes (§5) are shared deliverables.
|
||||
- Skills Hub (§12.6) — S21's Skills facet reuses `CapabilitiesApp`'s pack flow.
|
||||
- Agent Center (§12.9), Models (Settings→Models), Templates (workspace-templates) — needed as catalog sources for the 6-category federation.
|
||||
- Dock IA refactor (`dock-tiers.ts` Extend zone) — S21 needs a real dock entry (currently none).
|
||||
- UpgradeModal / tier gating (`waggle:tier-insufficient` event) — already wired; reuse.
|
||||
- **Blocks:** nothing downstream; it is a leaf surface.
|
||||
|
||||
---
|
||||
|
||||
## 8. Effort
|
||||
|
||||
**L.** The frontend rework is moderate (consolidate 2 surfaces → 1 faceted catalog + extract shared `ExtensionCard`/`InstallAuditPanel` + new `extension-catalog.ts` federation). What pushes it to L: the **6-category federation** spans five different backend domains (marketplace, connectors, templates, personas/agents, models) each with a different shape, plus **6 net-new/extend §16.9 routes** (connector sync/revoke, mcps list/install/test/revoke) and the FE/BE type reconciliation. Not XL because no `.mind` migration, no new data store (federate-at-read), audit read route already exists, and install/uninstall + tier-gating + audit-trail plumbing are all already live.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open questions
|
||||
|
||||
1. **`ExtensionType` reconciliation.** PRD §12.13 categories = Skills/Agents/Connectors/MCPs/Models/Templates (6, incl. **Agents**). PRD §15.2 `ExtensionType` = skill/connector/mcp/model/template/**external_tool** (6, incl. external_tool, **no agent**). Which is canonical — add `agent`, keep `external_tool`, or both (7)?
|
||||
2. **Federate-at-read vs unified catalog table.** Ship S21 by federating existing domains client-side (no marketplace-DB change, fast), or invest in widening `InstallationType` + `marketplace.db` to natively catalog all 6 kinds (enables publish/install/version-tracking parity for agents/models/templates)? Recommend federate-first; revisit per §22 "postpone public marketplace".
|
||||
3. **Install scoping (§12.13 "workspace/team scoping").** Installs are global to `~/.waggle/` today. Is per-workspace/per-team install scoping in scope for Phase 4, or deferred (it implies new persisted scope state on every installed capability)?
|
||||
4. **"Update available" + changelog (J25).** No version/changelog tracking exists for installed packages beyond a `version` column on the marketplace package. Where does the "update available" signal come from — catalog sync diffing installed version vs latest, or a new field?
|
||||
5. **MCP test semantics.** `POST /api/mcps/:id/test` — does "test" mean spawn-and-handshake the MCP server (live), or static manifest/permission validation? Affects whether it can run without launching a process.
|
||||
6. **Should `MarketplaceApp` or `CapabilitiesApp` be the canonical S21 home** (IA double-representation, frontend.md §f)? Recommend `MarketplaceApp` reworked + real dock entry; `CapabilitiesApp` keeps Skills/Tools/Audit and drops its `marketplace` tab.
|
||||
Reference in New Issue
Block a user