The previous /goal surface exposed management subcommands that made the CLI and desktop behavior hard to reason about. This keeps the user-facing command close to Claude Code's set/clear interaction while preserving historical transcript hydration and the existing evaluator loop.
Constraint: Desktop must see a live goal event before the model reaches the next tool call.\nRejected: Keep status/pause/resume/complete slash subcommands | requested UX is only set and clear.\nConfidence: high\nScope-risk: moderate\nDirective: Keep /goal user-facing syntax compact; extend internals without expanding the slash picker unless product explicitly asks.\nTested: bun test src/commands/goal/goal.test.tsx src/goals/goalState.test.ts src/goals/goalEvaluator.test.ts src/server/__tests__/ws-memory-events.test.ts src/server/__tests__/conversations.test.ts\nTested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx src/components/chat/composerUtils.test.ts\nTested: bun run check:server\nTested: cd desktop && bun run lint\nTested: cd desktop && bun run build\nTested: ./bin/claude-haha -p '/goal smoke verify immediate goal output' --output-format stream-json --include-partial-messages --max-turns 1 --no-session-persistence --verbose emitted immediate system.local_command_output before model stream\nTested: ./bin/claude-haha -p '/goal clear' --output-format stream-json --include-partial-messages --max-turns 1 --no-session-persistence --verbose exited with duration_api_ms=0 and No active goal\nNot-tested: Browser click smoke was blocked by Chrome DevTools transport closing and Computer Use cgWindowNotFound on this machine.
Goal status previously used a standalone success-card treatment, which made /goal look visually detached from the transcript. Rework the active goal strip and goal event cards to use the same compact memory/tool-result visual language.
Constraint: Preserve /goal state visibility while reducing alert-like chrome.
Rejected: Continue tuning only goal colors | the layout still read as a separate alert instead of transcript state.
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bun run test -- --run src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx src/theme/globals.test.ts
Tested: cd desktop && bun run test -- --run src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
The desktop composer had treated /goal lifecycle arguments as standalone slash-command rows. That made the UI look like /goal had second-level commands even though the CLI contract is one /goal command with arguments.
Constraint: Keep /goal status/pause/resume/complete/clear usable as CLI arguments.
Rejected: Keep pseudo subcommands in the picker | it contradicts the slash command model and confuses objective entry.
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bun run test -- --run src/components/chat/composerUtils.test.ts src/__tests__/pages.test.tsx src/pages/EmptySession.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Goal status needed to read like persistent agent state, not a success alert. Reuse the Memory surface language and expose goal-specific aliases so the visual treatment follows each supported desktop theme.\n\nConstraint: Must keep /goal visible in both active-session chrome and transcript events.\nRejected: Continue using success tokens | they made the UI look like a warning/success card and clashed with white/dark themes.\nConfidence: high\nScope-risk: narrow\nTested: cd desktop && bun run test -- --run src/pages/ActiveSession.test.tsx src/components/chat/MessageList.test.tsx src/theme/globals.test.ts\nTested: cd desktop && bun run lint\nTested: cd desktop && bun run build\nTested: Chrome screenshot fixture for light, white, and dark goal UI tokens.
Goal status needed to read like persistent agent state, not a success alert. Reuse the Memory surface language and expose goal-specific aliases so the visual treatment follows each supported desktop theme.\n\nConstraint: Must keep /goal visible in both active-session chrome and transcript events.\nRejected: Continue using success tokens | they made the UI look like a warning/success card and clashed with white/dark themes.\nConfidence: high\nScope-risk: narrow\nTested: cd desktop && bun run test -- --run src/pages/ActiveSession.test.tsx src/components/chat/MessageList.test.tsx src/theme/globals.test.ts\nTested: cd desktop && bun run lint\nTested: cd desktop && bun run build\nTested: Chrome screenshot fixture for light, white, and dark goal UI tokens.
Goal sessions need to feel like an ongoing agent-loop state, not a large success alert dropped into the chat. The UI now uses compact status and event treatments, and session titles stay anchored to the original goal objective instead of later /goal status commands.
Constraint: /goal history uses local_command transcript entries and later status checks can append fresh AI titles.
Rejected: Keep the existing green cards | they duplicate the objective, consume too much vertical space, and read as one-off notifications instead of durable loop state.
Confidence: high
Scope-risk: narrow
Directive: Goal status UI should remain compact and stateful; do not reintroduce large centered cards for routine lifecycle events.
Tested: bun test src/server/__tests__/sessions.test.ts
Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: Browser preview at 127.0.0.1:5179 with real session 3e9117d5-b792-43c9-bf57-7aec2b124f7e on desktop and mobile viewport
Not-tested: Packaged macOS app rebuild.
The CLI supports /goal status, pause, resume, complete, clear, and --tokens as arguments, but the desktop composer only showed the top-level /goal entry and closed completion after the space. Users could run the commands if they knew them, yet the UI made them look unavailable.
Constraint: Keep CLI semantics unchanged; this is a desktop discovery and completion fix only
Rejected: Add separate CLI slash commands for every goal subcommand | would diverge from the actual command surface
Confidence: high
Scope-risk: narrow
Directive: Keep /goal subcommands as argument completions unless the CLI command model gains first-class nested slash commands
Tested: cd desktop && bun run test -- --run src/components/chat/composerUtils.test.ts src/components/chat/ChatInput.test.tsx
Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/components/chat/MessageList.test.tsx src/pages/ActiveSession.test.tsx
Tested: cd desktop && bun run lint
Not-tested: live desktop manual interaction after restarting the app
New desktop sessions populated slash suggestions from the session endpoint before the CLI had emitted init metadata. That endpoint only scanned user and project skill directories, while the plugin settings view and global skills API already saw enabled plugin skills such as superpowers. The session endpoint now reuses the global skill listing and merges it with any cached CLI slash commands, and both composer surfaces rank command-name matches before broad description matches so /su surfaces superpowers first.
Constraint: New sessions need plugin skills before the first real user turn starts the CLI.
Rejected: Start or restart a hidden CLI process on plugin enable | heavier than needed and still misses the REST slash-command fallback path.
Confidence: high
Scope-risk: moderate
Directive: Keep session slash commands and /api/skills on the same skill discovery path when changing plugin skill loading.
Tested: bun test src/server/__tests__/skills.test.ts src/server/__tests__/plugins.test.ts src/server/__tests__/sessions.test.ts
Tested: cd desktop && bun run test --run src/components/chat/composerUtils.test.ts src/pages/EmptySession.test.tsx src/components/chat/ChatInput.test.tsx
Tested: bun run check:server
Tested: cd desktop && bun run lint
Not-tested: Manual desktop click-through in the packaged app.
The goal flow needs to drive the agent loop and remain visible after desktop reconnects, so this change treats /goal output as a structured lifecycle signal across CLI, server, and desktop history restoration.
Constraint: The current TypeScript CLI does not share Codex's Rust app-server thread-goal database, so persistence is reconstructed from the existing session transcript.
Rejected: UI-only goal badges | would leave the CLI self-loop unable to recover active or completed goals after process restart.
Rejected: Add a new persisted store | larger migration surface than needed for the existing transcript-backed session model.
Confidence: high
Scope-risk: moderate
Directive: Keep future goal state changes mirrored in transcript-visible local command output or introduce a real migration-backed store.
Tested: bun test src/goals/goalEvaluator.test.ts src/goals/goalState.test.ts
Tested: cd desktop && bun run test -- --run src/stores/chatStore.test.ts src/pages/ActiveSession.test.tsx src/components/chat/MessageList.test.tsx
Tested: bun run verify
Not-tested: Live provider baseline with a real model-driven goal loop.
Restored transcripts can still present stale running state while their final markdown and tool output continue to reflow. Resize-driven auto-follow now stays enabled only for live resizable output, so readers can scroll up after completion while active streams still remain pinned.
Constraint: Old H5 sessions can reconnect with stale non-idle UI state even when the transcript has an end_turn.
Rejected: Disable resize following entirely | active H5 streaming would regress when rendered content grows after tokens arrive.
Confidence: high
Scope-risk: narrow
Directive: Do not broaden ResizeObserver auto-follow without a restored completed-session regression test.
Tested: cd desktop && bun run test -- MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: Browser smoke on session 85862799-ec6b-43d0-b3b8-6e9fd9130954 kept scrollTop at 320 after content resize.
Sending from history must move the transcript to the new turn even if session state has not yet advanced from idle. The previous guard tied the jump to chatState, so a user_text tail that landed before thinking/streaming would be skipped permanently because the tail id no longer changed.
Constraint: The same MessageList path serves desktop and H5 chat.
Rejected: Wait for chatState to become thinking before scrolling | that misses updates where the message id is already recorded.
Confidence: high
Scope-risk: narrow
Directive: New user_text tail messages are intentional navigation events and must override historical scroll position.
Tested: cd desktop && bun run test -- MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: git diff --check
iPhone Chrome uses the iOS WebKit viewport model, so using scrollIntoView on the transcript sentinel can ask the browser to scroll ancestor containers, including the page viewport. That is risky with the fixed bottom composer and can expose blank page space when the latest button is clicked. This keeps all latest-message movement scoped to the chat scroll element and follows post-render height changes while streaming.
Constraint: H5 and desktop share MessageList, but the H5 browser runs through iOS WebKit viewport behavior.
Rejected: Keep bottomRef.scrollIntoView with container options | container support is not reliable enough and the default still targets all scrollable ancestors.
Confidence: high
Scope-risk: narrow
Directive: Do not reintroduce transcript scrollIntoView without real iPhone Chrome verification.
Tested: cd desktop && bun run test -- MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: git diff --check
Plugin enablement already has a live CLI reload control path, so desktop now applies plugin changes by refreshing the active session instead of waiting for a future conversation startup. The server forwards reload_plugins to the active CLI session, refreshes session slash-command cache, and notifies the client. The desktop plugin store automatically reloads after mutating plugin state, and the empty-session composer refetches skills when plugin capabilities change.
Constraint: Existing CLI exposes reload_plugins as the supported hot-refresh mechanism for commands, agents, plugins, and MCP state.
Rejected: Start a hidden replacement CLI process | higher cost, extra process lifecycle risk, and less precise than the existing control channel.
Confidence: high
Scope-risk: moderate
Directive: Keep plugin refresh routed through reload_plugins unless the CLI control contract is removed or changed.
Tested: bun test src/server/__tests__/plugins.test.ts
Tested: cd desktop && bun run test --run src/stores/pluginStore.test.ts src/__tests__/pluginsSettings.test.tsx src/pages/EmptySession.test.tsx
Tested: bun run check:server
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Not-tested: bun run check:desktop is blocked by a pre-existing color-mix compatibility failure in desktop/src/theme/globals.css.
H5 browsers can lag behind smooth scroll animations when streaming text grows, and sending a new prompt should always return the reader to the live turn even if they were browsing history. The message list now writes the scroll container directly while preserving the existing opt-out for manual history reading during assistant streaming.
Constraint: H5 and desktop share MessageList behavior
Rejected: Force-scroll on every streaming update | would break reading historical content during long replies
Confidence: high
Scope-risk: narrow
Directive: Preserve manual history reading except when a new user prompt is appended
Tested: cd desktop && bun run test -- MessageList.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Not-tested: Full check:desktop remains blocked by the existing vite-config color-mix assertion in globals.css
The sidebar now groups conversations by project, so the embedded project picker in the search box duplicated the main navigation model and hid state inside a compact icon. Remove that picker and its store state, leaving search as plain text filtering and keeping the project header menu focused on organization and sorting.
The archive-all entry was also hidden because a broad destructive action does not belong in the lightweight project menu.
Constraint: Project grouping is now the primary project navigation surface.
Rejected: Keep the embedded project picker hidden in place | it would leave dead filtering state and a stale recovery path.
Rejected: Keep archive-all in the header menu | broad destructive actions are too risky for this surface.
Confidence: high
Scope-risk: moderate
Directive: Do not reintroduce broad session deletion or hidden project filters into the sidebar header without a dedicated reviewed management flow.
Tested: cd desktop && bun run test src/components/layout/Sidebar.test.tsx src/stores/sessionStore.test.ts src/components/layout/TabBar.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Desktop memory settings already resolves project memory through the current sanitized project directory, while spawned CLI sessions could compute an older path variant and fail to read indexed memory files. Pin the child runtime to the same memory directory and let memory preview links open related markdown files directly from the rendered panel.
Constraint: Existing memory files may already live under both legacy underscore and current hyphenated project directories.
Rejected: Symlink or merge memory directories | it could mix stale legacy memory entries into the active project index.
Confidence: high
Scope-risk: moderate
Directive: Keep Settings memory discovery and spawned CLI memory context on the same project identity before changing either sanitizer.
Tested: bun test src/server/__tests__/conversation-service.test.ts src/server/__tests__/memory.test.ts
Tested: bun run check:server
Tested: cd desktop && bun run test -- MemorySettings MarkdownRenderer
Tested: cd desktop && bun run build
Tested: Browser smoke on local Vite/server for memory preview link navigation
Not-tested: bun run check:desktop still has pre-existing vite-config color-mix guard failure in desktop/src/theme/globals.css
The session sidebar already uses project grouping, so the change adds header-level project actions instead of replacing project row controls. Sorting and organization choices are persisted through the existing desktop UI preferences file and mirrored into local cache for startup continuity.
Constraint: Keep the existing project tree, drag ordering, pinning, hiding, and Finder project-row actions intact.
Rejected: Replacing project row actions with the header menu | row-level Finder and per-project session actions are still useful and already covered by tests.
Confidence: high
Scope-risk: moderate
Directive: Do not store sidebar organization in global Claude settings; keep it under cc-haha desktop-ui preferences.
Tested: cd desktop && bun run test -- src/components/layout/Sidebar.test.tsx
Tested: bun test src/server/__tests__/desktop-ui-preferences.test.ts
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Sidebar project ordering, pinning, and hidden-project state must survive
browser access to the same local server, so the UI now stores these
preferences under the cc-haha config directory and keeps localStorage as
a migration/cache fallback.
Constraint: Browser and H5 localStorage is isolated from the Tauri WebView
Rejected: Keep sidebar project preferences only in localStorage | browser sessions would not share state
Rejected: Reuse cc-haha/settings.json | provider and H5 access settings should stay separate
Confidence: high
Scope-risk: moderate
Directive: Keep sidebar hide/remove semantics non-destructive; do not delete transcript files for project removal
Tested: cd desktop && bun run test -- src/components/layout/Sidebar.test.tsx --run
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/desktop-ui-preferences.test.ts
Tested: bun run check:persistence-upgrade
Tested: bun run check:server
The desktop sidebar needed a project-first navigation model that keeps worktree sessions attached to their source project instead of scattering them as separate paths. The UI now renders sessions under project groups with project-level actions and persisted ordering, while the server exposes a logical project root for worktree transcripts.
Constraint: Existing memory directory tree work must remain separate from the session sidebar behavior.
Rejected: Group by raw transcript projectPath | worktree paths fragment the same repository into multiple sidebar projects.
Confidence: high
Scope-risk: moderate
Directive: Keep future sidebar grouping keyed by projectRoot before projectPath so isolated worktrees stay under their source repository.
Tested: cd desktop && bun run test -- src/components/layout/Sidebar.test.tsx
Tested: bun test src/server/__tests__/sessions.test.ts
Tested: git diff --check
Desktop file attachment selection was inlining every selected file as a data URL before sending the websocket message. Large multi-file sends could inflate the renderer request body by tens of megabytes before the server had a chance to materialize uploads.
Route Tauri file selection through the native dialog so desktop sends absolute paths, while preserving browser fallback data URLs for H5. Cover both active-session and draft-session composers plus a payload-size regression case.
Constraint: Browser/H5 cannot rely on local absolute file paths, so the existing FileReader fallback remains for non-Tauri runtimes.
Rejected: Raise websocket/body limits | would keep renderer memory pressure and still send file bytes unnecessarily.
Confidence: high
Scope-risk: narrow
Directive: Do not reintroduce FileReader data URLs for Tauri desktop file-picker attachments without measuring websocket payload size.
Tested: cd desktop && bun run test src/lib/composerAttachments.test.ts src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx
Tested: /tmp complex-project reproduction showed 12x3MB old inline payload at 50,333,174 bytes versus path-only payload around 1-2KB
Not-tested: Full desktop lint/build due unrelated existing Sidebar.tsx type errors in the dirty worktree
Related: https://github.com/NanmiCoder/cc-haha/issues/444
The detached worktree contains the verified desktop memory activity styling fix. Merge it into local main while preserving the existing main-line desktop changes and keeping the worktree commit as a separate decision record.
Constraint: Local main already diverged from the detached worktree and is ahead of origin
Rejected: Cherry-pick the worktree commit | would lose the explicit worktree merge topology the user requested
Confidence: high
Scope-risk: narrow
Directive: Preserve main-line desktop changes when resolving future memory-theme edits
Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx --run
Tested: cd desktop && bun run test src/components/layout/Sidebar.test.tsx --run
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Memory tool activity inherited the global warm brand treatment, which made the new desktop memory surface feel detached from white and dark themes. The component now uses memory-specific tokens with a quieter teal family, and the sidebar settings dock is made opaque so scrolling sessions cannot show through it.
Constraint: Desktop memory activity must remain readable across light, white, and dark themes without reusing the classic warm brand surface
Rejected: Keep using color-mix with --color-brand | preserves the original warm tint mismatch in white theme
Confidence: high
Scope-risk: narrow
Directive: Keep memory activity colors on memory-specific tokens instead of generic brand tokens
Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx --run
Tested: cd desktop && bun run test src/components/layout/Sidebar.test.tsx --run
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: Browser smoke screenshots for light, white, and dark memory activity previews
Copy actions that did not use CopyButton could succeed silently, leaving users unsure whether the clipboard changed. Show localized success toasts for H5 URL, QR link, and workspace path copies, and report a generic copy failure when the shared clipboard helper cannot write.
Constraint: Copy must keep working across macOS, Linux, and Windows Tauri WebViews, so the existing web Clipboard API plus textarea execCommand fallback remains the platform-neutral mechanism.
Rejected: Add native OS-specific clipboard calls | unnecessary surface area when the current web fallback already covers the desktop WebView contexts.
Confidence: high
Scope-risk: narrow
Directive: Copy surfaces that cannot visibly change their own button text should emit a toast on success or failure.
Tested: cd desktop && bun run test -- --run src/__tests__/generalSettings.test.tsx src/components/workspace/WorkspacePanel.test.tsx src/i18n/index.test.tsx
Tested: bun run check:desktop
Tested: bun run verify (passed=10 failed=0 skipped=0)
Not-tested: Manual OS matrix on Linux and Windows; behavior uses platform-neutral browser clipboard APIs inside Tauri.
Bring the focused titlebar-density fix from the Codex worktree into local main. The patch only adjusts the desktop tab strip height baseline and its regression test, leaving existing local workspace edits untouched.
Constraint: Local main has unrelated uncommitted desktop settings/workspace edits that must be preserved
Rejected: Broad staging before merge | would mix unrelated local work into this landing
Confidence: high
Scope-risk: narrow
Directive: Keep this merge limited to the tab bar height patch; do not treat unrelated dirty files as part of this change
Tested: cd desktop && bunx vitest run src/components/layout/TabBar.test.tsx
Tested: bun run check:desktop
Not-tested: Push or PR creation; user asked for local main merge only
The previous tab strip height made session titles and toolbar affordances feel cramped. Increase the shared titlebar height token for tabs, overflow controls, and the native drag gutter so the top chrome reads as a deliberate desktop titlebar while preserving the existing drag, close, and toolbar interactions.
Constraint: Keep the change scoped to the desktop tab strip and avoid changing session state, ordering, or window-control behavior
Rejected: Increase only the outer bar height | tab hit targets and overflow controls would remain visually misaligned
Confidence: high
Scope-risk: narrow
Directive: Keep tab, overflow button, and drag-gutter heights aligned when changing this titlebar again
Tested: cd desktop && bunx vitest run src/components/layout/TabBar.test.tsx
Tested: bun run check:desktop
Tested: Browser smoke on Vite with tab bar measured at about 44px and toolbar controls centered
Not-tested: Native packaged Tauri window rendering on Windows titlebar controls
The active empty-session composer used the same launch controls as the new-session surface but rendered them outside the glass panel, creating an inconsistent gap between the prompt box and project/branch/worktree selectors. Embed the desktop hero launch row in the composer panel and keep the existing mobile and post-message context-chip paths unchanged.
Constraint: Preserve the existing new-session composer layout and shared RepositoryLaunchControls behavior.
Rejected: Adjust outer margins only | would keep the active empty-session DOM structurally different from the already-correct new-session composer.
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bun run test -- src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx
Tested: cd desktop && bun run lint
Tested: git diff --check
Tested: agent-browser DOM check confirmed the launch row is inside chat-input-panel with 1px bottom gap
Not-tested: Full bun run verify gate
Bring the detached worktree clipboard fix onto local main while preserving main's existing desktop responsiveness work.
Constraint: Local main and the detached worktree diverged from e070c4a0, so this requires a merge commit instead of a fast-forward.
Confidence: high
Scope-risk: narrow
Directive: Keep desktop copy controls on the shared fallback helper when adding new copy surfaces.
Tested: bun run verify (passed=7 failed=0 skipped=3) in /Users/nanmi/.codex/worktrees/ec25/claude-code-haha before merge
Not-tested: Post-merge full gate on local main; merge applies the verified six-file desktop patch onto a clean local main.
Several desktop copy entry points still called navigator.clipboard directly, which can fail or report success inconsistently in Tauri and browser test contexts. Route those copy actions through the existing textarea execCommand fallback helper and cover the fallback paths for startup diagnostics, markdown copy controls, and workspace file paths.
Constraint: Clipboard API is not reliable in every desktop/browser context.
Rejected: Leave direct navigator.clipboard calls in isolated components | repeats the diagnostics copy failure mode and creates inconsistent copy behavior.
Confidence: high
Scope-risk: narrow
Directive: New desktop copy controls should use copyTextToClipboard or CopyButton instead of calling navigator.clipboard directly.
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- --run src/components/markdown/MarkdownRenderer.test.tsx src/components/layout/StartupErrorView.test.tsx src/components/workspace/WorkspacePanel.test.tsx src/__tests__/diagnosticsSettings.test.tsx src/components/chat/MessageList.test.tsx
Tested: bun run check:desktop
Tested: bun run check:native
Tested: bun run check:coverage
Tested: bun run verify (passed=7 failed=0 skipped=3)
Not-tested: Live provider baseline; not required for this desktop-only clipboard fix.
This merge carries the worktree fix into the local main branch so long multi-session desktop usage keeps rendering cost bounded while DeepSeek follows the shared General Settings Thinking toggle. It preserves running-session behavior and keeps legacy DeepSeek managed env from pinning thinking off after upgrade.
Constraint: User requested the worktree result merged into local main without pushing.
Confidence: high
Scope-risk: moderate
Directive: Keep DeepSeek controlled by the global Thinking setting; do not restore provider-specific disabled-thinking defaults without both toggle-state tests.
Tested: bun test src/server/__tests__/conversations.test.ts -t "global Thinking setting control DeepSeek"
Tested: bun test src/server/__tests__/persistence-upgrade.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/title-service.test.ts src/utils/__tests__/thinking.test.ts src/utils/__tests__/providerManagedEnvCompat.test.ts
Tested: cd desktop && bun run test -- MessageList.test.tsx ContextUsageIndicator.test.tsx
Tested: bun run check:server
Virtualized chat rendering keeps inactive long-running sessions cheap without disconnecting their CLI process, while DeepSeek now relies on the shared Thinking setting instead of a provider-specific disabled-thinking override. Existing legacy DeepSeek managed env is normalized so old local settings do not keep suppressing reasoning output after upgrade.
Constraint: Multiple desktop tabs must keep live sessions running and remain quick to switch without reconnecting.
Rejected: Pause or disconnect hidden sessions | would delay tab switching and interrupt streaming/tool state visibility.
Rejected: Keep DeepSeek disabled-thinking preset | conflicts with the General Settings Thinking control.
Confidence: high
Scope-risk: moderate
Directive: Do not reintroduce provider-specific disabled-thinking defaults for DeepSeek without testing both General Settings toggle states.
Tested: bun test src/server/__tests__/conversations.test.ts -t "global Thinking setting control DeepSeek"
Tested: bun test src/server/__tests__/persistence-upgrade.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/title-service.test.ts src/utils/__tests__/thinking.test.ts src/utils/__tests__/providerManagedEnvCompat.test.ts
Tested: cd desktop && bun run test -- MessageList.test.tsx ContextUsageIndicator.test.tsx
Tested: bun run check:server
PR #428 added a General Settings zoom slider after the desktop shortcut work already introduced a native-first app zoom controller. Keeping both paths would create double scaling and stale UI state, so the slider now routes through the existing controller and store state while the app shell no longer applies a second CSS zoom.
Constraint: UI zoom is device-local display state and should not be written into shared user settings.
Rejected: Keep cc-haha-ui-zoom plus AppShell style.zoom | it conflicts with shortcut zoom and multiplies visual scale.
Rejected: Persist zoom through /api/settings/user | it would sync display-specific state across machines.
Confidence: high
Scope-risk: moderate
Directive: Keep app zoom behind desktop/src/lib/appZoom.ts; do not add another storage key or DOM zoom application point without migration and shortcut sync tests.
Tested: cd desktop && bun run test -- --run src/lib/appZoom.test.ts src/hooks/useKeyboardShortcuts.test.tsx src/stores/settingsStore.test.ts src/__tests__/generalSettings.test.tsx src/lib/persistenceMigrations.test.ts src/lib/doctorRepair.test.ts
Tested: bun run check:desktop
Not-tested: Real Windows/Linux desktop runtime smoke.
The empty-session desktop composer rendered repository launch context
as a separate footer, which created a visual gap between the prompt
surface and the project/branch/worktree controls. This keeps the PC
layout in one panel while preserving the existing mobile bottom layout.
Constraint: Mobile H5 composer layout must remain unchanged.
Rejected: Restyle the shared launch bar globally | existing standalone and mobile placements rely on the old chrome.
Confidence: high
Scope-risk: narrow
Directive: Keep the composer placement desktop-only unless mobile layout is intentionally redesigned.
Tested: cd desktop && bun run test src/pages/EmptySession.test.tsx src/components/shared/RepositoryLaunchControls.test.tsx src/components/shared/DirectoryPicker.test.tsx
Tested: bun run check:desktop
Tested: agent-browser screenshots for PC and mobile empty-session composer.
Not-tested: Packaged Tauri runtime smoke.
- Add uiZoom field to UserSettings type and settingsStore with localStorage persistence
- Apply CSS zoom to AppShell root div for full-window scaling
- Add zoom slider in GeneralSettings (50%-200%, step 5%, centered at bottom-left)
- Export UI_ZOOM_MIN/MAX/STEP constants for consistent range control
- Add i18n support for UI zoom label and description (en/zh)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Goal commands already had local CLI metadata, but desktop and SDK init consumers only received a name list. Keep the legacy slash_commands string array intact and add explicit metadata so clients can render descriptions and usage hints without breaking older readers.
Constraint: Existing SDK/system init consumers may depend on slash_commands staying string-only.
Rejected: Replace slash_commands with objects | would risk breaking older SDK and desktop consumers.
Confidence: high
Scope-risk: moderate
Directive: Keep slash_commands as the compatibility name list unless all SDK consumers have migrated.
Tested: bun test src/server/__tests__/websocket-handler.test.ts src/utils/messages/systemInit.test.ts src/commands/headless.test.ts src/goals/goalState.test.ts src/goals/goalEvaluator.test.ts
Tested: cd desktop && bun test src/components/chat/composerUtils.test.ts
Tested: bun run check:server
Tested: bun run check:desktop
This brings the desktop memory worktree onto local main after main advanced with opener, IM, zoom, and autonomous goal work. The conflict resolution keeps both the selected-chat reference imports and memory event settings entrypoint in MessageList, and keeps both open-targets and memory API routes in the server router.
Constraint: main and feat/desktop-memory-surface diverged across desktop chat and server routing.
Constraint: desktop Vitest runs src-tauri tests under Vitest, so the Tauri config test cannot import bun:test.
Rejected: Fast-forward merge | main carried newer local commits not present on the memory branch.
Confidence: high
Scope-risk: moderate
Directive: Keep chat selection references, memory cards, open-target routes, and memory routes together when touching these files.
Tested: bun run check:desktop
Tested: bun run check:server
Desktop users could not tell when project memory was being referenced or updated unless they opened raw tool details. This surfaces memory reads and writes as a dedicated chat activity, while keeping ordinary tool calls visible in mixed groups, and tightens the memory settings layout for faster project and file navigation.
Constraint: CLI memory currently arrives through system notifications and normal file tool calls rather than a dedicated memory tool.
Rejected: Hide memory file writes inside the existing file tool group | users need a distinct product signal for memory activity.
Rejected: Preserve the manual create-memory control | project memories are model-produced files and the user already asked to remove manual creation.
Confidence: high
Scope-risk: moderate
Directive: Keep non-memory tool calls on the normal rendering path when adding more memory activity signals.
Tested: cd desktop && bun run test -- MessageList memorySettings
Tested: bun run check:desktop
Tested: agent-browser screenshots for settings and chat memory activity under /tmp/cc-haha-memory-redesign-*.png
The branch-level quality gate includes the memory context inspector changes, so this adds focused coverage for the loaded and empty memory-file states plus the Settings navigation side effect.
Constraint: Changed-line coverage must stay above the 90% PR threshold.
Rejected: Lower the coverage threshold | would hide a real gap in the memory context UI tests.
Confidence: high
Scope-risk: narrow
Directive: Keep memory inspector navigation covered when changing /context memory file rendering.
Tested: cd desktop && bunx vitest run src/components/chat/LocalSlashCommandPanel.test.tsx
Tested: bun run check:desktop
Tested: bun run check:coverage
The first project opener UI used generic code glyphs for every IDE, which made the menu visually noisy and did not match the native desktop expectation. This changes the macOS path to expose transparent PNG icons generated from each detected local .app bundle and renders them directly in the toolbar/menu, falling back to the existing glyph only when an icon cannot be loaded.
Constraint: Do not redistribute third-party IDE trademark assets in the app bundle.
Constraint: Keep icon detection local to already detected open targets and cache only runtime PNG results.
Rejected: Bundling downloaded IDE logos | creates asset maintenance and trademark redistribution risk.
Rejected: Adding simple-icons as a dependency | it is not a faithful desktop app icon source and still requires brand permission checks.
Confidence: high
Scope-risk: narrow
Directive: Prefer local bundle icons on macOS; add curated official assets only as a cross-platform fallback layer.
Tested: bun test src/server/__tests__/open-target-service.test.ts src/server/__tests__/open-target-api.test.ts
Tested: cd desktop && bun run test -- src/stores/openTargetStore.test.ts src/components/layout/OpenProjectMenu.test.tsx src/components/layout/TabBar.test.tsx
Tested: cd desktop && bun run lint
Tested: bun -e openTargetService.getTargetIcon for vscode and finder returned PNG data
Tested: cd desktop && bun run build
Desktop sessions need a fast local escape hatch that opens the same materialized cwd the agent is editing, without showing unavailable IDE choices or persisting detection state. This adds a local open-targets API with silent in-memory detection for common IDEs and platform file managers, then wires a compact Codex-style toolbar menu into the desktop TabBar for active session workdirs.
Constraint: The first version is local IDE/editor and Finder/Explorer/file-manager only, no terminal targets or IDE plugin integration.
Constraint: The opened path must come from the active session workDir so isolated worktrees open the actual agent editing surface.
Rejected: Persisting detected applications | detection is cheap and temporary state avoids stale app inventory.
Rejected: Rendering unavailable IDEs as disabled menu rows | the user asked to show only detected targets and fall back to Finder/Explorer when no IDE is available.
Confidence: high
Scope-risk: moderate
Directive: Keep this path session-workdir based; do not switch it to repository root without proving isolated worktree behavior.
Tested: bun test src/server/__tests__/open-target-service.test.ts src/server/__tests__/open-target-api.test.ts
Tested: cd desktop && bun run test -- src/stores/openTargetStore.test.ts src/components/layout/OpenProjectMenu.test.tsx src/components/layout/TabBar.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: bun test src/server/__tests__/h5-access-auth.test.ts -t 'allows local desktop H5 access settings under explicit server auth with a valid bearer'
Not-tested: bun run check:server full suite had one unrelated H5 auth integration timeout in the full concurrent run; the timed-out test passed when rerun alone.
Workspace and transcript selections now flow into the composer as lightweight references so users can carry exact snippets into the next prompt without file-only workarounds. The prompt formatter keeps workspace and chat excerpts separate, and the composer avoids sending chat snippets as fake file attachments.
Constraint: Selection references must prepare model context without polluting backend file attachment payloads.
Rejected: Reuse file attachments for chat selections | would leak chat:// pseudo paths into file-oriented payloads.
Confidence: high
Scope-risk: moderate
Directive: Keep chat-selection references prompt-only unless the backend gains a first-class chat context attachment type.
Tested: bun run check:desktop
Tested: bun run verify
Tested: agent-browser UI smoke for workspace, user-message, and assistant-message selections
Not-tested: Strict Chrome extension channel; connection timed out, local browser automation covered the flow.
The CLI already emits memory_saved events and stores Markdown memory files, but the desktop app had no usable surface for seeing or editing those writes. This adds a project memory API, a Settings memory editor, chat memory event cards, and routing from /memory or /context into the memory UI.
Constraint: Memory files live under Claude project storage and must remain plain Markdown editable by users.
Rejected: Only expose raw filesystem links | users need an in-app review and edit flow from chat.
Confidence: high
Scope-risk: moderate
Directive: Keep memory storage project-scoped and preserve unknown Markdown content when editing.
Tested: bun test src/server/__tests__/ws-memory-events.test.ts src/server/__tests__/memory.test.ts
Tested: bun run check:server
Tested: bun run check:desktop
Tested: agent-browser E2E for chat memory card, Open Memory navigation, and responsive Markdown editor layout
Not-tested: Live model auto-memory trigger rate with real provider credentials
Desktop users can receive new sessions from IM adapters or scheduled tasks while the app stays open. The sidebar now refreshes on mount, visible focus, and a low-frequency visible-only interval, with a manual refresh control and in-flight request dedupe so the fix does not create avoidable polling pressure.
Constraint: Sessions can be created outside the desktop process by IM and scheduler entrypoints
Rejected: WebSocket push for this patch | broader server contract change than needed for the reported stale list
Confidence: high
Scope-risk: narrow
Directive: Keep session-list refresh visible-only and deduped before lowering intervals or adding more triggers
Tested: cd desktop && bunx vitest run src/components/layout/Sidebar.test.tsx src/i18n/index.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: Browser smoke on isolated desktop backend/frontend with refresh button click
Not-tested: Full coverage gate is blocked by unrelated root test port/timeouts; changed lines coverage reported 100% (59/59)
Desktop search results were visually misleading when many directories shared the same basename, because the UI emphasized the basename and the server ranking allowed deep basename matches to outrank direct path-prefix matches. This makes search rows show the insertable relative path and ranks direct path-prefix matches before unrelated same-name directories.
Constraint: Desktop @ file suggestions should match the CLI mental model for path search.
Rejected: Keep basename plus parent-path rows | it hides the distinction between src/, src/hooks/, and desktop/src in the primary text.
Confidence: high
Scope-risk: narrow
Directive: Preserve path-prefix ranking ahead of basename-only matches for directory searches.
Tested: bun test src/server/__tests__/filesystem.test.ts
Tested: cd desktop && bun run test -- FileSearchMenu.test.tsx ChatInput.test.tsx
Tested: bun run check:server
Tested: bun run check:desktop
Desktop @ file search was recursively walking the filesystem with a local skip list, which let Python and Node generated directories leak into results and diverged from CLI behavior. Route candidate discovery through the same git-first model: tracked files, untracked files with exclude-standard, and ripgrep fallback for non-git folders, then derive selectable directories from those candidates.
Constraint: Desktop picker must select both files and directories without surfacing ignored project artifacts.
Constraint: No new dependencies; reuse the existing git, ripgrep, settings, and ignore utilities.
Rejected: Maintain a hardcoded directory denylist | it would drift from CLI and miss project-specific ignore rules.
Rejected: Full recursive readdir scanning | it ignores git index semantics and makes large dependency trees visible.
Confidence: high
Scope-risk: moderate
Directive: Keep desktop @ file candidate discovery aligned with src/hooks/fileSuggestions.ts before changing ranking or ignore behavior.
Tested: bun test src/server/__tests__/filesystem.test.ts
Tested: cd desktop && bun run test -- FileSearchMenu.test.tsx ChatInput.test.tsx
Tested: cd desktop && bun run lint && bun run test -- --run && bun run build
Tested: bun run check:server
Tested: bun run check:coverage
Tested: bun run verify
Not-tested: Native Windows/Linux manual UI smoke; path handling relies on cross-platform git/ripgrep wrappers and normalized relative paths.