+ {group.rows.map((row) => (
+
+ ))}
+
+ );
+ }
+ ```
+
+ (Uses a `div`, not ``, to avoid inheriting the global `header` rule in `style.css`.) This will not compile until Task 4 drops `project` from `TaskRow` Props โ run the test after Task 4.
+
+## Task 4: TaskRow โ three actions + reordered body
+
+**Files:** Modify `apps/workbranch-companion/src/ui/TaskRow.tsx`, `apps/workbranch-companion/tests/task-row.test.tsx`
+
+> Decision resolved: fully remove memo/clear/noti/copy from the companion presentation command surface. Do not leave hidden action kinds or labels behind.
+
+- [x] **Step 1 (RED):** In `task-row.test.tsx`, import `taskActionsFor`, drop `project="workbranch"` from all four `` / `TaskRow({...})` usages, and rewrite the actions test:
+
+ ```ts
+ it("exposes only IDE, Terminal, and Finder actions", () => {
+ const html = renderToStaticMarkup(
+ {}} />,
+ );
+ expect(html).toContain('aria-label="open generated-task in IDE"');
+ expect(html).toContain('aria-label="open generated-task in terminal"');
+ expect(html).toContain('aria-label="open generated-task in Finder"');
+ expect(html).not.toContain("edit memo for generated-task");
+ expect(html).not.toContain("clear memo for generated-task");
+ expect(html).not.toContain("clear notifications for generated-task");
+ expect(html).not.toContain("copy path for generated-task");
+ expect(taskActionsFor(nestedChecklistTask).map((action) => action.label)).toEqual([
+ "IDE",
+ "Terminal",
+ "Finder",
+ ]);
+ });
+ ```
+
+ (The other three tests stay valid after the prop drop: the Raycast-summary test still finds `workbranch`/`feat/update-0617` via the repo chip, and the click test still finds the IDE button.)
+
+- [x] **Step 2 (GREEN):** In `TaskRow.tsx`:
+ - Replace `TASK_ACTION_KINDS` with `const TASK_ACTION_KINDS = ["ide", "terminal", "finder"] as const;` so `TaskActionKind` is only the three remaining actions.
+ - Remove `memoEdit`, `memoClear`, `notiClear`, and `copyPath` from `TASK_ACTION_LABELS` and `actionAriaLabel`.
+ - Simplify `taskActionsFor`:
+
+ ```ts
+ export function taskActionsFor(task: Task): readonly TaskRowAction[] {
+ return TASK_ACTION_KINDS.map((kind) => ({
+ kind,
+ label: TASK_ACTION_LABELS[kind],
+ ariaLabel: actionAriaLabel(kind, task.name),
+ disabled: false,
+ }));
+ }
+ ```
+ - Add/adjust a direct action-order assertion, e.g. `expect(taskActionsFor(nestedChecklistTask).map((action) => action.label)).toEqual(["IDE", "Terminal", "Finder"]);`.
+ - Drop `project` from `Props` and the destructure; delete the `
{project}
` line.
+ - Reorder the `.task-detail` children to **repo chips โ current step โ steps โ actions**:
+
+ ```tsx
+
+
+ {plan && now ? : null}
+ {plan ? (
+
+
+
+ ) : null}
+
+ {actions.map((action) => (
+
+ ))}
+
+
+ ```
+
+- [x] **Step 3:** Run `pnpm --filter @workbranch/companion test -- tests/task-row.test.tsx tests/project-group.test.tsx` โ expect PASS.
+
+## Task 5: App shell โ header summary + grouped rendering
+
+**Files:** Modify `apps/workbranch-companion/src/App.tsx`, `apps/workbranch-companion/src/infrastructure/tauriClient.ts`
+
+- [x] Update imports: `import { buildMenuModel, type MenuModel, type MenuSummary } from "./application/state";`, add `import { ProjectGroup } from "./ui/ProjectGroup";`, and reduce the TaskRow import to `import type { TaskActionKind } from "./ui/TaskRow";` (App no longer renders `TaskRow` directly; `commandForTaskAction`/`handleTaskAction` still use the type).
+- [x] Narrow `commandForTaskAction` to the three remaining `TaskActionKind` cases: `ide`, `terminal`, and `finder`; remove the `window.prompt` memo path and deleted memo/notification/copy cases.
+- [x] In `tauriClient.ts`, narrow `CompanionCommand` to `{ kind: "finder" | "ide" | "terminal"; task: string }` variants only. Do not touch Rust command handling in this slice.
+- [x] Add an inline header summary helper:
+
+ ```tsx
+ function plural(count: number, word: string): string {
+ return count === 1 ? word : `${word}s`;
+ }
+
+ function AppSummary({ summary }: { readonly summary: MenuSummary }) {
+ const { projectCount, taskCount, active, blocked, notifications } = summary;
+ return (
+
+ );
+ }
+ ```
+
+- [x] Replace the header and body JSX:
+
+ ```tsx
+
+
+
+
+
+ {model.groups.length === 0 ? (
+
No workbranch tasks registered.
+ ) : null}
+ {model.groups.map((group) => (
+ void handleTaskAction(root, task, kind)}
+ />
+ ))}
+
+ ```
+
+## Task 6: CSS โ header summary, project grouping, row order
+
+**Files:** Modify `apps/workbranch-companion/src/style.css`
+
+- [x] Change the `.project-line, footer { ... }` rule (`:216`) to `footer { ... }` only (delete `.project-line`).
+- [x] Optionally reduce `.task-name` `font-size` from `13px` to `12px` so the project header reads as the larger element.
+- [x] Append:
+
+ ```css
+ .app-summary {
+ align-items: baseline;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ min-width: 0;
+ }
+
+ .app-inventory {
+ color: var(--muted);
+ font-size: 12px;
+ font-weight: 600;
+ }
+
+ .app-badges {
+ display: inline-flex;
+ gap: 6px;
+ }
+
+ .badge {
+ border: 1px solid var(--line);
+ border-radius: 999px;
+ font-size: 11px;
+ font-weight: 600;
+ line-height: 1;
+ padding: 3px 7px;
+ }
+
+ .badge-active {
+ background: var(--accent-soft);
+ border-color: rgba(124, 92, 255, 0.3);
+ color: var(--accent);
+ }
+
+ .badge-blocked {
+ background: rgba(255, 95, 112, 0.14);
+ border-color: rgba(255, 95, 112, 0.28);
+ color: var(--blocked);
+ }
+
+ .badge-noti {
+ background: rgba(245, 184, 75, 0.12);
+ border-color: rgba(245, 184, 75, 0.22);
+ color: var(--notify);
+ }
+
+ .project-group {
+ margin-bottom: 14px;
+ }
+
+ .project-group-header {
+ align-items: baseline;
+ border-left: 2px solid var(--accent);
+ display: flex;
+ gap: 8px;
+ margin-bottom: 8px;
+ padding-left: 8px;
+ }
+
+ .project-group-name {
+ color: var(--text);
+ font-size: 13px;
+ font-weight: 700;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .project-group-count {
+ color: var(--faint);
+ font-size: 11px;
+ white-space: nowrap;
+ }
+
+ .project-group .task {
+ margin-left: 8px;
+ }
+ ```
+
+## Task 7: Verification
+
+**Files:** Update `docs/plans/0036-companion-project-grouped-ui.md` (evidence), `../TASK-WORKBRANCH.md`
+
+- [x] Automated (from repo root):
+
+ ```bash
+ pnpm --filter @workbranch/companion test
+ pnpm --filter @workbranch/companion typecheck
+ pnpm --filter @workbranch/companion lint
+ pnpm --filter @workbranch/companion build
+ git diff --check
+ ```
+
+ Expected: Vitest green (incl. new `project-group.test.tsx` and updated `acl`/`task-row`), TypeScript clean, Biome exit 0 (pre-existing `parseContract.ts` info diagnostics may print but no new ones from changed files), `tsc && vite build` succeeds, whitespace clean.
+
+- [ ] Visual gate:
+
+ ```bash
+ pnpm --filter @workbranch/companion tauri dev
+ ```
+
+ Manual checks:
+ - Header shows `N projects ยท M tasks` + status badges (no `โ 0`); idle shows inventory only; zero tasks shows `No tasks`.
+ - Tasks are grouped under a project header that reads as the primary level; task rows are visibly subordinate (indent + accent bar).
+ - Within a row, order is task name โ branch chips โ plan/current step โ steps โ actions.
+ - Exactly three actions in order **IDE | Terminal | Finder**.
+ - Narrow-width wrapping and keyboard focus rings still work.
+
+ If this session cannot observe the rendered menu bar, record the gap and require a human visual check before release.
+
+- [x] Append a `## ๊ตฌํ ๊ฒฐ๊ณผ` section (files changed, tests pass/fail, visual result or gap, remaining risk) and set `TASK-WORKBRANCH.md` status to `done` (automated + visual confirmed) or `review` (code complete, visual pending).
+
+## Acceptance criteria
+
+- The popover groups tasks under a project header (`project ยท n tasks`); the project level reads as primary and task rows as subordinate.
+- The header shows inventory + status (`N projects ยท M tasks` + `โถ/โ /๐` badges when > 0), idle shows inventory only, zero tasks shows `No tasks`; the `โ 0` fallback is gone.
+- Each task body is ordered task name โ branch โ plan (current step + steps) โ actions.
+- Each task row shows exactly IDE, Terminal, Finder, in that order; memo edit/clear, notification clear, and copy-path are removed from the companion presentation command surface.
+- `buildMenuModel` returns `{ summary, groups, errors }`; `acl.test.ts`, `task-row.test.tsx`, and new `project-group.test.tsx` pass; typecheck, lint, build, and `git diff --check` pass.
+- CLI contract, domain model, ACL mapping, Rust ports, and runtime Tauri command implementation are unchanged; only the presentation-facing `CompanionCommand` union is narrowed to match the remaining UI actions.
+
+## Non-goals
+
+- Do not change `workbranch list --json` / `list --global --json` schemaVersion 1, the domain model, or `infrastructure/acl.ts` mapping.
+- Do not add task lifecycle mutations or new UI dependencies (Tailwind/shadcn/Radix).
+- Do not add replacement memo/notification/copy UI in this slice; deleted row actions are a deliberate product cut, not a relocation.
+- Do not modify the Rust side, activity store, or watcher.
+- Do not add settings/preferences, launch-at-login, font selection, or color theme selection in this slice; those are split into `docs/plans/0037-companion-settings-cli-theme.md`.
+
+## Self-review checklist for this plan
+
+- [x] Resolves the header, hierarchy, body-order, and action decisions from the design Q&A.
+- [x] Resolves the memo/noti/copy decision as fully removed from the companion presentation command surface.
+- [x] Locks model + new component semantics with tests before markup (TDD).
+- [x] Lists exact files, code, and the model consumers that must change together (`App.tsx`, `acl.test.ts`).
+- [x] Keeps CLI/contract/domain/Rust scope unchanged.
+- [x] Includes a visual/manual gate for a UI change.
+- [x] No TBD/TODO placeholders.
+- [x] Settings/preferences and CLI-like theme work split to 0037 so this plan stays focused on grouping/action hierarchy.
+
+## ๊ตฌํ ๊ฒฐ๊ณผ
+
+- ๋ณ๊ฒฝ ํ์ผ: `DESIGN.md`, companion `state.ts`/`App.tsx`/`TaskRow.tsx`/`ProjectGroup.tsx`/`style.css`/`tauriClient.ts`, `acl.test.ts`, `task-row.test.tsx`, `project-group.test.tsx`, `../TASK-WORKBRANCH.md`.
+- ๊ตฌํ ์๋ฃ: view model์ด `{ summary, groups, errors }`๋ก ๋ณ๊ฒฝ๋์๊ณ , ๋น ํ๋ก์ ํธ ํํฐ๋ง/ํ๋ก์ ํธ ์ต์ task ๊ธฐ์ค ์ ๋ ฌ/task row ์ต์ ์ ์ ๋ ฌ์ ํ ์คํธ๋ก ์ ๊ฐ๋ค.
+- ๊ตฌํ ์๋ฃ: popover header๋ inventory + status badge๋ฅผ ๋ ๋๋งํ๊ณ , body๋ `ProjectGroup` ์๋ `TaskRow`๋ฅผ ์ค์ฒฉ ๋ ๋๋งํ๋ค.
+- ๊ตฌํ ์๋ฃ: companion presentation action surface๋ `IDE | Terminal | Finder`๋ก ์ถ์ํ๊ณ , `memoEdit`/`memoClear`/`notiClear`/`copyPath` ๋ฐ TS `CompanionCommand` union์ ์ญ์ ๋์ variants๋ฅผ ์ ๊ฑฐํ๋ค. Rust runtime command implementation์ ๋ณ๊ฒฝํ์ง ์์๋ค.
+- ์๋ ๊ฒ์ฆ ํต๊ณผ:
+ - `pnpm --filter @workbranch/companion test` โ 7 files / 20 tests passed.
+ - `pnpm --filter @workbranch/companion typecheck` โ passed.
+ - `pnpm --filter @workbranch/companion lint` โ exit 0; ๊ธฐ์กด `parseContract.ts` `useLiteralKeys` info diagnostics ์ถ๋ ฅ๋จ.
+ - `pnpm --filter @workbranch/companion build` โ `tsc && vite build` passed.
+ - `git diff --check` โ passed.
+ - manual markdown trailing-whitespace check for `../TASK-WORKBRANCH.md`, `0036`, `0037` โ passed.
+- Visual gate update: installed Rust 1.95.0 and set a local rustup override for this checkout; `pnpm --filter @workbranch/companion tauri dev` built successfully and ran `target/debug/workbranch-companion`. Native menu-bar popover inspection is still not directly observed in this agent session because macOS Assistive Access and screencapture are unavailable. As partial visual evidence, Playwright drove the Vite surface with Tauri IPC mocks and verified inventory/status badges, project grouping, branch-before-plan order, IDE|Terminal|Finder-only actions, and no page errors. Screenshot: `/tmp/workbranch-companion-qa/grouped-ui.png`.
+- Remaining risk: native tray popover visual QA is not directly observed; keep task status at `review` until it is manually inspected in a GUI session with accessibility/screen capture available.
diff --git a/docs/plans/0037-companion-settings-cli-theme.md b/docs/plans/0037-companion-settings-cli-theme.md
new file mode 100644
index 0000000..adad455
--- /dev/null
+++ b/docs/plans/0037-companion-settings-cli-theme.md
@@ -0,0 +1,276 @@
+# 0037 Companion Settings and CLI Theme Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:executing-plans` or `$plan-execute auto` to implement this plan task-by-task. For `.ts`/`.tsx`/`.rs` edits, follow TypeScript/Rust guidance and lock semantics with tests before visual changes. For visual changes, follow `DESIGN.md`, drive `pnpm --filter @workbranch/companion tauri dev`, and visually verify the settings panel, font/theme application, and launch-at-login toggle before claiming completion.
+
+**Goal:** Add a compact settings surface to the Workbranch Companion popover and shift the visual system toward a terminal/CLI-like developer HUD: a settings icon beside refresh, launch-at-login toggle, fixed-width font selector, and color theme selector.
+
+**Architecture:** Split this from 0036. Keep 0036 focused on project grouping and row hierarchy. In this plan, add preference state and settings UI to the Tauri React companion. Use official Tauri v2 plugins for OS/app persistence: `@tauri-apps/plugin-autostart` for launch-at-login and `@tauri-apps/plugin-store` for font/theme preferences. Do not change the CLI JSON contract, domain task model, watcher, or activity store.
+
+**Tech Stack:** Tauri v2, React 18, TypeScript, Vite, Vitest, Biome, plain CSS, official Tauri plugins `autostart` and `store`. No Tailwind/shadcn/Radix.
+
+---
+
+## Product framing
+
+The companion is becoming less of a generic dashboard and more of a command-line control panel for task worktrees. The settings surface should feel like a small terminal preferences pane, not a full app settings window.
+
+User-visible additions:
+
+1. Top-right toolbar becomes `Refresh | Settings` with compact icon buttons and accessible labels.
+2. Settings icon opens an in-popover settings panel.
+3. `Launch at login` toggles app autostart.
+4. Font selector offers only fixed-width fonts.
+5. Color theme selector switches between CLI-like themes.
+6. The default visual feel moves from Raycast-like chrome toward fixed-width, terminal-inspired HUD styling.
+
+## Current repo evidence
+
+- `apps/workbranch-companion/src/App.tsx` currently renders a single refresh button in the top-right header and has no settings panel state.
+- `apps/workbranch-companion/src/style.css` currently uses `Inter, ui-sans-serif, system-ui` at `:root`; only repo chips use a monospace stack.
+- `apps/workbranch-companion/package.json` has Tauri/React dependencies but no autostart/store plugins.
+- `apps/workbranch-companion/src-tauri/src/lib.rs` currently initializes only `tauri_plugin_positioner`; no autostart/store plugin is wired.
+- `apps/workbranch-companion/src-tauri/capabilities/default.json` must grant explicit plugin permissions when new Tauri plugins are exposed to the frontend.
+- Historical Swift plan `docs/plans/0027-companion-launch-at-login.md` resolved the product semantics for launch-at-login: system state is the source of truth, not project config, and the setting should be immediate-apply. The current Tauri app needs a fresh implementation path.
+- Official Tauri v2 docs confirm:
+ - Autostart JS API: `enable`, `disable`, `isEnabled` from `@tauri-apps/plugin-autostart`.
+ - Autostart permissions: `autostart:allow-enable`, `autostart:allow-disable`, `autostart:allow-is-enabled`.
+ - Autostart Rust init via `tauri_plugin_autostart::init(...)`.
+ - Store plugin init via `tauri_plugin_store::Builder::new().build()` and permissions via `store:default`.
+
+## Decision gates
+
+- [x] **Scope split from 0036:** resolved to a new 0037 plan. Rationale: settings/preferences plus OS launch-at-login are not just presentation hierarchy and would make 0036 too broad.
+- [x] **Visual direction:** resolved to **command-line CLI / terminal HUD**. Rationale: fixed-width type, compact status lines, and theme presets match a developer worktree monitor better than generic app chrome.
+- [x] **Launch at login implementation:** resolved to official Tauri `autostart` plugin. Rationale: it is the current Tauri v2 path for `enable`/`disable`/`isEnabled` and avoids hand-written LaunchAgent plumbing.
+- [x] **Preference persistence:** resolved to official Tauri `store` plugin. Rationale: font/theme are app preferences, not workbranch project config or CLI contract; store gives explicit app-owned persistence without adding ad-hoc files.
+- [x] **Font choices:** resolved to fixed-width fonts only. Rationale: the UI identity should not be breakable by proportional fonts. The selector exposes a curated list and stores a font token, not arbitrary CSS text.
+- [x] **Theme choices:** resolved to a small fixed preset list. Rationale: app stays designed and testable; no custom color editor in this slice.
+
+## Preference contract
+
+Add a small companion settings model in TypeScript:
+
+```ts
+export type CompanionFont = "system-mono" | "sf-mono" | "menlo" | "monaco" | "jetbrains-mono";
+export type CompanionTheme = "terminal-dark" | "amber-crt" | "green-mono" | "high-contrast";
+
+export type CompanionPreferences = {
+ readonly font: CompanionFont;
+ readonly theme: CompanionTheme;
+};
+```
+
+Defaults:
+
+- `font: "system-mono"`
+- `theme: "terminal-dark"`
+- launch-at-login default is read from `isEnabled()` and not duplicated in the store.
+
+Font display labels:
+
+- `System Mono` -> CSS stack: `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace`
+- `SF Mono` -> `SFMono-Regular, ui-monospace, Menlo, Monaco, Consolas, monospace`
+- `Menlo` -> `Menlo, ui-monospace, Monaco, Consolas, monospace`
+- `Monaco` -> `Monaco, ui-monospace, Menlo, Consolas, monospace`
+- `JetBrains Mono` -> `"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace`
+
+Theme presets:
+
+- `terminal-dark`: near-black background, white/gray text, restrained cyan/blue accent.
+- `amber-crt`: near-black background, amber foreground/accent.
+- `green-mono`: near-black background, green terminal foreground/accent.
+- `high-contrast`: black background, high-contrast white text, stronger borders.
+
+## File structure
+
+Modify:
+
+- `DESIGN.md` โ update visual language, typography, color/theme tokens, and settings components to reflect CLI-like companion direction.
+- `apps/workbranch-companion/package.json` and lockfile โ add official Tauri plugin packages.
+- `apps/workbranch-companion/src-tauri/Cargo.toml` / `Cargo.lock` โ add autostart/store Rust plugin dependencies through the Tauri add flow.
+- `apps/workbranch-companion/src-tauri/src/lib.rs` โ initialize autostart and store plugins.
+- `apps/workbranch-companion/src-tauri/capabilities/default.json` โ add autostart and store permissions.
+- `apps/workbranch-companion/src/App.tsx` โ add toolbar settings button, settings panel state, preference loading/application, and launch-at-login status wiring.
+- `apps/workbranch-companion/src/style.css` โ add CLI-like theme variables, font-family variable, settings panel styles, toolbar styles, and theme classes.
+- `apps/workbranch-companion/tests/**/*.test.tsx?` โ add focused preference/settings tests.
+- `TASK-WORKBRANCH.md` โ task progress updates only.
+
+Create:
+
+- `apps/workbranch-companion/src/application/preferences.ts` โ preference types, defaults, font/theme option lists, sanitizers, and store helpers.
+- `apps/workbranch-companion/src/ui/SettingsPanel.tsx` โ settings panel UI.
+- `apps/workbranch-companion/tests/preferences.test.ts` โ preference sanitization/default coverage.
+- `apps/workbranch-companion/tests/settings-panel.test.tsx` โ static markup / callback coverage.
+
+Do not modify:
+
+- `apps/workbranch-cli/**`
+- `packages/contract/**`
+- `apps/workbranch-companion/src/domain/**`
+- `apps/workbranch-companion/src/infrastructure/acl.ts`
+- watcher/activity-store behavior except plugin initialization required by this settings slice.
+
+## Task 1: Update design contract for CLI-like settings direction
+
+**Files:** Modify `DESIGN.md`, `../TASK-WORKBRANCH.md`
+
+- [ ] Update **Brand** personality to terminal/CLI-like developer HUD, not generic app dashboard.
+- [ ] Update **Visual language**:
+ - typography defaults to fixed-width UI;
+ - theme tokens include `terminal-dark`, `amber-crt`, `green-mono`, `high-contrast`;
+ - avoid heavy gradients; prefer prompt-like separators, subtle grid/border lines, compact density.
+- [ ] Update **Components** to include top toolbar, settings button, settings panel, switch row, select row, and theme swatches.
+- [ ] Update **Accessibility** to require real button labels for refresh/settings, select labels, and switch state text.
+- [ ] Verify no placeholders: `rg -n "TBD|TODO|placeholder|fill in" DESIGN.md` -> no matches.
+
+## Task 2: Add official Tauri plugins
+
+**Files:** Modify package/Cargo/capabilities/lib.rs surfaces
+
+- [ ] Add autostart plugin via Tauri CLI from `apps/workbranch-companion`:
+
+ ```bash
+ pnpm tauri add autostart
+ ```
+
+- [ ] Add store plugin via Tauri CLI from `apps/workbranch-companion`:
+
+ ```bash
+ pnpm tauri add store
+ ```
+
+- [ ] Confirm `src-tauri/src/lib.rs` initializes:
+ - `tauri_plugin_autostart::init(...)`
+ - `tauri_plugin_store::Builder::new().build()`
+- [ ] Confirm `src-tauri/capabilities/default.json` includes:
+ - `autostart:allow-enable`
+ - `autostart:allow-disable`
+ - `autostart:allow-is-enabled`
+ - `store:default`
+
+## Task 3: Preference model and persistence
+
+**Files:** Create `src/application/preferences.ts`; Create/modify tests
+
+- [ ] Add `CompanionFont`, `CompanionTheme`, `CompanionPreferences`, defaults, and option arrays.
+- [ ] Add sanitizers so unknown stored values fall back to defaults.
+- [ ] Add store helpers around `@tauri-apps/plugin-store` that load and save only `{ font, theme }`.
+- [ ] Tests:
+ - default preferences are terminal-dark + system-mono;
+ - invalid font/theme sanitize to defaults;
+ - option arrays contain only fixed-width font choices;
+ - theme list includes the four resolved presets.
+
+## Task 4: Settings panel UI
+
+**Files:** Create `src/ui/SettingsPanel.tsx`; Create `tests/settings-panel.test.tsx`
+
+- [ ] Render a compact CLI-like panel opened from the top toolbar.
+- [ ] Include sections:
+ - `Startup` with `Launch at login` toggle;
+ - `Font` select with fixed-width options only;
+ - `Theme` select/swatches with four presets.
+- [ ] The launch-at-login toggle is immediate-apply and calls `enable()`/`disable()` through an injected callback.
+- [ ] Font/theme updates save preferences and update the app immediately.
+- [ ] Font/theme update callbacks must report preference save or sanitization failures to the app shell; do not swallow errors inside the panel.
+- [ ] Static markup tests assert labels, option names, and accessible control names.
+
+### Accessibility Requirements
+
+- Use semantic grouping: render the `Startup`, `Font`, and `Theme` sections as `fieldset` elements, each with a visible `legend` child.
+- The top toolbar icon button that opens the settings panel must have an `aria-label` that names the action, e.g. `Open settings`.
+- Every form control must have an associated `label` element using the `for`/`id` pattern: launch-at-login toggle, font select, and theme select.
+- Keyboard focus order must start at the settings panel opener, then move through the panel controls in source order: launch-at-login toggle, font select, theme select, and any close/dismiss control.
+- State changes must be announced to screen readers: launch-at-login activation/deactivation and preference save/update results should use an appropriate ARIA live region or equivalent role/state change.
+
+## Task 5: App shell wiring
+
+**Files:** Modify `src/App.tsx`
+
+- [ ] Replace single refresh button with a toolbar group: Refresh and Settings controls separated by a thin divider.
+- [ ] Add settings panel open/close state.
+- [ ] On app startup, load preferences from store and apply theme/font classes to the root shell.
+- [ ] On settings change, save preferences and update root shell immediately.
+- [ ] On font/theme preference failures, keep or restore the last valid applied preferences and set the existing footer/status message, matching the launch-at-login failure pattern.
+- [ ] If a stored or incoming font/theme value sanitizes to a fallback, apply the sanitized value and surface a concise footer/status message instead of silently changing the selection.
+- [ ] On app startup, call `isEnabled()` to initialize launch-at-login state.
+- [ ] On launch-at-login toggle:
+ - true -> `enable()`, then refresh `isEnabled()`;
+ - false -> `disable()`, then refresh `isEnabled()`;
+ - failures set the existing footer/status message.
+- [ ] Keep task refresh/watch behavior unchanged.
+
+## Task 6: CLI-like style system
+
+**Files:** Modify `src/style.css`
+
+- [ ] Replace proportional root stack with `var(--app-font-family)` defaulting to fixed-width.
+- [ ] Add root classes or data attributes for `font-*` and `theme-*` preferences.
+- [ ] Define theme variables for the four presets.
+- [ ] Style toolbar as compact command controls. Use text labels or inline SVG icons with `aria-label`s; do not rely on emoji glyphs for refresh/settings icons.
+- [ ] Style settings panel as terminal-like block: thin border, compact section labels, aligned select/switch rows, no modal-heavy chrome.
+- [ ] Preserve readable contrast and focus rings across all themes.
+
+## Task 7: Verification
+
+Automated from repo root:
+
+```bash
+pnpm --filter @workbranch/companion test
+pnpm --filter @workbranch/companion typecheck
+pnpm --filter @workbranch/companion lint
+pnpm --filter @workbranch/companion build
+git diff --check
+```
+
+Tauri/plugin verification:
+
+```bash
+pnpm --filter @workbranch/companion tauri build
+```
+
+Manual visual/behavior gate:
+
+```bash
+pnpm --filter @workbranch/companion tauri dev
+```
+
+Manual checks:
+
+- Header shows refresh and settings controls separated visually.
+- Settings panel opens/closes without disrupting task refresh/watch behavior.
+- Launch-at-login toggle reflects `isEnabled()` and reports errors in the footer/status line.
+- Font selector contains only fixed-width choices and changes the whole companion UI.
+- Theme selector changes the whole companion UI across the four presets.
+- CLI-like visual feel is stronger than 0036/Raycast: fixed-width text, terminal-like density, less glossy app chrome.
+- Keyboard focus rings, labels, empty/error states, and narrow width remain readable.
+
+## Acceptance criteria
+
+- `docs/plans/0036-companion-project-grouped-ui.md` remains focused on project grouping/action hierarchy; settings/theme work lives in this 0037 plan.
+- Top-right toolbar includes refresh and settings controls.
+- Settings panel includes launch-at-login, fixed-width font selector, and color theme selector.
+- Launch-at-login uses official Tauri autostart plugin and does not store duplicate launch state in workbranch project config.
+- Font/theme preferences persist through official Tauri store plugin.
+- All selectable fonts are fixed-width options; arbitrary fonts are not accepted.
+- Theme presets are fixed and covered by tests.
+- UI visual direction is updated in `DESIGN.md` and implemented via CSS tokens/classes, not ad-hoc inline styles.
+- CLI contract, task domain model, ACL mapping, watcher behavior, and activity store behavior are unchanged.
+
+## Non-goals
+
+- Do not add task lifecycle mutation UI.
+- Do not add arbitrary custom theme editor or arbitrary font input.
+- Do not introduce Tailwind/shadcn/Radix.
+- Do not change `workbranch list --json` / `list --global --json` schemaVersion 1.
+- Do not merge this work back into 0036; 0036 should remain independently implementable.
+
+## Self-review checklist for this plan
+
+- [x] Split scope from 0036 is explicit.
+- [x] Settings includes launch-at-login, fixed-width font selection, and color theme selection.
+- [x] CLI-like visual direction is reflected in design-contract tasks.
+- [x] Official Tauri v2 autostart/store plugin paths and permissions are captured.
+- [x] Preference persistence excludes project config and CLI contract changes.
+- [x] Tests and manual visual gate cover settings behavior and theme/font application.
+- [x] No TBD/TODO placeholders.