Plan: Fix sidebar route-app flicker on load + remove profile "Owner" label¶
URL: https://mkdocs.justinsforge.com/memory/plans/sidebar-flicker-and-owner-label-2026-07-05/
Date: 2026-07-05
Approved spec: System apps in the sidebar are server-rendered (layout apps prop) and stable; route apps (Business Manager, News, Social Analytics, Agent, Email, Clips) render only from the client hook useInstalledApps, which starts from an empty module cache, blanks to [] in its mount effect, then fetches /api/apps/install after hydration, so they flash in/out/in on load. Fix: server-compute the installed ids in the suite layout and pass them to the shell as initialInstalled; useInstalledApps seeds its shared cache on the very first load (cacheWs === null) so the first paint matches the authoritative list and the mount effect skips the blank+refetch. Separately, remove the · Owner label from the bottom profile row.
Out of scope:
- Workspace-switch flash (the deliberate anti-leak blank+refetch stays as-is)
- App Library, owner-only filtering semantics, any /api/apps/install change
- Any DB migration or write path
Task list¶
Task 1: Seed useInstalledApps from a server-provided list¶
- Files:
/home/justinwieb/forge-suite-wt/feat/sidebar-flicker-profile/components/apps/useInstalledApps.ts - What: Add an optional
initial?: { ids: string[]; wsId: number | null }param. In theuseStatelazy initializer, wheninitial && cacheWs === null, set the module cache (installedIds = initial.ids; loaded = true; cacheWs = initial.wsId) so the first client paint matches the authoritative list. The existing effect then seeswsId === cacheWsand skips its blank+refetch; genuine workspace switches (cacheWs != null) are untouched. - Verification:
cd <wt> && npx tsc --noEmit(typechecks clean); the hook still exports the sameUseInstalledAppsshape. - Commit:
fix(apps): let useInstalledApps seed its cache from a server list
Task 2: Server-render installed ids and pass to the shell¶
- Files:
/home/justinwieb/forge-suite-wt/feat/sidebar-flicker-profile/app/(suite)/layout.tsx,/home/justinwieb/forge-suite-wt/feat/sidebar-flicker-profile/components/workspace/WorkspaceShell.tsx - What: In the layout,
await getInstalledApps(me, wsId)(route apps) and combine with the already-computedappNodestypes (installed system apps) intoinitialInstalled, pass it toWorkspaceShell. In the shell, acceptinitialInstalled?: string[]and calluseInstalledApps({ ids: initialInstalled ?? [], wsId: ws.id }). - Verification:
cd <wt> && npx tsc --noEmit; then browser: hard-reloadhttp://100.97.43.104:3061several times and confirm Business Manager / News / Social Analytics / Agent no longer flash in/out. - Commit:
fix(sidebar): server-render installed apps so route apps don't flash on load
Task 3: Remove the "Owner" label from the profile row¶
- Files:
/home/justinwieb/forge-suite-wt/feat/sidebar-flicker-profile/components/workspace/AccountMenu.tsx - What: In the account row, render only
{shownName}(drop the inner<span className="ws-acct-plan"> · {shownPlan}</span>). Remove the now-unusedplanprop and theshownPlancomputation. - Verification:
cd <wt> && npx tsc --noEmit && npx eslint components/workspace/AccountMenu.tsx; browser: the bottom profile row shows the name with no· Owner. - Commit:
feat(sidebar): drop the Owner/Member label from the profile row
Task 4: Verify end-to-end in the running app¶
- Files: none (verification only)
- What: With the dev server on :3061, load the workspace, hard-reload 5+ times watching the Apps section, and confirm (a) no route-app flash, (b) system apps still stable, (c) profile row shows name only. Capture a screenshot to the scratchpad as evidence.
- Verification: screenshot saved + visual confirmation;
git log --onelineshows three separate commits. - Commit: none (no code change) — report branch + review notes to Justin.