Handoff: workspace scoping fixes (Tasks bleed, calendar sources, settings gear)¶
URL: https://mkdocs.justinsforge.com/memory/handoffs/workspace-scoping-fixes-2026-07-06/
Date: 2026-07-06
Branch: feat/workspace-scoping-fixes (worktree ~/forge-suite-wt/feat/workspace-scoping-fixes, dev slot :3061, DB ct_feat_workspace_scoping_fixes). NOT merged, NOT deployed. Follows the merged feat/multiuser-apps-michael work; fixes Justin's 2026-07-06 feedback.
Commits¶
30e57ddTasks never cross workspaces + per-workspace calendar sources.listTaskswithopts.workspaceloads ONLY that workspace (counts/lists/tags scope with it); TasksApp + every TasksClient fetch pass the active workspace; the calendar task overlay scopes the same way. Noopts.workspace= the old aggregate (Cockpit, capture bot unaffected).- New workspaces auto-install Tasks + Habits + Notes (supersedes the 2026-07-01 no-auto-provision call), scoped to that workspace.
- Calendar: per-user per-workspace Google source visibility at
users.prefs.calendar_app.workspace_sources. Default: sources ON in yourkind='personal'home workspace, OFF inkind='business'brand workspaces (why Gus was showing Justin's personal email calendar). Sync AND reads honor it (disabling hides already-cached rows immediately). Settings modal (sliders button, now in every calendar view) gained a "My calendars in this workspace" checkbox section;op:"set_sources"on /api/apps/calendar. 39b9c2cSettings gear at sidebar bottom + workspace gear opens full settings.- Footer icon cluster gains a gear (very bottom of the sidebar) opening the Settings popup at Workspace > General. Mechanism:
openSettingsModal(section, workspaceId?)window event, AccountMenu owns the modal. - WorkspaceSwitcher's per-workspace gear now opens the FULL settings modal with that workspace's row pre-expanded (
focusWorkspaceId), replacing the small WorkspaceSettings dialog. The expanded row absorbed rename (blur/Enter) + type-to-confirm soft delete; GeneralPane gained a Recently-deleted restore list.WorkspaceSettings.tsxdeleted.
Verified¶
- API: new workspace (id 70, kind business) auto-installed tasks/habits/knowledge nodes;
/api/tasks?workspace=returns only that workspace; calendar sources default false on business ws / true on ws 67; set_sources toggles persist. - Playwright on :3061: footer gear opens Settings at General; "Settings for Scope Test" gear opens the modal with Scope Test expanded (icon/name/description visible).
- tsc clean; eslint delta vs main = 0 new errors (one added setState-in-effect matches the file's existing pattern).
Round 2 (same session, commit a067986)¶
- Main-sidebar footer gear REMOVED (misread the first ask). The real ask: the TASKS app's icon rail now carries a Settings action pinned at its very bottom (
AppSidebar bottomAction, generic for other apps later); the panel list keeps only Completed + Trash. - Settings > General: each workspace renders as its own card; Sharing (invite email + role) nests inside each expanded card; the standalone Share section is gone. Rename/delete/restore from round 1 unchanged.
- Workspace image icons no longer clip (Gus logo): default
object-fit: contain, plus Fit/Fill + Size (50-200%) controls in the workspace card, encoded as#fit=...&zoom=...fragment on the stored icon string (WorkspaceAvatar.parseWorkspaceIcon); no server/schema change. - All verified in-browser on :3061 (tasks rail, modal cards, no main-sidebar gear).
Round 3 (same session, commit e200b72): Tasks app polish¶
- Per-workspace task visibility replaces the hard single-workspace scope from round 1:
prefs.tasks_app.visible_workspaces.<wsId>= which workspaces' tasks show inside that workspace's Tasks app (default only itself). Justin checks everything on in his personal workspace; Scope Test/Gus stay scoped.listTasksgainedappWorkspace; the Tasks settings modal's Workspaces page is now this per-workspace checklist (current workspace locked on); the calendar task overlay follows the same pref. The old globalexcluded_workspacespref still governs the workspace-less aggregate (Cockpit, capture bot). - Composer date popover: Today/Tomorrow/Next-week keep the popover open (dismiss by clicking out); the chosen time renders with an X to clear it; the "All day" control is gone (no time = all-day); Reminder defaults to None.
- People chip in the composer (peer of Priority/Tags): assign a workspace member on create; roster lazy-loads from
/api/workspace/[id]/members;assignedTorides POST /api/tasks (route already supported it). - Out-of-view create toast: adding e.g. a tomorrow task from Today now toasts
"<title>" won't show here because it's due <date>.instead of the optimistic row silently vanishing on revalidate. - Verified: API round-trip of visibility prefs per workspace (67 includes 70 after opt-in, 70 stays scoped), assigned create returns the member name, and the popover/People/toast behaviors in-browser on :3061.
Round 4 (same session, commit f26c7af)¶
- The out-of-view create toast is replaced by an "In Another View" ghost section: the created task pins right under the composer with a blue gradient ring and an "In another view" pill (
.tk-ghost-*in tasks.css). Session-only (React state, refresh clears); a ghost hides once its task is a real row in the current scope. - Settings > General gained an "Add new workspace" dashed row under the workspace cards: name + Create via /api/workspace/create (also switches active, matching the switcher). AccountMenu re-fetches the profile on WORKSPACE_UPDATED_EVENT so the modal list updates live.
- Verified in-browser: ghost row + pill render and survive revalidate; workspace 71 "Row Created WS" created from the modal (delete it from Recently deleted flow if unwanted; it's the dev DB anyway).
Round 5 (same session, commit aa2e31f)¶
- All view: new smart view above Today in the Tasks sidebar (
counts.all), and it's the DEFAULT landing view (TaskDefaultViewgained "all" in lib + client mirror + settings options; users with a previously saved default keep theirs). - Just Added: in-scope creates pin in a session-only "Just Added" section right under the composer (excluded from the normal groups so no dupes); refresh clears it.
- No-flash ghosts: out-of-view creates skip the optimistic list insert entirely and go straight into In Another View (temp row swapped for the real task after POST). The "In another view" pill overlay was removed; the section header + blue gradient ring carry the label.
- Verified in-browser: fresh open lands on All, Just Added renders under the composer, no bottom flash on out-of-view creates.
Round 6 (same session, commit 413e3f6)¶
- Assignee badge alignment:
.tk-badgeis inline-flex/centered so the icon+name pill sits on the same line as the due date. - Completed-in-place: checking a task off keeps the row in its normal group, checked + struck through, for the session (snapshot merged back after revalidate); a reload files it into Completed as before. Uncheck or toast-Undo drops the snapshot. Verified in-browser.
Notes / for review¶
- Footer now holds [account row][+][gear]; the account name truncates a little at default sidebar width. Say the word if the gear should replace "+"-adjacent placement or shrink.
- "Icon part of the sidebar" was interpreted as the footer icon cluster (no separate icon rail exists in the shell); adjust if a different spot was meant.
- Justin's home workspace keeps his Google calendars by default; brand workspaces need a one-time toggle in Calendar settings if he DOES want them there.
- Slot DB needed
forge_workspace_migrate.pyafter bootstrap (base clone lagged the merged migrations); remember for future slots.
[Claude Code]