diff --git a/docs/navigation-audit.md b/docs/navigation-audit.md
index b13876ec..9720dd82 100644
--- a/docs/navigation-audit.md
+++ b/docs/navigation-audit.md
@@ -83,7 +83,7 @@ are good building blocks. The **Workspace/Panel framework** contains **zero** ro
| ~~M4~~ | ~~`FileBrowser/.../useFileBrowserApp.ts:269`~~, `FileItem.tsx:516`, ~~`Breadcrumb.tsx:16`~~ | a folder | `/files?path=
` | **Partly done — the rest is an owner decision, not a defect.** `currentPath` is `?path=` on `/files`, so back/forward and linking a folder work, and the crumbs are ` `s. Two things the audit line did not know: `?view=` is *ephemeral* (wiped on mount by `useFileViewerPanels`), so `path` is the screen's first durable param, and four `setSearchParams({…})` calls replaced the whole query string — opening any file would have silently reset the folder. They go through a `setViewerParams` helper now that keeps `path`. Opt-in via the parsed `WorkspaceIdentity` (`screens/files`), because a dashboard can hold two browsers and one shared param would move both. **Folder *items* stay buttons:** ⌘/Ctrl/Shift-click is already bound to multi-select in `FileItem.tsx` and open is double-click, so anchor semantics collide with an existing gesture. |
| ~~M5~~ | ~~`CodeEditor/FileTree.tsx:59`, `EditorTabs.tsx:33`~~ | open source file / active tab | `/code-editor?file=` | **Done, minus `open=`.** The active file is `?file=`; tree *file* rows and tabs are ` `s. **The tab set stays local** — it is a working session, not an address: it grows without bound, each entry costs a read on load, and nobody links someone else to a tab bar. A `?file=` naming a file that is not open now *opens* it, which is what makes a pasted link work; a path that fails to read is remembered so a bad link errors once instead of once per render, and the address is left alone rather than rewritten. **Tree folder rows stay buttons** — unlike the M4 case this needs no owner call, because expanding a directory is disclosure, not navigation. Two things fixed in passing: the tab close control was a `role="button"` span *nested inside* the tab (invalid then, a nested interactive inside an anchor now) and is a sibling `` with an `aria-label`; and `closeFile` computed the next-active file *inside* a `setFiles` updater, which is exactly the impurity React double-invokes to catch. |
| ~~M6~~ | `Settings/SettingsPanel.tsx` | a settings sub-section | `/settings/:page/:section` | **Done.** `` + `useParams`, five `*_SELECTED` globals gone, one `SettingsRoute` guard per page. The "one change covers all settings pages" claim was *almost* right: Integrations builds its own sidebar and did not go through `createSettingsPanelComponents`, and it also held the Enterprise/Personal tab in a second global — derived from the section key now, which is what fixes deep-linking a Personal section. |
-| M7 | `workspaces/components/Combobox.tsx:53` | caller-supplied route | (existing) | shared widget: `href`-bearing options do `navigate(option.href)`; render them as ` `. Every caller inherits the opaque click. |
+| ~~M7~~ | ~~`workspaces/components/Combobox.tsx:53`~~ | caller-supplied route | — | **Deleted, not fixed.** "Every caller inherits the opaque click" was the reason this ranked MEDIUM, and it is wrong: `Combobox` has **no callers**. Nothing has imported it since the initial commit, there is no barrel export, and nothing anywhere sets `href` on a `SelectOption` — so the navigate, the separator that only showed for `href` options, and the `href` field on both declarations of the type were all unreachable. Writing anchor semantics into a component that is never rendered is building, not fixing. Its `Command` primitives stay; `AIHarnessesSection` uses them. |
| ~~M8~~ | `Layout/Header/UserMenu.tsx` | — | — | **Done.** Removed rather than routed: nothing had ever been built behind `/settings/resources`, so the item was a bounce to `/` dressed as navigation. Its `header.userMenu.resources` locale keys went with it. |
| M9 | `Screens/Dashboard/Plans/index.tsx:36` | a plan document | `/plans/:name` | native `` → local state; a plan is a real addressable doc. Have the select `navigate()` or use a link list. |
| ~~M10~~ | `SystemMonitor/ScopeList.tsx` | monitor scope (btop/pm2/docker) | `/system-monitor/:scope` | **Done.** Route pair + `Navigate` guard; the scope buttons are `NavLink`s and `useMonitorScope` reads `useParams` instead of the channel. The Dock's hand-rolled `isActive` is a `startsWith`, so its highlight survives the redirect. |
@@ -175,7 +175,7 @@ publishers means changing the chat panel, which is another agent's, so it is wri
| 9 | `Projects/ProjectPreview.tsx:457` | open project | `/projects/:id` | YES | verify vs H3 |
| 10 | `Jobs/JobDetail.tsx:709` | back button | `/jobs` | YES | use `BackButton`/` ` |
| 11 | `ChatHistory/SessionList.tsx:70` | "New Chat" | `/chat/new` | ~ | borderline (LOW) |
-| 12 | `components/Combobox.tsx:54` | nav combobox option | `option.href` | YES | M7 |
+| ~~12~~ | ~~`components/Combobox.tsx:54`~~ | nav combobox option | `option.href` | — | M7 — file deleted, it had no callers |
| ~~13~~ | `MusicPlayer/MusicPlayerHost.tsx:251` | open current album (setCwd+nav) | `/music` | ~ | **Done** — the side-effect was the `setCwd`; with the location in the URL the tile is a plain ` ` to `musicPath(albumRel)`. |
| 14 | `FileBrowserWidget/useFileBrowserWidget.ts:53` | open folder in Files | `/files?view=…` | ~ | query nav |
| 15 | `FileBrowserApp/useFileBrowserApp.ts:401` | create dashboard from folder | `/dashboards/new?…` | ~ | action-nav |
@@ -219,7 +219,7 @@ publishers means changing the chat panel, which is another agent's, so it is wri
- [x] **M3** Activity → `/activity/:id`; the `{label, query}` selection object is gone — the id is the URL and the stream query is derived from the registry row. `/activity` also had no `usePageTitle` rule (it read "Officer"); added. **Needs runtime test.**
- [x] **M4** FileBrowser folders → `/files?path=`; breadcrumbs are ` `s. Folder *rows* deliberately still buttons — ⌘-click is multi-select, open is double-click; converting them needs an owner call on the gesture.
- [x] **M5** CodeEditor active file → `/code-editor?file=`; tree file rows and tabs are ` `s. The open-tab *set* stays local state, on purpose — see the findings row.
-- [ ] **M7** Combobox: render `href` options as ` ` (`Combobox.tsx:53`).
+- [x] **M7** Combobox — **deleted instead**. Zero callers since the initial commit; `href` on `SelectOption` was never set by anything, so the whole branch was unreachable.
### Phase 3 — Whole-workspace routing decisions (needs a design call first)
- [x] **Music** — `/music?path=`; `music:cwd` deleted; every drill-in (including the dock's now-playing tile, navigate-site 13) is a ` `. `music:favorites` and `music:resync` stay — a view toggle and a refresh signal. **Needs runtime test.**
diff --git a/src/apps/CLAUDE.md b/src/apps/CLAUDE.md
index 4c8945a2..7d18ea13 100644
--- a/src/apps/CLAUDE.md
+++ b/src/apps/CLAUDE.md
@@ -19,7 +19,6 @@ Location: `src/workspaces/components/`
Built on top of shadcn primitives:
- `Avatar.tsx` - User avatars
- `Card.tsx` - Custom card wrapper
-- `Combobox.tsx` - Searchable select
- `ColorPicker.tsx` - Color selection
- `DataTable/` - Table with sorting, filtering, pagination (see below)
- `Dialogs/` - Common dialog patterns
diff --git a/src/workspaces/components/Combobox.tsx b/src/workspaces/components/Combobox.tsx
deleted file mode 100644
index 4a55d26b..00000000
--- a/src/workspaces/components/Combobox.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import type { SelectOption } from 'types';
-import * as React from 'react';
-import { useNavigate } from 'react-router';
-import { cn } from 'helpers/cn';
-import { Check, ChevronsUpDown } from 'lucide-react';
-import { Button } from '@/components/ui/button';
-import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '@/components/ui/command';
-import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
-import { Separator } from '@/components/ui/separator';
-
-type ComboboxProps = {
- options: SelectOption[];
- value: string | number;
- onChange: (value: T) => void;
- placeholder?: string;
- className?: string;
-};
-export const Combobox = ({
- options,
- value,
- onChange,
- placeholder = 'Select...',
- className,
-}: ComboboxProps) => {
- const navigate = useNavigate();
- const [open, setOpen] = React.useState(false);
-
- return (
-
-
-
-
- {value ? options?.find((option) => option.value === value)?.label : placeholder}
-
-
-
-
-
-
-
-
-
- {options?.map((option) => (
-
- {
- if (option.href) {
- return navigate(option.href);
- }
- const newValue = options.find(
- (option) => option.label?.toLowerCase() === currentValue.toLowerCase(),
- )?.value;
- if (value && value !== newValue) {
- const setValue = typeof value === 'number' ? Number(newValue) : '' + newValue;
- onChange(setValue as T);
- }
- setOpen(false);
- }}
- >
-
- {option.label}
-
- {option.href && }
-
- ))}
-
-
-
-
- );
-};
diff --git a/src/workspaces/types/globals.d.ts b/src/workspaces/types/globals.d.ts
index 60bb51dc..cc958c32 100644
--- a/src/workspaces/types/globals.d.ts
+++ b/src/workspaces/types/globals.d.ts
@@ -1,5 +1,5 @@
declare global {
- type SelectOption = { value: number | string; label?: string; href?: string; hidden?: boolean };
+ type SelectOption = { value: number | string; label?: string; hidden?: boolean };
type StateSetter = React.Dispatch>;
diff --git a/src/workspaces/types/index.ts b/src/workspaces/types/index.ts
index 7e164ea2..7cc484a2 100644
--- a/src/workspaces/types/index.ts
+++ b/src/workspaces/types/index.ts
@@ -4,6 +4,6 @@ export * from 'officerdb/types';
export type { Job, JobStep, JobStatus, JobStepStatus, JobProgress } from './queue';
export type { EmailSummary, EmailMessage, EmailThread } from './email';
-export type SelectOption = { value: number | string; label?: string; href?: string };
+export type SelectOption = { value: number | string; label?: string };
export type StateSetter = React.Dispatch>;