Files
workspace/docs/superpowers/plans/2026-05-15-floating-chat-deprecation-sweep.md
2026-05-15 22:30:34 +02:00

31 KiB
Raw Blame History

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
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
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
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:

import { useDoubleClickAI } from '@/hooks/useDoubleClickAI';

Remove lines 7071:

import { FloatingChatPortal } from '@/components/ai/FloatingChat';
import { FloatingChatProvider } from '@/context/FloatingChatContext';

In AppShell (around line 149163), change:

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:

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:

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:

import { useFloatingChat } from '@/context/FloatingChatContext';

Remove line 63:

const { registerSection, unregisterSection } = useFloatingChat();

Remove the useEffect block that calls registerSection/unregisterSection (lines ~91101):

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:

import { useFloatingChat } from '@/context/FloatingChatContext';

Remove line 95:

const { registerSection, unregisterSection } = useFloatingChat();

Remove the useEffect that calls registerSection/unregisterSection (lines ~107110):

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:

import { useFloatingChat } from '@/context/FloatingChatContext';

Remove line 117:

const { registerSection, unregisterSection } = useFloatingChat();

Remove the useEffect that calls registerSection/unregisterSection (lines ~119128):

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:

import { useFloatingChat } from '@/context/FloatingChatContext';

Remove line 18:

const { registerSection, unregisterSection } = useFloatingChat();

Remove the useEffect that calls registerSection/unregisterSection (lines ~2027):

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):

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
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
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:538data-ai-section="project-summary"
  • ProjectDetail.tsx:620data-ai-section="project-tasks"
  • ProjectDetail.tsx:631data-ai-section="project-notes"
  • TimelineGanttView.tsx:239data-ai-section={sectionId}
  • notes.$noteId.tsx:291data-ai-section="note-editor"
  • tasks.tsx:42data-ai-section="tasks-overview"
  • tasks.tsx:61data-ai-section="tasks-list"

Steps

  • Step 1: List all occurrences
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:

<div
  ref={summaryRef}
  data-ai-section="project-summary"
  className="..."
>

Change to:

<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:

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:

<ScrollArea ref={editorRef} data-ai-section="note-editor" className="flex-1 min-h-0">

Change to:

<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
grep -rn "data-ai-section" src/renderer/
echo "exit=$?"

Expected: no output, exit=1 (grep found nothing).

  • Step 7: Type check + commit
source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -i "error" | head -20

Expected: zero errors.

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 413 (the FloatingDomainSignal type):

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 2331):

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:

export interface UIChatContext {
  type: 'global' | 'project';
  projectId?: string;
}

Delete the UseAIChatOptions interface (lines 5052):

interface UseAIChatOptions {
  onDomainSignal?: (domain: FloatingDomainSignal) => void;
}

Update the useAIChat function signature — remove the options parameter:

