# Floating Chat Deprecation Sweep Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Delete every floating-chat code path across the adiuvAI Electron app — components, context, hooks, DOM attributes, stream branches, main-process methods, and shared-type artefacts — in five clean commits. **Architecture:** The contextual sidebar (M4) fully replaces floating chat; floating is dead code with no live consumers. Each task targets one layer: (1) renderer components/context/hooks, (2) DOM data-attributes, (3) renderer hook logic, (4) main-process IPC/orchestrator, (5) store/localStorage keys sweep. **Tech Stack:** TypeScript, React 19, Electron (main + preload + renderer), tRPC v11, Zod, electron-store. --- ## Pre-flight: baseline tsc check - [ ] **Run tsc before touching anything** ```bash cd /c/Users/PC-Roby/Documents/_adiuvai_workspace/adiuvAI source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | tail -20 ``` Record the number of pre-existing errors (expected: 0). Any errors here are pre-existing and not your fault. --- ## Task M6.1 — Delete FloatingChat component, context, hook + AppShell cleanup **Files:** - Delete: `src/renderer/components/ai/FloatingChat.tsx` - Delete: `src/renderer/context/FloatingChatContext.tsx` - Delete: `src/renderer/hooks/useDoubleClickAI.ts` - Modify: `src/renderer/components/layout/AppShell.tsx` - Modify: `src/renderer/components/projects/ProjectDetail.tsx` - Modify: `src/renderer/components/timeline/TimelineGanttView.tsx` - Modify: `src/renderer/routes/notes.$noteId.tsx` - Modify: `src/renderer/routes/tasks.tsx` ### Context Six files import from `FloatingChatContext` or `FloatingChat`. After deleting the three source files, all six must be cleaned up. `ProjectDetail.tsx`, `TimelineGanttView.tsx`, `notes.$noteId.tsx`, and `tasks.tsx` import `useFloatingChat()` and call `registerSection`/`unregisterSection`. These are pure floating-chat wiring — they register DOM regions with the floating panel so it could anchor itself. With floating chat gone, these calls become dead code and must be removed entirely (no replacement needed; the contextual sidebar uses `useContextualScope` for scope awareness, which is already imported in tasks.tsx and ProjectDetail.tsx). `AppShell.tsx` wraps the tree in `` (line 151) and calls `useDoubleClickAI()` inside `AppShellInner` (line 166). ### Steps - [ ] **Step 1: Confirm all consumers before deletion** ```bash grep -rn "FloatingChat\|useFloatingChat\|FloatingChatProvider\|useDoubleClickAI" src/renderer/ ``` Expected output matches: `AppShell.tsx`, `FloatingChat.tsx`, `FloatingChatContext.tsx`, `useDoubleClickAI.ts`, `ProjectDetail.tsx`, `TimelineGanttView.tsx`, `notes.$noteId.tsx`, `tasks.tsx`. If any unexpected file appears, stop and report before proceeding. - [ ] **Step 2: Delete the three source files** ```bash git rm src/renderer/components/ai/FloatingChat.tsx git rm src/renderer/context/FloatingChatContext.tsx git rm src/renderer/hooks/useDoubleClickAI.ts ``` - [ ] **Step 3: Clean AppShell.tsx** Open `src/renderer/components/layout/AppShell.tsx`. Remove line 28: ```ts import { useDoubleClickAI } from '@/hooks/useDoubleClickAI'; ``` Remove lines 70–71: ```ts import { FloatingChatPortal } from '@/components/ai/FloatingChat'; import { FloatingChatProvider } from '@/context/FloatingChatContext'; ``` In `AppShell` (around line 149–163), change: ```tsx export function AppShell({ children }: AppShellProps) { return (
{children}
); } ``` to: ```tsx export function AppShell({ children }: AppShellProps) { return (
{children}
); } ``` In `AppShellInner` (around line 166), remove: ```ts useDoubleClickAI(); ``` Search for `` in the file (around line 288) and delete that line. - [ ] **Step 4: Clean ProjectDetail.tsx** Open `src/renderer/components/projects/ProjectDetail.tsx`. Remove line 24: ```ts import { useFloatingChat } from '@/context/FloatingChatContext'; ``` Remove line 63: ```ts const { registerSection, unregisterSection } = useFloatingChat(); ``` Remove the `useEffect` block that calls `registerSection`/`unregisterSection` (lines ~91–101): ```ts useEffect(() => { if (isLoading || !project) return; registerSection({ id: 'project-summary', label: 'Project Summary', ref: summaryRef, projectId }); registerSection({ id: 'project-tasks', label: 'Tasks', ref: tasksRef, projectId }); registerSection({ id: 'project-notes', label: 'Notes', ref: notesRef, projectId }); return () => { unregisterSection('project-summary'); unregisterSection('project-tasks'); unregisterSection('project-notes'); }; }, [projectId, isLoading, project, registerSection, unregisterSection]); ``` - [ ] **Step 5: Clean TimelineGanttView.tsx** Open `src/renderer/components/timeline/TimelineGanttView.tsx`. Remove line 31: ```ts import { useFloatingChat } from '@/context/FloatingChatContext'; ``` Remove line 95: ```ts const { registerSection, unregisterSection } = useFloatingChat(); ``` Remove the `useEffect` that calls `registerSection`/`unregisterSection` (lines ~107–110): ```ts useEffect(() => { registerSection({ id: sectionId, label: sectionLabel, ref: sectionRef, projectId }); return () => unregisterSection(sectionId); }, [sectionId, sectionLabel, projectId, registerSection, unregisterSection]); ``` - [ ] **Step 6: Clean notes.$noteId.tsx** Open `src/renderer/routes/notes.$noteId.tsx`. Remove line 29: ```ts import { useFloatingChat } from '@/context/FloatingChatContext'; ``` Remove line 117: ```ts const { registerSection, unregisterSection } = useFloatingChat(); ``` Remove the `useEffect` that calls `registerSection`/`unregisterSection` (lines ~119–128): ```ts useEffect(() => { registerSection({ id: 'note-editor', label: 'Note Editor', ref: editorRef, projectId: noteProjectId, anchorMode: 'right-margin', }); return () => unregisterSection('note-editor'); }, [noteId, noteProjectId, registerSection, unregisterSection]); ``` - [ ] **Step 7: Clean tasks.tsx** Open `src/renderer/routes/tasks.tsx`. Remove line 5: ```ts import { useFloatingChat } from '@/context/FloatingChatContext'; ``` Remove line 18: ```ts const { registerSection, unregisterSection } = useFloatingChat(); ``` Remove the `useEffect` that calls `registerSection`/`unregisterSection` (lines ~20–27): ```ts useEffect(() => { registerSection({ id: 'tasks-overview', label: 'Tasks Overview', ref: overviewRef }); registerSection({ id: 'tasks-list', label: 'Task List', ref: listRef }); return () => { unregisterSection('tasks-overview'); unregisterSection('tasks-list'); }; }, [registerSection, unregisterSection]); ``` Also remove the `overviewRef` and `listRef` ref declarations if they are now unused (check whether they are still referenced by the JSX `ref=` attributes after M6.2 strips `data-ai-section`): ```ts const overviewRef = useRef(null); const listRef = useRef(null); ``` Note: After M6.2 removes `data-ai-section` attributes, `ref={overviewRef}` and `ref={listRef}` in the JSX will also be gone, making the refs fully dead. Remove them here (or in M6.2 — just do it in whichever task you're in when you notice them). - [ ] **Step 8: Type check** ```bash source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -i "error" | head -30 ``` Expected: zero errors related to FloatingChat, FloatingChatContext, useDoubleClickAI. If any appear, locate the offending file and remove the import/usage. - [ ] **Step 9: Commit** ```bash git add src/renderer/components/layout/AppShell.tsx \ src/renderer/components/projects/ProjectDetail.tsx \ src/renderer/components/timeline/TimelineGanttView.tsx \ src/renderer/routes/notes.\$noteId.tsx \ src/renderer/routes/tasks.tsx git commit -m "$(cat <<'EOF' refactor(contextual): delete FloatingChat, FloatingChatContext, useDoubleClickAI Replaced by ContextualChatProvider + AdiuvaTriggerButton in M4. Pre-1.0 clean removal — no deprecation period. EOF )" ``` --- ## Task M6.2 — Strip all `data-ai-section` attributes **Files:** - Modify: `src/renderer/components/projects/ProjectDetail.tsx` - Modify: `src/renderer/components/timeline/TimelineGanttView.tsx` - Modify: `src/renderer/routes/notes.$noteId.tsx` - Modify: `src/renderer/routes/tasks.tsx` ### Context `data-ai-section` was used by `useDoubleClickAI` to walk the DOM and find the nearest section anchor to open floating chat. Now that floating chat is gone, these attributes are purely dead markup. The contextual sidebar uses `scope` payloads, not DOM attributes. Current occurrences (7 total): - `ProjectDetail.tsx:538` — `data-ai-section="project-summary"` - `ProjectDetail.tsx:620` — `data-ai-section="project-tasks"` - `ProjectDetail.tsx:631` — `data-ai-section="project-notes"` - `TimelineGanttView.tsx:239` — `data-ai-section={sectionId}` - `notes.$noteId.tsx:291` — `data-ai-section="note-editor"` - `tasks.tsx:42` — `data-ai-section="tasks-overview"` - `tasks.tsx:61` — `data-ai-section="tasks-list"` ### Steps - [ ] **Step 1: List all occurrences** ```bash grep -rn "data-ai-section" src/renderer/ ``` Confirm you see exactly the 7 occurrences listed above. If more appear, handle them too. - [ ] **Step 2: Remove from ProjectDetail.tsx** For each JSX element with a `data-ai-section` prop, delete only that prop (keep `className` and all others). Example: Find: ```tsx
``` Change to: ```tsx
``` Repeat for `project-tasks` and `project-notes`. - [ ] **Step 3: Remove from TimelineGanttView.tsx** Find the element at line ~239 and remove only the `data-ai-section={sectionId}` prop. Keep `ref={sectionRef}` and any other props. Also check: after M6.1 removed `registerSection`/`unregisterSection`, there may now be `sectionRef`, `sectionId`, `sectionLabel` variables declared but unused. Remove any that are now dead. Check with: ```bash grep -n "sectionRef\|sectionId\|sectionLabel" src/renderer/components/timeline/TimelineGanttView.tsx ``` If these variables are only used by the removed `useEffect` and the now-removed `data-ai-section` attribute, delete their declarations too. - [ ] **Step 4: Remove from notes.$noteId.tsx** Find the `` at line ~291: ```tsx ``` Change to: ```tsx ``` Note: `editorRef` is still used by the existing scroll area ref, so keep it. - [ ] **Step 5: Remove from tasks.tsx** Remove `data-ai-section="tasks-overview"` from the div at line ~42 and `data-ai-section="tasks-list"` from the div at line ~61. Also remove `ref={overviewRef}` and `ref={listRef}` from those elements if the refs were removed in M6.1. If `overviewRef` and `listRef` are now unused (no JSX `ref=` and no other use), delete their `useRef` declarations too. - [ ] **Step 6: Verify clean** ```bash grep -rn "data-ai-section" src/renderer/ echo "exit=$?" ``` Expected: no output, `exit=1` (grep found nothing). - [ ] **Step 7: Type check + commit** ```bash source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -i "error" | head -20 ``` Expected: zero errors. ```bash git add src/renderer/components/projects/ProjectDetail.tsx \ src/renderer/components/timeline/TimelineGanttView.tsx \ src/renderer/routes/notes.\$noteId.tsx \ src/renderer/routes/tasks.tsx git commit -m "$(cat <<'EOF' refactor(contextual): strip all data-ai-section attributes Section-anchoring obsolete now that there is no floating chat. The contextual sidebar uses scope payload, not DOM attributes. EOF )" ``` --- ## Task M6.3 — Drop `'floating'` from `useAIChat` + `ChatInputBox` + `useChatStream` **Files:** - Modify: `src/renderer/hooks/useAIChat.ts` - Modify: `src/renderer/hooks/useChatStream.ts` - Modify: `src/renderer/components/ai/ChatInputBox.tsx` - Modify: `src/renderer/components/brief/TaskBriefChat.tsx` ### Context `useAIChat.ts` has `FloatingDomainSignal` type, `'floating'` in `UIChatContext.type` union, a `scope` field, a `'floating'` cache-key branch, `isFloating` logic in `handleSend`, `floating_domain` switch case, and `onDomainSignalRef`. All dead after M6.1 deleted `FloatingChat.tsx` which was the only consumer. `useChatStream.ts` has a `floating_domain` case and the `onDomainSignal` option — kept during M2.1 explicitly "until M6 removes floating." Now is the time. `ChatInputBox.tsx` has `'floating'` as a variant in `ChatInputBoxVariant` with its own style entry. This is a visual variant only — the type can be removed if nothing still passes `variant="floating"`. After `FloatingChat.tsx` is gone, nothing does. `TaskBriefChat.tsx` passes `mode: 'floating'` to `chatMutation.mutate(...)`. After removing the floating mode from the tRPC schema (M6.4), this becomes a type error. Fix it here by removing `mode: 'floating'` and `scope` from the mutation call — task briefs will fall through to the default home orchestrator, or better: use `mode: 'contextual'` with the scope. Since task brief already passes `scope: { type: 'task', id: taskId }`, change `mode: 'floating'` to `mode: 'contextual'`. `parseMutationsToEntityTags` and `TABLE_TO_ENTITY` in `useAIChat.ts` are still needed — `useChatStream.ts` imports `parseMutationsToEntityTags` from `useAIChat.ts`. Keep both exports. ### Steps - [ ] **Step 1: Edit useAIChat.ts** Open `src/renderer/hooks/useAIChat.ts`. **Delete lines 4–13** (the `FloatingDomainSignal` type): ```ts export type FloatingDomainSignal = | 'tasks' | 'notes' | 'timelines' | 'projects' | { type: 'task' | 'timeline' | 'project' | 'note' | 'node'; id?: string | null; section?: 'task' | 'timeline' | 'note' | null; }; ``` **Replace the `UIChatContext` interface** (lines 23–31): ```ts export interface UIChatContext { type: 'global' | 'project' | 'floating'; projectId?: string; /** For floating mode — the entity scope to pass to the backend. */ scope?: { type: 'task' | 'project' | 'note' | 'timeline'; id?: string; }; } ``` with: ```ts export interface UIChatContext { type: 'global' | 'project'; projectId?: string; } ``` **Delete the `UseAIChatOptions` interface** (lines 50–52): ```ts interface UseAIChatOptions { onDomainSignal?: (domain: FloatingDomainSignal) => void; } ``` **Update the `useAIChat` function signature** — remove the `options` parameter: ```ts export function useAIChat(defaultContext: UIChatContext, options?: UseAIChatOptions): UseAIChatReturn { ``` becomes: ```ts export function useAIChat(defaultContext: UIChatContext): UseAIChatReturn { ``` **Update `getContextCacheKey`** — replace the entire function body: ```ts function getContextCacheKey(ctx: UIChatContext): string { if (ctx.type === 'global') return 'global'; if (ctx.type === 'project') return `project:${ctx.projectId ?? ''}`; // Floating chat should keep a single continuous session while the panel is open, // even when route/section context changes due floating-domain navigation. return 'floating'; } ``` with: ```ts function getContextCacheKey(ctx: UIChatContext): string { if (ctx.type === 'global') return 'global'; return `project:${ctx.projectId ?? ''}`; } ``` **Update the `useMemo` for `contextCacheKey`** — remove `defaultContext.scope?.type` and `defaultContext.scope?.id` from deps: ```ts const contextCacheKey = useMemo( () => getContextCacheKey(defaultContext), [defaultContext.type, defaultContext.projectId, defaultContext.scope?.type, defaultContext.scope?.id], ); ``` becomes: ```ts const contextCacheKey = useMemo( () => getContextCacheKey(defaultContext), [defaultContext.type, defaultContext.projectId], ); ``` **Remove `onDomainSignalRef`** (lines 151–152): ```ts const onDomainSignalRef = useRef(options?.onDomainSignal); onDomainSignalRef.current = options?.onDomainSignal; ``` **Remove the `floating_domain` case** from the `switch (event.type)` block (lines 237–239): ```ts case 'floating_domain': onDomainSignalRef.current?.(event.domain); break; ``` **Remove `isFloating` and the conditional spread** in `handleSend` (lines 249–259): ```ts const isFloating = ctx.type === 'floating'; chatMutationRef.current.mutate( { requestId, message: trimmed, conversationHistory, sessionId: sessionIdRef.current, ...(isFloating && ctx.scope ? { mode: 'floating' as const, scope: ctx.scope } : {}), }, ``` becomes: ```ts chatMutationRef.current.mutate( { requestId, message: trimmed, conversationHistory, sessionId: sessionIdRef.current, }, ``` - [ ] **Step 2: Edit useChatStream.ts** Open `src/renderer/hooks/useChatStream.ts`. Remove `onDomainSignal` from `UseChatStreamArgs` interface (lines 17–18): ```ts /** Optional: legacy floating_domain pivot signal. Kept until M6 removes floating. */ onDomainSignal?: (domain: unknown) => void; ``` Remove `onDomainSignal` from the destructuring of `useChatStream`'s argument (line 25): ```ts onDomainSignal, ``` Remove `domainRef` declarations (lines 33–34): ```ts const domainRef = useRef(onDomainSignal); domainRef.current = onDomainSignal; ``` Remove the `floating_domain` case from the switch block (lines 70–72): ```ts case 'floating_domain': domainRef.current?.(event.domain); break; ``` - [ ] **Step 3: Edit ChatInputBox.tsx** Open `src/renderer/components/ai/ChatInputBox.tsx`. The `'floating'` variant in `ChatInputBoxVariant` (line 12) and its entry in `VARIANT_STYLES` (lines 30–35) should be removed since no live code passes `variant="floating"` any more. Change: ```ts type ChatInputBoxVariant = 'panel' | 'floating' | 'comment'; ``` to: ```ts type ChatInputBoxVariant = 'panel' | 'comment'; ``` Remove the `floating` entry from `VARIANT_STYLES` (lines 30–35): ```ts floating: { container: 'flex items-center gap-2 px-3 py-2.5', textarea: 'flex-1 resize-none bg-transparent text-sm placeholder:text-muted-foreground/60 outline-none max-h-20 overflow-y-auto', button: 'flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground shadow-sm transition-all hover:bg-primary/90 active:scale-95 disabled:opacity-30 disabled:cursor-not-allowed', iconSize: 14, }, ``` Also remove the comment about FloatingChat on line 52: ```ts // Re-init when the cache key changes (context switches in FloatingChat). ``` Replace with a neutral comment or just remove the comment line. - [ ] **Step 4: Fix TaskBriefChat.tsx** Open `src/renderer/components/brief/TaskBriefChat.tsx`. At line ~174–184, change: ```ts chatMutation.mutate( { requestId, message: trimmed, conversationHistory, sessionId, mode: 'floating', scope: { type: 'task', id: taskId }, briefMode: true, briefingContext: briefingText || undefined, }, ``` to: ```ts chatMutation.mutate( { requestId, message: trimmed, conversationHistory, sessionId, mode: 'contextual', scope: { type: 'task', id: taskId }, briefMode: true, briefingContext: briefingText || undefined, }, ``` - [ ] **Step 5: Type check** ```bash source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -i "error" | head -30 ``` Expected: zero errors. If tsc reports that `'floating'` is no longer valid for some type (from M6.4 not yet done), note it — the router schema still allows `'floating'` until M6.4. If tsc reports that `useAIChat` call sites are broken, check them: ```bash grep -rn "useAIChat" src/renderer/ ``` All call sites should be passing only `type: 'global'` or `type: 'project'` contexts after FloatingChat.tsx was deleted. - [ ] **Step 6: Commit** ```bash git add src/renderer/hooks/useAIChat.ts \ src/renderer/hooks/useChatStream.ts \ src/renderer/components/ai/ChatInputBox.tsx \ src/renderer/components/brief/TaskBriefChat.tsx git commit -m "$(cat <<'EOF' refactor(contextual): drop 'floating' branch from useAIChat and useChatStream UIChatContext is now 'global' | 'project' only. Floating domain signal, scope field, and onDomainSignal callback removed. ChatInputBox no longer defines floating variant. TaskBriefChat migrated to contextual mode. EOF )" ``` --- ## Task M6.4 — Main process: drop `sendFloatingRequest`, `orchestrateFloating`, floating mode **Files:** - Modify: `src/main/api/backend-client.ts` - Modify: `src/main/ai/orchestrator.ts` - Modify: `src/main/router/index.ts` - Modify: `src/preload/trpc.ts` - Modify: `src/renderer/lib/ipcLink.ts` - Modify: `src/shared/api-types.ts` ### Context `backend-client.ts` has `sendFloatingRequest` (lines 398–458) and a `floating_domain` case in its WS message handler (lines 1060–1063). `orchestrator.ts` has `OrchestrateFloatingInput` interface (lines 40–49) and `orchestrateFloating` function (lines 142–173). It also imports `WsFloatingRequest` from shared types. `router/index.ts` imports `orchestrateFloating` (line 16) and uses it at lines 950–960. The `mode` enum must change from `z.enum(['home', 'floating', 'contextual'])` to `z.enum(['contextual'])`. `preload/trpc.ts` defines the `V3StreamEvent` union which includes `floating_domain` (lines 29–42). `renderer/lib/ipcLink.ts` defines a duplicate `V3StreamEvent` type which also includes `floating_domain` (lines 16–33). `shared/api-types.ts` defines `WsFloatingDomainSchema` (lines 217–228) and `WsFloatingDomain` type. Check also whether `WsFloatingRequest` type is still needed by anything after removing `orchestrateFloating`: ```bash grep -rn "WsFloatingRequest\|WsFloatingDomain" src/ ``` ### Steps - [ ] **Step 1: Delete sendFloatingRequest from backend-client.ts** Open `src/main/api/backend-client.ts`. Delete the entire `sendFloatingRequest` method (lines ~394–458), from the JSDoc comment through the closing `}`. Also delete the `floating_domain` case in the WS message handler (lines ~1060–1063): ```ts case 'floating_domain': { const listener = this.streamListeners.get(frame.data.requestId); listener?.onDomain(frame.data.domain); break; } ``` Also check whether `onDomain` is still used in `StreamListener` type or elsewhere. If `onDomain` callback is only referenced by `sendFloatingRequest` and the `floating_domain` case, delete the `onDomain` field from `StreamListener` too (search for `onDomain` to confirm all uses). - [ ] **Step 2: Delete orchestrateFloating from orchestrator.ts** Open `src/main/ai/orchestrator.ts`. Delete lines 40–49 (`OrchestrateFloatingInput` interface): ```ts interface OrchestrateFloatingInput { message: string; requestId?: string; sessionId?: string; scope: WsFloatingRequest['scope']; conversationHistory?: WsFloatingRequest['conversationHistory']; briefMode?: boolean; briefingContext?: string; sender?: Electron.WebContents; } ``` Delete lines 1–16 of the import section's `WsFloatingRequest` import. Change: ```ts import type { WsFloatingRequest } from '../../shared/api-types'; ``` to nothing (remove the line entirely), since `WsFloatingRequest` is only used by `OrchestrateFloatingInput`. Delete the entire `orchestrateFloating` function (lines 142–173): ```ts export async function orchestrateFloating(input: OrchestrateFloatingInput): Promise { ... } ``` Update the docstring at the top of the file (lines 1–10) — remove the reference to `sendFloatingRequest()`: ``` * 2. Delegates to BackendClient.sendHomeRequest() / sendFloatingRequest() ``` becomes: ``` * 2. Delegates to BackendClient.sendHomeRequest() / sendContextualRequest() ``` - [ ] **Step 3: Update router/index.ts** Open `src/main/router/index.ts`. Change line 16 — remove `orchestrateFloating` from the import: ```ts import { orchestrate, orchestrateFloating, orchestrateContextual, orchestrateTaskBriefResearch, dailyBrief, getCachedBrief, invalidateBriefCache } from '../ai/orchestrator'; ``` becomes: ```ts import { orchestrate, orchestrateContextual, orchestrateTaskBriefResearch, dailyBrief, getCachedBrief, invalidateBriefCache } from '../ai/orchestrator'; ``` Change line 933 — update the `mode` enum: ```ts mode: z.enum(['home', 'floating', 'contextual']).optional(), ``` becomes: ```ts mode: z.enum(['contextual']).optional(), ``` Delete the floating branch in the mutation handler (lines 950–960): ```ts if (input.mode === 'floating' && input.scope) { return await orchestrateFloating({ message: input.message, requestId: input.requestId, sessionId: input.sessionId, scope: input.scope as Parameters[0]['scope'], conversationHistory: input.conversationHistory, briefMode: input.briefMode, briefingContext: input.briefingContext, sender: ctx.sender, }); } ``` The resulting mutation handler should flow: `if contextual → orchestrateContextual`, else `→ orchestrate`. - [ ] **Step 4: Update preload/trpc.ts** Open `src/preload/trpc.ts`. Remove the `floating_domain` case from the `V3StreamEvent` union (lines 29–42): ```ts | { type: 'floating_domain'; requestId: string; domain: | 'tasks' | 'notes' | 'timelines' | 'projects' | { type: 'task' | 'timeline' | 'project' | 'note' | 'node'; id?: string | null; section?: 'task' | 'timeline' | 'note' | null; }; }; ``` The `V3StreamEvent` union becomes: ```ts type V3StreamEvent = | { type: 'stream_start'; requestId: string } | { type: 'stream_text'; requestId: string; chunk: string } | { type: 'stream_end'; requestId: string; mutations?: unknown[] }; ``` - [ ] **Step 5: Update renderer/lib/ipcLink.ts** Open `src/renderer/lib/ipcLink.ts`. Remove the `floating_domain` case from the `V3StreamEvent` union (lines 16–33) the same way as done in preload. The type becomes: ```ts type V3StreamEvent = | { type: 'stream_start'; requestId: string } | { type: 'stream_text'; requestId: string; chunk: string } | { type: 'stream_end'; requestId: string; mutations?: unknown[] }; ``` - [ ] **Step 6: Update shared/api-types.ts** Open `src/shared/api-types.ts`. Check whether `WsFloatingDomainSchema` / `WsFloatingDomain` are imported anywhere: ```bash grep -rn "WsFloatingDomain\|WsFloatingRequest" src/ ``` If `WsFloatingRequest` is imported only by the now-deleted orchestrator import, and `WsFloatingDomain` is imported nowhere, delete both from `shared/api-types.ts`: - Delete `WsFloatingDomainSchema` Zod object (lines 217–228) - Delete `export type WsFloatingDomain = z.infer;` (line 229) - If `WsFloatingRequest` type/schema exists, delete it too (search for it in the file) - [ ] **Step 7: Sweep for any remaining floating references in main** ```bash grep -rn "floating\|Floating" src/main/ src/preload/ | grep -v node_modules | grep -v "\.md:" ``` Any remaining references to floating in these directories should be removed. Common residuals: - `onDomain` callback type in `StreamListener` (if `sendFloatingRequest` was its only consumer) - Stale comments in `backend-client.ts` mentioning floating - [ ] **Step 8: Type check** ```bash source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -i "error" | head -30 ``` Expected: zero errors. The `TaskBriefChat.tsx` now sends `mode: 'contextual'` which is valid per the updated schema. - [ ] **Step 9: Commit** ```bash git add src/main/api/backend-client.ts \ src/main/ai/orchestrator.ts \ src/main/router/index.ts \ src/preload/trpc.ts \ src/renderer/lib/ipcLink.ts \ src/shared/api-types.ts git commit -m "$(cat <<'EOF' refactor(contextual): main process drops sendFloatingRequest and floating mode ai.chat tRPC procedure now accepts mode='contextual' (or unset for home). Orchestrator loses the floating delegation branch. Backend client method and WsFloatingDomain shared type removed. EOF )" ``` --- ## Task M6.8 — Sweep electron-store and localStorage `'floating'` keys **Files:** - Inspect: `src/main/store.ts` - Inspect: `src/renderer/` (any localStorage usage) ### Context `src/main/store.ts` defines the electron-store schema — no `floating.*` keys exist in the current schema (verified: `AppSettings` has `sidebarCollapsed`, `encryptedTokens`, `backendUrl`, `deviceId`, `dailyBriefCache`, `localAgents`, `formatPrefs`, `uiLanguage`, `timelineZoom`). No floating keys. This task is a verification sweep. If nothing is found, the commit is skipped. ### Steps - [ ] **Step 1: Search for any floating.* key usage** ```bash grep -rn "floating\." src/main/ src/preload/ src/renderer/ | grep -v node_modules | grep -v "\.md:" ``` Also check localStorage: ```bash grep -rn "localStorage" src/renderer/ | grep -v node_modules | grep "float" ``` - [ ] **Step 2: Decision point** If Step 1 returns NO results (or only results already cleaned by M6.1–M6.4), this task is a no-op. Skip the commit and note in the final report: "M6.8: no floating.* store or localStorage keys found — no commit needed." If Step 1 returns results with actual floating key reads/writes in store.ts or localStorage calls: - Delete the key from `AppSettings` interface and the `defaults` object in `store.ts` - Delete any `localStorage.getItem('floating.*')` or `setItem('floating.*', ...)` calls - [ ] **Step 3: Conditional commit** Only run this if changes were made in Step 2: ```bash git add -A git status --short # confirm what's staged git commit -m "$(cat <<'EOF' chore(contextual): purge residual 'floating' keys from store and renderer EOF )" ``` --- ## Final Self-Review Checklist After all commits, run these verification checks and paste the output into your report: - [ ] **FloatingChat imports gone** ```bash grep -rn "FloatingChat\|useFloatingChat\|FloatingChatProvider\|useDoubleClickAI" src/renderer/ ``` Expected: no output. - [ ] **data-ai-section gone** ```bash grep -rn "data-ai-section" src/renderer/ ``` Expected: no output. - [ ] **floating string in renderer hooks** ```bash grep -rn "'floating'" src/renderer/hooks/ ``` Expected: no output (sidebar.tsx uses `"floating"` as a layout variant — that is unrelated and harmless). - [ ] **UIChatContext type** ```bash grep -n "type:" src/renderer/hooks/useAIChat.ts | head -5 ``` Expected: `'global' | 'project'` only. - [ ] **tRPC schema** ```bash grep -n "floating" src/main/router/index.ts ``` Expected: no output. - [ ] **sendFloatingRequest gone** ```bash grep -rn "sendFloatingRequest\|orchestrateFloating" src/main/ ``` Expected: no output. - [ ] **Final tsc** ```bash source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -c "error TS" ``` Expected: `0`