18 KiB
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.txtlines 346-349, 470-473, 509, 514, 531-540, 542-545, J18/J19/J20/J21 (227-242); mockupscreen_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/connectisrequireTier('TEAMS')(:110);/api/team/governance/permissionsisrequireTier('ENTERPRISE')(:418)./api/teams/*= local SQLite CRUD onteams.db(teams+team_memberstables, DDL atteam.ts:51-72), works in solo mode viagetLocalUserId(dataDir)(:457). Full CRUD:POST /api/teams(:460, auto-adds creator asowner),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, readsaudit_eventsviagetAuditDbfromevents.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 ofTeamGovernanceApp.tsx) — tab shell: Overview · Shared Memory · Shared Artifacts · Skills · Agents · MCPs · Automations · Settings. Tier-gate stays (Teams/Enterprise) via existingLockedFeature.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). Reuseui/avatar,ui/badge,ui/dropdown-menu.overlays/InviteMemberDialog.tsx— invite by email/userId + role select (J18). Reuseui/dialog,ui/select. POSTs to/api/teams/:id/members.apps/team/TeamActivityFeed.tsx— audit/activity list (reuseTimelineApp/EventsApprow 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/TeamRoletypes exist inapps/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.tsTeamRole = 'owner'|'admin'|'member'|'viewer'(:21),MemberRow(team.ts:799-810mapper). - 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.dbCHECK →owner | admin | member | viewerRecommendation: 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.
- PRD §17.2 →
Scopeunion (personal/workspace/team/organization) — MISSING in FE types (substrate-types §e). Needed forShareToTeamDialog. Add tolib/types.ts(PRD §15.2).- Capability/policy types for the governance tabs (
team_capability_policies/overrides/ requestsshapes from 02b) — add FE types when that read route lands. - Reuse existing
TierCapabilitiesfrom@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
TeamRoletype + all member-management UI — resolve first. ShareToTeamDialogis 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 againstmemory_frames+searchTeamMemory.- Permission-denied / request-access pattern (J21) is shared with other screens — build the
reusable
PermissionDeniedcomponent 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
- RBAC role model (blocking): adopt PRD §17.2 (
Owner/Admin/Contributor/Viewer), blueprint (+Member +Guest), or keep the liveteams.db4-role union (owner/admin/member/viewer)? Recommendation: keep live union for v1, map Contributor→Member, defer Guest. Needs founder sign-off. - PUT vs PATCH role-gate inconsistency (real bug):
PUT /api/teams/:id/members/:userIdis owner-only (team.ts:624) butPATCHon the same path is owner/admin (:649). The PRD uses PATCH. Which gate is correct — owner-only role changes, or owner+admin? Align both. /api/sharescope semantics in solo/local mode: memory frames have no structuredscope/teamIdcolumn (implicit per-.mind, substrate-types §c). For v1, model team-share as (a) implicit via workspaceteamId+ remote sync, or (b) amemory_framesmetadata migration? Recommendation: (a) for v1, defer (b).- Local vs remote team source of truth on this screen:
/api/teams/*(localteams.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 fromteams.dbor the team server.) - 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? - Postgres no-cascade (02b:44): deleting a team is blocked by FKs if
team_entities/resources/ members reference it. The localDELETE /api/teams/:iddeletes members first (team.ts:563) — does the cloud delete path need an explicit child-cleanup order, and should the UI warn before delete?