6.3 KiB
OS UI/UX Production Readiness Audit — 2026-05-13
Scope: apps/web/src/components/os/** + packages/server/src/local/routes/{skills,marketplace}.ts
Tier model (CONFIRMED — no rename)
Internal IDs unchanged. User-facing labels (from apps/web/src/components/os/overlays/onboarding/constants.ts:87):
simple→ "Essential" — 6 dock entries (home, chat, files, vault, settings)professional→ "Standard" — 8 entries (+ agents, memory)power→ "Everything" — 13 entries (all docks visible)admin→ alias ofpower
getDockForTier(tier, billingTier) in dock-tiers.ts filters by billingTier (FREE/TRIAL/PRO/TEAMS/ENTERPRISE) on top, hiding TEAMS-only entries (Approvals, Team Governance) below TEAMS.
25 OS apps registered in Desktop.tsx
chat · dashboard · settings · vault · profile · connectors · memory · events · cockpit · mission-control · capabilities · waggle-dance · agents · files · scheduled-jobs · marketplace · voice · room · approvals · timeline · backup · telemetry · governance + spawn-agent dock shortcut. Each renders a real component (renderAppContent switch).
Backend routes (verified)
- Marketplace:
/api/marketplace/{search,installed,install,uninstall,packs,packs/:slug,sources,categories,security-check,security-status,sync,publish,enterprise-packs}— full surface. - Skills:
/api/skills+/api/skills/starter-pack/{catalog,:id}+/api/skills/capability-packs/{catalog,:id}+/api/skills/test+/api/skills/create+ CRUD on/api/skills/:name. - Plugins:
/api/plugins/*including tool-file editor (PUT /api/plugins/:name/tools/:toolName). - Audit:
/api/audit/installsreturns{ entries: [...] }.
Bugs found in Phase 2 audit
B1: Audit tab silently empty
CapabilitiesApp.tsx:20-21 reads data.installs but server returns { entries }. AuditTab always renders empty list.
Fix: Read data.entries (or both for resilience).
B2: Marketplace install 403 on FREE tier with no upgrade affordance
marketplace.ts:170 gates POST /api/marketplace/install on requireTier('PRO'). FREE users get raw 403 → toast "Install failed" with no upgrade path.
Fix: MarketplaceApp + CapabilitiesApp install handlers should detect 403 + open UpgradeModal.
B3: SkillPack starter-pack catalog shape mismatch
Server /api/skills/starter-pack/catalog returns { skills: [{id,name,description,family,familyLabel,state,isWorkflow}], families }. SkillPack type expects {id,name,description,category,trust,skills?[]}. Cards render but category is undefined → no color badge; trust is undefined → falls back to community. Cosmetic but reduces information density.
Fix: Map starter-pack entries to SkillPack shape (family→category, set trust='verified').
Phase 3 — SHIPPED c2f45ca
- ✅ B1 fixed — CapabilitiesApp AuditTab reads
entries(withinstallsfallback) and normalises capabilityName/action. - ✅ B2 fixed — MarketplaceApp + CapabilitiesApp install handlers detect 403 and dispatch
waggle:tier-insufficient. Other failures parseerr.message/err.error/blockedand toast.adapter.installPack+adapter.installMarketplacePacknow throw on!okwith status + body attached. - ✅ B3 fixed — adapter maps
family→category,state→installed, setstrust='verified'for starter-pack and capability-pack catalogs.
Phase 4 — SHIPPED 1bcbeef
Reframed the design after finding acquire_capability and install_capability already exist in packages/agent/src/skill-tools.ts. The gap was UX, not infra: the agent's recommendation surfaced as prose ("call install_capability with name X and source Y") that the user had to translate manually.
Built a pure-UI bridge:
CapabilityRequestCard.tsx— inline action card (Install / Dismiss). Routes by source:starter-pack→adapter.installPack(no auth, bundled);marketplace→ resolves packageId via search, then POSTs install; 403 dispatches UpgradeModal.capability-request-parser.ts—segmentText()splits agent text into[text, capability, text...]around two patterns:- Pattern A (preferred, structured):
<!--waggle:capability_request {"name":"X","source":"Y","reason":"..."}--> - Pattern B (legacy):
`install_capability` with name "X" and source "Y"
- Pattern A (preferred, structured):
TextBlock.tsx— renders segments inline; preserves streaming cursor + bouncing-dot loader.- Dedup by
source::nameso a single proposal mentioned twice (body + recommendation) renders one card.
No agent package rebuild needed — Pattern B picks up the current acquire_capability summary verbatim. When the agent gets updated to emit Pattern A, richer info flows through with zero client change.
Phase 5 — SHIPPED (this commit)
- Extracted
segmentText/Segmentintocapability-request-parser.tssibling file. Silences react-refresh/only-export-components warning and isolates the parser surface for testing. - Static sweep of
apps/web/src/components/os/**for stubs: zero. TheComing soon...line atDesktop.tsx:358is a defensive default for unknownAppIds; all 25 known IDs map to real components. - ESLint clean on all Phase 3+4+5 touched files.
Production-readiness state at end of sprint
- Three tiers (
simple/professional/power) configured indock-tiers.tswith UI labels Essential/Standard/Everything. All 13 docks visible atpower. Tier filtering by billing tier (FREE→ENTERPRISE) works for TEAMS-gated entries (Approvals, Team Governance). - 25 dock apps all clickable, all map to real components in
renderAppContent. - Skill / tool / marketplace search + install fully wired:
/api/marketplace/search+/api/marketplace/install+ 403 → UpgradeModal/api/skills/starter-pack/catalog+ per-skill install (always allowed)/api/skills/capability-packs/catalog+ bulk pack install/api/audit/installsrendered with normalised entries- SecurityGate runs on every install (CRITICAL → blocked, HIGH → force-flag required, MEDIUM/LOW → audit + proceed)
- Agent install-request UX:
acquire_capabilityproposals render inline install buttons via parser. Marker-protocol ready for richer payloads.
Tests + checks
- 526/526 apps/web suite green (8 new in
capability-request-parser) tsc --noEmit --project apps/webclean- ESLint clean on touched files (Phase 3+4+5 surfaces)