export function useAIChat(defaultContext: UIChatContext, options?: UseAIChatOptions): UseAIChatReturn {

becomes:

export function useAIChat(defaultContext: UIChatContext): UseAIChatReturn {

Update getContextCacheKey — replace the entire function body:

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:

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:

const contextCacheKey = useMemo(
  () => getContextCacheKey(defaultContext),
  [defaultContext.type, defaultContext.projectId, defaultContext.scope?.type, defaultContext.scope?.id],
);

becomes:

const contextCacheKey = useMemo(
  () => getContextCacheKey(defaultContext),
  [defaultContext.type, defaultContext.projectId],
);

Remove onDomainSignalRef (lines 151152):

const onDomainSignalRef = useRef(options?.onDomainSignal);
onDomainSignalRef.current = options?.onDomainSignal;

Remove the floating_domain case from the switch (event.type) block (lines 237239):

case 'floating_domain':
  onDomainSignalRef.current?.(event.domain);
  break;

Remove isFloating and the conditional spread in handleSend (lines 249259):

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:

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 1718):

  /** 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):

  onDomainSignal,

Remove domainRef declarations (lines 3334):

  const domainRef = useRef(onDomainSignal);
  domainRef.current = onDomainSignal;

Remove the floating_domain case from the switch block (lines 7072):

          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 3035) should be removed since no live code passes variant="floating" any more.

Change:

type ChatInputBoxVariant = 'panel' | 'floating' | 'comment';

to:

type ChatInputBoxVariant = 'panel' | 'comment';

Remove the floating entry from VARIANT_STYLES (lines 3035):

  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:

    // 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 ~174184, change:

    chatMutation.mutate(
      {
        requestId,
        message: trimmed,
        conversationHistory,
        sessionId,
        mode: 'floating',
        scope: { type: 'task', id: taskId },
        briefMode: true,
        briefingContext: briefingText || undefined,
      },

to:

    chatMutation.mutate(
      {
        requestId,
        message: trimmed,
        conversationHistory,
        sessionId,
        mode: 'contextual',
        scope: { type: 'task', id: taskId },
        briefMode: true,
        briefingContext: briefingText || undefined,
      },
  • Step 5: Type check
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:

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
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 398458) and a floating_domain case in its WS message handler (lines 10601063).

orchestrator.ts has OrchestrateFloatingInput interface (lines 4049) and orchestrateFloating function (lines 142173). It also imports WsFloatingRequest from shared types.

router/index.ts imports orchestrateFloating (line 16) and uses it at lines 950960. 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 2942).

renderer/lib/ipcLink.ts defines a duplicate V3StreamEvent type which also includes floating_domain (lines 1633).

shared/api-types.ts defines WsFloatingDomainSchema (lines 217228) and WsFloatingDomain type.

Check also whether WsFloatingRequest type is still needed by anything after removing orchestrateFloating:

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 ~394458), from the JSDoc comment through the closing }.

Also delete the floating_domain case in the WS message handler (lines ~10601063):

        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 4049 (OrchestrateFloatingInput interface):

interface OrchestrateFloatingInput {
  message: string;
  requestId?: string;
  sessionId?: string;
  scope: WsFloatingRequest['scope'];
  conversationHistory?: WsFloatingRequest['conversationHistory'];
  briefMode?: boolean;
  briefingContext?: string;
  sender?: Electron.WebContents;
}

Delete lines 116 of the import section's WsFloatingRequest import. Change:

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 142173):

export async function orchestrateFloating(input: OrchestrateFloatingInput): Promise<OrchestrateResult> {
  ...
}

Update the docstring at the top of the file (lines 110) — 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:

import { orchestrate, orchestrateFloating, orchestrateContextual, orchestrateTaskBriefResearch, dailyBrief, getCachedBrief, invalidateBriefCache } from '../ai/orchestrator';

becomes:

import { orchestrate, orchestrateContextual, orchestrateTaskBriefResearch, dailyBrief, getCachedBrief, invalidateBriefCache } from '../ai/orchestrator';

Change line 933 — update the mode enum:

      mode: z.enum(['home', 'floating', 'contextual']).optional(),

becomes:

      mode: z.enum(['contextual']).optional(),

Delete the floating branch in the mutation handler (lines 950960):

        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 2942):

  | {
      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:

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 1633) the same way as done in preload. The type becomes:

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:

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 217228)

  • 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

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

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
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*
grep -rn "floating\." src/main/ src/preload/ src/renderer/ | grep -v node_modules | grep -v "\.md:"

Also check localStorage:

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.1M6.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:

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
grep -rn "FloatingChat\|useFloatingChat\|FloatingChatProvider\|useDoubleClickAI" src/renderer/

Expected: no output.

  • data-ai-section gone
grep -rn "data-ai-section" src/renderer/

Expected: no output.

  • floating string in renderer hooks
grep -rn "'floating'" src/renderer/hooks/

Expected: no output (sidebar.tsx uses "floating" as a layout variant — that is unrelated and harmless).

  • UIChatContext type
grep -n "type:" src/renderer/hooks/useAIChat.ts | head -5

Expected: 'global' | 'project' only.

  • tRPC schema
grep -n "floating" src/main/router/index.ts

Expected: no output.

  • sendFloatingRequest gone
grep -rn "sendFloatingRequest\|orchestrateFloating" src/main/

Expected: no output.

  • Final tsc
source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -c "error TS"

Expected: 0