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
The memory resource tree should keep the project/file hierarchy, but the extra project action surface was not wanted for this page. This removes the pinning state, Finder action, and matching tests/i18n while leaving the tree layout intact.
Constraint: Preserve the existing memory directory tree changes.
Rejected: Revert the whole memory tree redesign | the user explicitly asked to keep the original directory tree work.
Confidence: high
Scope-risk: narrow
Tested: cd desktop && bun run test -- src/__tests__/memorySettings.test.tsx
Tested: cd desktop && bun run lint
The memory resource tree needed to match the project-first interaction model: projects expand in place, project rows do not show noisy counts or explicit expand affordances, and common project actions live behind the row menu.
Constraint: Must reuse the existing open-target Finder/file-manager flow instead of shelling out from the view.
Rejected: Reuse the session sidebar project filter menu | it is a filter surface, not the memory resource tree.
Confidence: high
Scope-risk: narrow
Directive: Keep memory project actions backed by openTargetStore so platform-specific file managers stay centralized.
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- src/__tests__/memorySettings.test.tsx
Tested: cd desktop && bun run build
Tested: Playwright screenshot smoke of Settings > Memory project menu at /tmp/cc-haha-memory-ui.png
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
Desktop settings writes update settings.json through SettingsService, while plugin
state writes use the shared CLI settings cache. Reset the shared cache after a
successful atomic SettingsService write so later plugin updates merge against the
fresh file instead of an older in-memory snapshot.
Constraint: Desktop general settings and plugin state share ~/.claude/settings.json but use different write paths
Rejected: Move plugin writes onto SettingsService | broader API change than needed for this regression
Confidence: high
Scope-risk: narrow
Directive: Keep cache invalidation aligned across all settings.json write paths
Tested: bun test src/server/__tests__/settings.test.ts -t "cached CLI settings"
Tested: bun test src/server/__tests__/settings.test.ts src/server/__tests__/plugins.test.ts
Tested: cd desktop && bun run test -- generalSettings settingsStore pluginsSettings
Tested: bun run check:server
Tested: git diff --check
Not-tested: Full bun run verify still fails on unrelated dirty desktop changed-line coverage and CSS color-mix gate
The memory settings surface previously split projects and memory files into separate selection regions. This change keeps the desktop settings frame but makes the memory resource area behave like a single resource tree: project folders expand in place, nested memory folders/files remain in the same hierarchy, and visual badges are removed from the tree rows to keep the UI quiet.
Constraint: The desktop settings shell and existing memory store/API contracts stay unchanged.
Rejected: Keep project and file lists as separate panels | the requested interaction needs project folders and their memory files in one tree.
Rejected: Keep file-type badges in tree rows | they made the navigation look noisy and artificial.
Confidence: high
Scope-risk: moderate
Directive: Keep memory navigation tree rows plain; do not reintroduce badge-heavy file rows without a UX pass.
Tested: cd desktop && bun run test src/__tests__/memorySettings.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: local agent-browser screenshot of settings memory tree
The local main branch already contains memory theme tokens that use color-mix, so the Safari compatibility assertion should cover the startup-sensitive zoom slider CSS block instead of the entire stylesheet.
Constraint: Existing main theme tokens intentionally include color-mix for non-zoom surfaces.
Rejected: Remove the Safari compatibility assertion | the zoom slider still needs coverage for WebView-safe CSS.
Confidence: high
Scope-risk: narrow
Tested: bunx vitest run src/__tests__/generalSettings.test.tsx src/theme/globals.test.ts
Not-tested: Full bun run verify
Bring the verified settings zoom interaction cleanup from the desktop worktree onto local main while preserving the existing main history.
Constraint: Local main already contains unrelated desktop commits after the worktree base.
Confidence: high
Scope-risk: narrow
Tested: bunx vitest run src/__tests__/generalSettings.test.tsx src/theme/globals.test.ts
Tested: browser smoke verified dark-theme order and slider visibility at localhost:5174
Not-tested: Full bun run verify after merge
The settings zoom slider is a secondary desktop preference, so it now previews drag movement locally before committing the app zoom on release and sits below system notifications. The shortcut hint also spells out reset behavior, and the dark theme slider thumb uses stronger contrast tokens so it stays visible while dragging.
Constraint: Desktop zoom changes affect the full application surface and should not update continuously during pointer drag.
Rejected: Keep zoom directly under theme selection | the control is low-frequency and was visually dominating General settings.
Confidence: high
Scope-risk: narrow
Tested: bunx vitest run src/__tests__/generalSettings.test.tsx src/theme/globals.test.ts
Tested: browser smoke verified dark-theme order and slider visibility at localhost:5174
Not-tested: Full bun run verify
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
Desktop startup can fail before React mounts on older WebViews, so an HTML-level watchdog now renders startup diagnostics even when the module bundle never reaches the app code. Persistent provider migration now imports legacy root provider config into cc-haha-owned storage without deleting the old source file, and plugin marketplace cleanup refuses obvious corrupted cache roots or outside paths.
Constraint: User explicitly accepted the current reviewed state for landing despite remaining review concerns.
Constraint: Global ~/.claude state is user-owned and protected; automatic repair must avoid deleting shared config, transcripts, skills, MCP, plugins, OAuth, adapters, and teams.
Rejected: Tell users to delete ~/.claude or ~/.claude/cc-haha | unsafe because it can destroy user-owned Claude state and still may not fix WebView compatibility failures.
Confidence: medium
Scope-risk: moderate
Directive: Do not weaken protected-path checks; future deletion paths should validate real paths and symlink behavior before recursive rm.
Tested: bun test src/utils/plugins/installedPluginsManager.test.ts src/utils/plugins/marketplaceManager.test.ts
Tested: bun run check:server
Tested: cd desktop && bun run test -- --run src/main.test.tsx index-html.test.ts vite-config.test.ts src/theme/globals.test.ts
Tested: cd desktop && bun run build
Tested: bun run check:coverage
Not-tested: live macOS 12/Safari 15 WKWebView startup on an affected machine.
Not-tested: H5 diagnostic URL redaction and symlink-escape hardening are known follow-up risks from review.
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.
Settings reported false negatives for resources the runtime could load, because the API paths used narrower discovery rules than the execution path. This keeps the Settings surfaces read-only where appropriate while matching runtime visibility for linked skills, version-constrained plugins, and merged MCP sources.
Constraint: Settings MCP list must not actively connect to servers while loading.
Rejected: Add UI-specific fallbacks | would preserve drift between Settings and runtime loaders
Confidence: high
Scope-risk: moderate
Directive: Keep Settings resource listing backed by the same shared loader semantics as runtime discovery.
Tested: bun test src/server/__tests__/skills.test.ts
Tested: bun test src/server/__tests__/plugins.test.ts
Tested: bun test src/server/__tests__/mcp.test.ts
Tested: bun run check:server
Tested: bun run check:coverage
Tested: bun run verify
Not-tested: Live external MCP connector account state
- 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>
The /goal command already exposes description and argument hints in the CLI. Revert the broader SDK, system/init, and server metadata changes so the remaining behavior change is only the desktop fallback and rendering path.
Constraint: Desktop help should not require changing core CLI or SDK wire contracts.
Rejected: Add slash_commands_metadata to system/init | unnecessary for the reported desktop-only display issue.
Confidence: high
Scope-risk: narrow
Directive: Fix missing desktop slash command descriptions in desktop fallback/rendering unless the CLI wire contract is explicitly being redesigned.
Tested: cd desktop && bun run test -- --run src/components/chat/composerUtils.test.ts src/__tests__/pages.test.tsx src/components/chat/ChatInput.test.tsx
Tested: bun test src/commands/headless.test.ts src/goals/goalState.test.ts src/goals/goalEvaluator.test.ts
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 /goal slash command was developed and verified in an isolated worktree, while local main has advanced with unrelated desktop fixes. Merge the feature branch rather than rebasing so both lines of local history remain intact.
Constraint: local main contains newer independent commits after the feature base
Rejected: Fast-forward merge | main is not an ancestor of the feature branch
Confidence: high
Scope-risk: moderate
Directive: Keep /goal continuation limits bounded and provider-agnostic when modifying the loop
Tested: bun test src/commands/headless.test.ts src/goals/goalState.test.ts src/goals/goalEvaluator.test.ts
Tested: bun run check:server
Tested: bun run verify
Tested: real DeepSeek v4 pro CLI and desktop /goal validation in /tmp business fixtures
Not-tested: push or remote PR creation
The detached worktree carries the verified Token usage dark-theme contrast
repair. Merge it into local main while preserving the existing local main
history for desktop opener work.
Constraint: Local main already contains unpushed desktop opener commits
Confidence: high
Scope-risk: narrow
Directive: Keep future activity heatmap palette changes isolated to activity theme tokens
Tested: cd desktop && bun run test ActivitySettings.test.tsx globals.test.ts
Tested: bun run check:desktop
Not-tested: Post-merge full verify gate
The activity heatmap was reusing broad primary and inverse tokens whose
meaning changes by theme. The usage tooltip became unreadable in dark mode,
and nearby status badges referenced semantic container tokens that were not
available in every theme. This introduces activity-specific theme tokens and
locks the token surface with focused regression coverage.
Constraint: Desktop supports light, white, and dark themes from globals.css
Rejected: Change global primary or inverse tokens | broader blast radius across unrelated controls
Confidence: high
Scope-risk: narrow
Directive: Keep activity heatmap colors behind activity-specific tokens before changing palette behavior
Tested: cd desktop && bun run test ActivitySettings.test.tsx globals.test.ts
Tested: bun run check:desktop
Tested: Browser smoke on dark Token usage tooltip at http://127.0.0.1:5179
Not-tested: Native packaged app visual smoke
Tauri WebView was allowed to call the desktop sidecar API, but the CSP still blocked image loads from that same local server, so extracted IDE icons degraded to the generic code glyph in packaged builds. macOS detection also trusted command shims before app bundles, which let stale JetBrains Toolbox scripts keep removed IDEs visible.
Constraint: Opener icons are served by the per-run desktop sidecar on localhost or 127.0.0.1
Rejected: Bundle a fixed static icon set | the product should use detected installed application icons across platforms
Rejected: Trust macOS command shims for detection | stale shims survive app removal and make the menu lie
Confidence: high
Scope-risk: narrow
Directive: Keep macOS IDE detection anchored to real app bundles before accepting command shims
Tested: bun test src/server/__tests__/open-target-service.test.ts src/server/__tests__/open-target-api.test.ts desktop/src-tauri/tauri-config.test.ts
Tested: cd desktop && bun run test -- src/api/client.test.ts src/api/openTargets.test.ts src/stores/openTargetStore.test.ts src/components/layout/OpenProjectMenu.test.tsx src/components/layout/TabBar.test.tsx
Tested: cd desktop && bun run lint
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
Packaged Tauri builds load the React app from a WebView origin while the sidecar API runs on a dynamic localhost port. The opener menu was rendering relative image paths such as /api/open-targets/icons/vscode, so the image request never reached the sidecar and every target fell back to the generic icon.
The desktop API layer now normalizes returned iconUrl values through the configured API base URL, matching how JSON requests already reach the sidecar. This keeps the React menu platform-agnostic while making packaged app icons load from the live server.
Constraint: Tauri production builds do not share the sidecar API origin for normal image src attributes.
Rejected: Build absolute URLs on the server | the server does not know the final browser-visible base URL in every H5/Tauri startup mode.
Confidence: high
Scope-risk: narrow
Directive: Any future API-provided browser asset URL should be normalized through the configured API base URL before assigning it to DOM src/href fields.
Tested: cd desktop && bun run test -- src/api/client.test.ts src/api/openTargets.test.ts src/stores/openTargetStore.test.ts src/components/layout/OpenProjectMenu.test.tsx src/components/layout/TabBar.test.tsx
Tested: cd desktop && bun run lint
Memory files are produced by the agent during conversation, so the desktop settings surface should focus on browsing, search, and editing existing Markdown memory rather than offering a manual file factory.
Constraint: Project memory creation belongs to the conversation/runtime flow, not Settings.
Rejected: Keep the add box as a fallback | it implies users should manually seed runtime-owned memory and creates low-value UI clutter.
Confidence: high
Scope-risk: narrow
Directive: Do not reintroduce manual memory-file creation in Settings unless the runtime product flow explicitly changes.
Tested: cd desktop && bunx vitest run src/__tests__/memorySettings.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: Browser smoke on local Vite/server verified the create placeholder, create action, and duplicate-path error text are absent while file search and four memory files render.
Windows and Linux need to follow the same product rule as macOS: show icons from the user's installed applications instead of maintaining bundled brand artwork. The opener service now resolves command paths for Windows, walks from launcher scripts to the real IDE executable, extracts associated executable icons through the existing PNG endpoint, and resolves Linux .desktop Icon entries through common XDG application and icon theme locations.
Constraint: Do not redistribute third-party IDE trademark assets in the app bundle.
Constraint: Keep the frontend platform-agnostic by preserving the existing iconUrl contract.
Rejected: Add static per-IDE icon assets | impossible to keep complete and creates unnecessary trademark redistribution risk.
Confidence: medium
Scope-risk: moderate
Directive: Prefer platform installation metadata over bundled brand art when adding more opener targets.
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:server
Not-tested: Native Windows and Linux VM smoke tests for real installed IDE icons
Some memory-only project directories have no session JSONL metadata, so the previous label recovery still fell back to reversing sanitizePath(). That reverse mapping is lossy for Chinese characters, spaces, and punctuation, leaving paths rendered with repeated slashes. The memory API now performs a bounded filesystem-guided recovery: it walks only roots whose sanitized prefix can still match the project id and only descends through candidate directories that preserve that prefix match.
Constraint: Do not change the existing ~/.claude/projects/<sanitized>/memory storage layout.
Rejected: Full-home recursive search | too expensive and unnecessary because sanitizePath prefix matching gives a tight traversal boundary.
Confidence: high
Scope-risk: narrow
Directive: Keep project ids as opaque storage keys; recover display labels from real metadata or existing directories first.
Tested: bun test src/server/__tests__/memory.test.ts
Tested: local server curl /api/memory/projects against the real PicTacticAgent legacy memory project
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
Coding sessions need a native way to keep working until a stated completion condition is actually met. This adds a CLI /goal command, goal state and evaluator logic, and a headless-safe command filter so interactive and print-mode runs can both start a goal and continue when the evaluator says more work remains.
Constraint: The evaluator must work with saved third-party provider runtimes that may reject structured output schemas.
Constraint: Completion evidence must come from visible transcript text, not hidden goal prompts or assistant thinking blocks.
Rejected: Implement /goal as a bundled skill only | it would not own the query loop or support native continuation semantics.
Rejected: Expose all local-jsx commands to headless mode | interactive UI commands would become available in print mode.
Confidence: high
Scope-risk: moderate
Directive: Keep /goal evaluator evidence scoped to visible transcript text before adding richer completion signals.
Tested: bun test src/commands/headless.test.ts src/goals/goalState.test.ts src/goals/goalEvaluator.test.ts
Tested: bun run check:server
Tested: bun run verify
Tested: Real provider /goal smoke with MiniMax-M2.7-highspeed, including a two-turn continuation that logged Goal continuation #1 and completed with GOAL_SMOKE_LOOP_DONE.
Not-tested: Durable cross-process goal persistence for resumed sessions.
The CLI stores project directories with sanitizePath(), which intentionally replaces every non-alphanumeric byte with '-' for cross-platform safety. That storage key is lossy, so the desktop memory page must not treat it as the only source of display truth. The memory API now prefers the current cwd or the cwd/workDir/projectPath recorded in recent session JSONL files, and only falls back to the lossy reverse mapping when no metadata exists.
Constraint: Keep the existing ~/.claude/projects/<sanitized>/memory layout unchanged for CLI compatibility.
Rejected: Change sanitizePath to preserve Unicode | would break existing transcript and memory directory lookup semantics.
Confidence: high
Scope-risk: narrow
Directive: Treat project ids as storage keys, not user-facing path labels.
Tested: bun test src/server/__tests__/memory.test.ts
Tested: cd desktop && bunx vitest run src/__tests__/memorySettings.test.tsx
Tested: local server curl /api/memory/projects with a Chinese-space project path
Not-tested: bun run check:server still has two unrelated cron scheduler launcher timeout failures in src/server/__tests__/cron-scheduler-launcher.test.ts
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.