# 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 `<FloatingChatProvider>` (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 (
    <FloatingChatProvider>
      <ExpandedClientsProvider>
        <TaskBriefingProvider>
          <HeaderProvider>
            <div className="flex w-full h-full">
              <AppShellInner>{children}</AppShellInner>
            </div>
          </HeaderProvider>
        </TaskBriefingProvider>
      </ExpandedClientsProvider>
    </FloatingChatProvider>
  );
}
```
to:
```tsx
export function AppShell({ children }: AppShellProps) {
  return (
    <ExpandedClientsProvider>
      <TaskBriefingProvider>
        <HeaderProvider>
          <div className="flex w-full h-full">
            <AppShellInner>{children}</AppShellInner>
          </div>
        </HeaderProvider>
      </TaskBriefingProvider>
    </ExpandedClientsProvider>
  );
}
```

In `AppShellInner` (around line 166), remove:
```ts
useDoubleClickAI();
```

Search for `<FloatingChatPortal />` 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<HTMLDivElement>(null);
const listRef = useRef<HTMLDivElement>(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
<div
  ref={summaryRef}
  data-ai-section="project-summary"
  className="..."
>
```
Change to:
```tsx
<div
  ref={summaryRef}
  className="..."
>
```

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 `<ScrollArea>` at line ~291:
```tsx
<ScrollArea ref={editorRef} data-ai-section="note-editor" className="flex-1 min-h-0">
```
Change to:
```tsx
<ScrollArea ref={editorRef} className="flex-1 min-h-0">
```

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<OrchestrateResult> {
  ...
}
```

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<typeof orchestrateFloating>[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<typeof WsFloatingDomainSchema>;` (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`
