This brings the desktop branch/worktree launch flow into the local main checkout after validation in the feature worktree. The merge keeps the feature commit intact while preserving the newer main history.
Constraint: Local main already contains newer notification routing work
Confidence: high
Scope-risk: moderate
Tested: ALLOW_CLI_CORE_CHANGE=1 bun run quality:pr in the feature worktree before merge
Tested: /tmp business-flow script with dirty checkout, checked-out branch, isolated worktree, missing branch, non-git, missing directory, and real LLM session
Tested: agent-browser desktop UI flow across dirty, checked-out, non-git, branch search, recent-project, and Run scenarios
Desktop sessions now resolve repository launch intent before creation, so users can pick a branch, choose whether to isolate it in a worktree, and get stable error messages instead of silent branch-switch failures. The server owns the Git safety checks and session metadata so the UI and real agent runtime agree on the actual working directory.
Constraint: Direct branch switching must not overwrite or hide uncommitted user changes
Constraint: Desktop worktree branches must stay out of normal branch selection and recent-project labels
Rejected: Let the UI call git directly | server-side checks keep session metadata and runtime launch paths consistent
Rejected: Auto-switch dirty checkouts | users need an explicit isolated-worktree choice to protect local edits
Confidence: high
Scope-risk: moderate
Directive: Do not relax dirty-worktree or checked-out-branch guards without adding equivalent business-flow tests
Tested: ALLOW_CLI_CORE_CHANGE=1 bun run quality:pr
Tested: /tmp business-flow script with dirty checkout, checked-out branch, isolated worktree, missing branch, non-git, missing directory, and real LLM session
Tested: agent-browser desktop UI flow across dirty, checked-out, non-git, branch search, recent-project, and Run scenarios
Desktop notifications now carry a narrow target payload so clicking a permission, completion, or scheduled-task notification can reopen the matching tab and reconnect the session. macOS uses the native notification bridge for tap callbacks, while the shared desktop notification layer also accepts plugin action payloads where the platform exposes them.
Constraint: Notification clicks need to activate existing desktop tabs without adding a new navigation state channel.
Rejected: Store only notification ids and infer the active session later | stale notifications would point at the wrong session after tab changes.
Confidence: high
Scope-risk: moderate
Directive: Keep notification targets serializable and versioned through desktopNotifications before adding new target types.
Tested: bun run quality:pr with ALLOW_CLI_CORE_CHANGE=1; desktop unit tests; server tests; native sidecar build and cargo check; docs build.
Not-tested: Packaged Windows toast activation runtime; current fallback depends on Tauri notification action delivery.
Computer Use is useful when explicitly needed, but exposing its MCP tools by default creates unnecessary desktop-control surface for users who want coding-only sessions. This adds a shared disable path for CLI flags, environment, and desktop settings while keeping preauthorized app state in one config file.
The same change also preserves Windows and WSL shell startup behavior by applying the MSYS argument-conversion guard only on WSL-bound launches.
Constraint: Computer Use MCP must not be exposed to the Coding Agent when disabled
Constraint: Desktop settings and CLI sessions need to read the same persisted Computer Use config
Rejected: Environment-only disable switch | desktop users need a persistent Settings control
Rejected: Remove Computer Use setup entirely | enabled sessions still need the existing built-in MCP path
Confidence: high
Scope-risk: moderate
Directive: Keep every new Computer Use entrypoint wired through loadStoredComputerUseConfig or the CLI disable flag before adding MCP tools
Tested: bun test src/utils/computerUse/gates.test.ts src/utils/computerUse/preauthorizedConfig.test.ts src/server/__tests__/computer-use-api.test.ts src/utils/shell/wslInterop.test.ts desktop/src/pages/ComputerUseSettings.test.tsx
Tested: bun run check:server; bun run check:desktop; bun run check:docs; bun run check:policy; bun run check:native; git diff --check
Tested: SKIP_INSTALL=1 ./desktop/scripts/build-macos-arm64.sh; codesign verify; hdiutil verify; built CLI Computer Use E2E exposure and disable checks
Not-tested: Full screenshot/control action after granting macOS Screen Recording permission on this machine
Linux release builds use MenuBuilder for the shared tray menu, while MenuItemBuilder and SubmenuBuilder are only used in the macOS native menu bar. Gate only the macOS-only imports so Linux builds no longer warn about them and still compile the shared tray path.
Constraint: Linux builds compile the shared tray setup path and require MenuBuilder
Constraint: MenuItemBuilder and SubmenuBuilder are only referenced inside cfg(target_os = "macos")
Rejected: Gate the whole tauri::menu import on macOS | Linux still needs MenuBuilder for tray menu construction
Confidence: high
Scope-risk: narrow
Directive: Do not gate MenuBuilder by platform unless setup_system_tray is also made platform-specific
Tested: cargo fmt --check
Tested: git diff --check
Not-tested: Local Linux cargo check beyond system GTK/WebKit pkg-config setup; GitHub release Linux job is the verification surface
Release logs showed two warning classes after the packaging fixes: Linux builds warned about macOS-only menu imports, and tauri-action reported an unknown releaseAssetNamePattern input. Gate the native menu imports to macOS and use the action's supported assetNamePattern input so release logs stay clean and asset naming is actually applied.
Constraint: Linux release builds compile the shared Tauri lib without macOS-only menu usage
Constraint: tauri-action v0 accepts assetNamePattern, not releaseAssetNamePattern
Rejected: Ignore warning-only output | release candidates should not hide real signal behind avoidable warnings
Confidence: high
Scope-risk: narrow
Directive: Keep workflow input names aligned with tauri-action's published action schema
Tested: cargo fmt --check
Tested: cargo check --target aarch64-apple-darwin
Tested: git diff --check
Not-tested: Full GitHub release workflow after retag before pushing
The macOS notification bridge is compiled by build.rs outside Cargo's Rust target handling. On Apple Silicon, clang defaulted macos_notifications.o to arm64 even when Cargo was building x86_64, so the x64 release job ignored the object and missed the exported notification symbols.
Constraint: The ObjC bridge is linked manually through cargo:rustc-link-arg
Constraint: Release workflow builds both aarch64-apple-darwin and x86_64-apple-darwin from macOS runners
Rejected: Rely on clang default architecture | Apple Silicon defaults arm64 and breaks x86_64 linking
Confidence: high
Scope-risk: narrow
Directive: Keep native ObjC bridge compilation aligned with Cargo TARGET when adding platform bridge files
Tested: cargo fmt --check
Tested: git diff --check
Tested: cargo check --target aarch64-apple-darwin
Tested: cargo build --target x86_64-apple-darwin --release
Tested: file target/x86_64-apple-darwin/release/.../macos_notifications.o reports Mach-O 64-bit object x86_64
Not-tested: Full GitHub release workflow after retag before pushing
This records the user-facing 0.2.1 release notes and aligns the desktop package, Tauri config, Cargo manifest, and lockfile package version so the tag-triggered release workflow can publish the intended build.
Constraint: GitHub Release body is sourced from release-notes/v0.2.1.md in the tagged commit
Constraint: Keep provider and OpenAI login details weak in the release narrative because this release is centered on IM, notifications, context usage, and desktop stability
Rejected: Run scripts/release.ts directly | it creates a generic commit message and tag before review of the release narrative
Confidence: high
Scope-risk: narrow
Directive: Do not tag v0.2.1 from a commit that lacks release-notes/v0.2.1.md or mismatched desktop version files
Tested: bun run scripts/release.ts 0.2.1 --dry
Tested: cargo check
Tested: git diff --check
Not-tested: Live release gate with real provider credentials in this commit step
Telegram polling stopped consuming updates when all IM adapters were loaded into the same desktop sidecar process. Launch each IM platform as its own sidecar process so Telegram keeps an isolated long-polling consumer while Feishu, WeChat, and DingTalk manage their own SDK loops independently.
Constraint: Telegram Bot API long polling needs one healthy consumer per token and should not share lifecycle with other IM SDKs
Rejected: Keep one combined adapter process | reproduced pending Telegram updates until Telegram was launched alone
Confidence: high
Scope-risk: moderate
Directive: Keep IM adapter process ownership isolated unless cross-platform lifecycle tests prove a merged process is safe
Tested: cargo fmt --check
Tested: cargo check
Tested: bun run check:native
Tested: ./desktop/scripts/build-macos-arm64.sh
Tested: Computer Use launched built app and sent Telegram smoke message with pending_update_count=0
Not-tested: Automated Feishu, WeChat, or DingTalk live message delivery after process split
Telegram long polling can be consumed or disrupted by orphaned adapter sidecar processes left behind after desktop restarts or IM settings refreshes. Kill stale Unix claude-sidecar adapters processes before launching a new managed adapter sidecar so only one Telegram consumer remains active.
Constraint: Telegram Bot API long polling should have a single active consumer for a token
Rejected: Only restart the current tracked child | orphaned sidecars with PPID 1 are outside the tracked Tauri child handle
Confidence: high
Scope-risk: narrow
Directive: Keep adapter sidecar startup single-instance across platforms when adding new IM adapters
Tested: cargo fmt --check
Tested: cargo check
Tested: bun run check:native
Tested: git diff --check
Not-tested: Packaged macOS app relaunch with a real stale sidecar during automated test
Desktop users already receive native notifications for permission prompts, but long-running turns can finish with only a final Markdown reply while the app is out of focus. The completion path now treats a non-empty assistant reply as a notifiable terminal event and reuses the existing desktop notification settings, dedupe, and cooldown behavior.
Constraint: Notification permission must still be controlled by the existing desktopNotificationsEnabled setting and native OS authorization.
Rejected: Send from status idle events | idle is also used by prewarm, clear, stop, and other non-reply transitions.
Rejected: Notify on every message_complete | empty completions and post-stop tail completions would create noisy false positives.
Confidence: high
Scope-risk: narrow
Directive: Keep completion notifications tied to finalized assistant text, not generic idle/status transitions.
Tested: bun run test -- src/stores/chatStore.test.ts
Tested: bun run check:desktop
Not-tested: Manual macOS notification center click-through behavior
Desktop notifications were enabled by default and depended on the Tauri notification plugin for both permission state and delivery. On macOS the plugin reports desktop permission as granted and can hide foreground delivery failures, so authorization prompts could appear in the app without a real system notification.
This moves macOS permission and delivery through a native UserNotifications bridge, keeps notifications opt-in by default, and sends a test notification after successful authorization.
Constraint: Tauri notification plugin desktop permission state is always granted.
Constraint: macOS foreground delivery needs explicit native presentation handling.
Rejected: Keep using the plugin with retries | it still cannot report real macOS permission or delivery failures.
Confidence: high
Scope-risk: moderate
Directive: Do not remove the macOS bridge without verifying foreground banners from an installed app bundle.
Tested: cd desktop && bun run test --run src/lib/desktopNotifications.test.ts src/stores/settingsStore.test.ts src/__tests__/generalSettings.test.tsx src/stores/chatStore.test.ts
Tested: bun run check:desktop
Tested: bun run check:native
Tested: SKIP_INSTALL=1 ./scripts/build-macos-arm64.sh
Tested: codesign --verify --deep --strict --verbose=2 desktop/build-artifacts/macos-arm64/Claude Code Haha.app
Tested: hdiutil verify desktop/build-artifacts/macos-arm64/Claude Code Haha_0.2.0_aarch64.dmg
Tested: Computer Use macOS Settings and Bash authorization notifications, confirmed usernoted displayed banners.
Not-tested: Windows toast delivery on a real installed Windows build.
Desktop context inspection could send get_context_usage while a prewarmed CLI process existed but before the SDK socket was connected. That first control request sat in the pending outbound queue and could time out, leaving the composer spinner or a misleading empty-state context display. The server now waits for the SDK socket before sending control requests, and the UI renders the actual initial context snapshot instead of masking empty active-session data.
Constraint: CLI /context and desktop get_context_usage must stay on the shared collectContextData/analyzeContextUsage path
Rejected: Hide zero-token active snapshots in the component | that masks backend timing bugs and prevents showing real initial system/tool context
Confidence: high
Scope-risk: narrow
Directive: Do not queue control requests before the SDK socket is connected; let user messages keep the pending outbound behavior
Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "not queue control|context-only|prewarmed empty session|structured session inspection"
Tested: cd desktop && bun run test -- pages.test.tsx --test-name-pattern "EmptySession shows draft context|first-paint spinner|empty live session|live context usage|runtime model"
Tested: bun run check:server
Tested: cd desktop && bun run lint
Tested: real dev API prewarmed empty session returned 31,190 / 262,144 (12%) on first context-only inspection and 16ms on repeat
Tested: browser at http://127.0.0.1:5174/ showed 上下文用量 12% for an empty active session
A newly opened desktop session can return a live context snapshot before any
prompt content has been counted. When that snapshot is all zeroes, rendering it
as 0% makes the first real context update look like an unexplained jump. The
composer now keeps that empty live snapshot in the pending state while still
using its model metadata.
Constraint: Live context snapshots can be initialized before any user-visible context exists
Rejected: Render 0% for zero-token live snapshots | it repeats the misleading empty-session state
Confidence: high
Scope-risk: narrow
Directive: Do not show numeric context usage for empty zero-token sessions
Tested: cd desktop && bun run test -- pages.test.tsx
Tested: cd desktop && bun run lint
DingTalk bot unbind was routed through the generic adapter config update,
but undefined credential fields are dropped during JSON serialization. That
left stored Client ID and Secret values intact after clicking the unbind
button. Match the working WeChat path with a dedicated server endpoint and
frontend API call so credential removal is explicit.
Constraint: JSON request bodies cannot preserve undefined fields for generic config clearing
Rejected: Keep using updateConfig with undefined fields | serialized requests omit the fields before the server can clear them
Confidence: high
Scope-risk: narrow
Directive: Keep credential-clearing flows on explicit unbind endpoints rather than relying on partial config merges
Tested: bun test src/server/__tests__/adapters.test.ts
Tested: cd desktop && bun run test src/stores/adapterStore.test.ts
Tested: bun run check:desktop
Tested: bun run check:server
Not-tested: bun run quality:pr is blocked by repository policy requiring allow-cli-core-change approval for the broader branch impact
Bring the detached worktree terminal improvements into the local main line so desktop sessions can open a bottom terminal in the current project directory and still promote it to a full tab when needed.
Constraint: Local main already contains additional desktop fixes after the worktree base
Confidence: high
Scope-risk: moderate
Tested: bun run check:desktop
Tested: git diff --check
Not-tested: bun run quality:pr is blocked by existing branch policy requiring allow-cli-core-change approval
Desktop terminal access should behave like an IDE: active project sessions open a bottom panel in the session working directory while keeping a full terminal tab available for dedicated use. The panel has constrained resizing and cleanup so session tab state remains isolated, and terminal guidance points users to the bundled claude-haha command for extension setup.
Constraint: Desktop bundles the user-facing CLI as claude-haha while claude-sidecar remains internal
Rejected: Always opening a standalone terminal tab | loses the current project context and diverges from common IDE behavior
Rejected: Exposing claude-sidecar in terminal guidance | it is an internal launcher, not the supportable user command
Confidence: high
Scope-risk: moderate
Directive: Keep bottom terminals keyed by session id and pass session workDir/projectPath into spawned terminals
Tested: bun run check:desktop
Tested: git diff --check
Tested: Computer Use E2E against built macOS app during implementation
Not-tested: bun run quality:pr is blocked by existing branch policy requiring allow-cli-core-change approval
The desktop composer now treats draft and not-yet-running sessions as pending
until a real CLI context snapshot exists. Context inspection has a lightweight
context-only path so the composer and /context panel do not wait on unrelated
usage or MCP status work, and desktop boot no longer blocks the first shell
render on tab restoration.
Constraint: Empty composer state has no live CLI context to inspect until a session starts
Rejected: Display 0% before live context exists | it implied one user message consumed the fixed prompt/tool baseline
Confidence: high
Scope-risk: moderate
Directive: Do not show numeric context usage without a real context snapshot
Tested: cd desktop && bun run test -- pages.test.tsx
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: bun run check:server
Tested: Browser reload at http://127.0.0.1:5174 with cleared open-tabs showed pending context and no /inspection request
Bring the runtime diagnostics worktree back onto local main while preserving the existing local main commits for provider context windows and IM localization.
Constraint: Preserve both independent local main commits and the diagnostics worktree commit.
Confidence: high
Scope-risk: moderate
Directive: Keep diagnostics capture non-blocking and sanitized when modifying this area again.
Tested: merged cleanly on local main after prior check:server, check:desktop, and agent-browser diagnostics export verification
Diagnostics exports previously preserved only compact summaries, which made user-uploaded issue bundles hard to debug. This records richer sanitized details from server, CLI, SDK, browser, React, and desktop API failure paths while keeping request bodies and secrets out of client-side reports.
Constraint: Diagnostic bundles must be useful for GitHub issues without including chat contents, file contents, full environments, or API keys.
Rejected: Only expand the Settings UI summary | the exported bundle would still miss hidden runtime failures.
Confidence: high
Scope-risk: moderate
Directive: Keep diagnostics write paths best-effort and non-blocking; do not add request-body capture without a redaction review.
Tested: bun test src/server/__tests__/diagnostics-service.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run test src/api/client.test.ts src/__tests__/diagnosticsSettings.test.tsx --run
Tested: bun run check:server
Tested: bun run check:desktop
Tested: agent-browser E2E on ports 37652/41752 captured client_unhandled_rejection and exported a redacted tar.gz bundle
The Chinese settings UI should use localized platform names directly instead of appending English names in parentheses, which made the tabs look visually awkward.
Constraint: Only the Chinese locale needed this presentation adjustment
Confidence: high
Scope-risk: narrow
Reversibility: clean
Tested: cd desktop && bunx vitest run src/i18n/index.test.tsx
Not-tested: Full desktop quality gate, because this is a two-string locale-only change
This captures the pending worktree fixes before applying them to the
current local main. The changes tighten IM adapter path and credential
handling, preserve retry behavior for failed desktop notifications, and
make Azure/OpenAI provider auth and stop reasons reflect actual runtime
state.
Constraint: Worktree was detached from an older local main with pending uncommitted fixes
Rejected: Merge the detached HEAD directly | would also replay unrelated stale history
Rejected: Leave notification dedupe as fire-and-forget | failed sends consumed retry keys
Confidence: high
Scope-risk: broad
Directive: Keep adapter absolute-path matching constrained to configured work roots
Tested: git diff --check
Not-tested: full quality gate before local main integration
Remote main already contains PR #217. Local main had provider context-window and auth-strategy work, so this merge keeps both context resolvers and reconciles the desktop Bun lockfile with the merged desktop package manifest. The accidental remote .nvimlog artifact is excluded from the merge result.
Constraint: Local main was 15 commits ahead and 8 commits behind origin/main after PR #217 was merged remotely
Rejected: Fast-forward pull | local main had unpushed commits
Rejected: Keep remote desktop lockfile verbatim | failed bun install --frozen-lockfile against the merged package.json
Confidence: high
Scope-risk: broad
Directive: Keep provider-configured context windows ahead of OpenAI OAuth model windows so explicit user/provider settings win
Tested: bun install --frozen-lockfile; cd desktop && bun install --frozen-lockfile; git diff --check; bun run check:server; bun run check:desktop; ALLOW_CLI_CORE_CHANGE=1 bun run quality:pr
Not-tested: live provider OAuth login with a real OpenAI account
PR 217 combined OpenAI OAuth model options with env-configured provider models, but the picker path returned before appending third-party model aliases. The desktop workspace test also exercised Prism highlighting for every expanded line, which was costly enough to time out on CI runners. The docs job did not need the root React Vite plugin; the desktop package owns that dependency.\n\nConstraint: PR Quality must pass server, desktop, and docs jobs before merge.\nRejected: Increase Vitest timeout | keeps the expensive render path and hides the CI regression.\nRejected: Add root Vite 8 peer dependencies | root only builds VitePress docs; React Vite plugin belongs in desktop/.\nConfidence: high\nScope-risk: narrow\nTested: npm ci --ignore-scripts; npm run docs:build; bun test src/server/__tests__/settings.test.ts src/server/__tests__/conversation-service.test.ts; bun run check:server; bun run check:desktop\nNot-tested: live provider OAuth login
Provider model context windows now feed the composer-level context indicator, so users can see current usage without opening slash panels. The indicator refreshes when the session runtime model changes, keeps a stable loading placeholder, and marks estimate fallback data during reconnect or compaction windows.
Constraint: The desktop UI must use the real session inspection API and session-scoped runtime selection instead of mocked or static context values.
Rejected: Keep context details only in /context or /status | the user explicitly wanted an always-visible composer indicator.
Rejected: Reuse stale context after model switches | different provider windows make identical percentages misleading.
Confidence: high
Scope-risk: moderate
Directive: Do not remove the runtimeSelectionKey refresh path without real multi-provider browser verification.
Tested: cd desktop && bun run test -- pages.test.tsx
Tested: bun run check:desktop
Tested: KEEP_ARTIFACTS=1 desktop/scripts/e2e-context-usage-live-agent-browser.sh
Not-tested: Live browser E2E with every configured provider in the committed script; separate agent-browser sweep verified four providers locally.
Desktop notification permission can appear granted while macOS still suppresses a foreground banner, so blocking permission prompts now also request OS-level window attention. The notification sender keeps the native notification path, but prompt callers can ask the window manager for attention when silence would block user progress.
The desktop package also declares the existing qrcode runtime dependency used by AdapterSettings so production desktop builds can resolve that import.
Constraint: Tauri window attention requires an explicit capability grant in packaged builds.
Constraint: macOS may suppress foreground notification banners even when notification APIs report success.
Rejected: Replace native notifications with a custom in-app sound or overlay | the notification feature should stay OS-owned and already has an in-app permission card.
Confidence: high
Scope-risk: narrow
Directive: Keep requestAttention limited to blocking prompts unless another flow truly needs persistent OS attention.
Tested: bun run test -- src/lib/desktopNotifications.test.ts src/stores/chatStore.test.ts
Tested: bun run check:desktop
Tested: bun run check:native
Tested: git diff --check
Not-tested: bun run quality:pr | blocked by existing branch-wide CLI core policy requiring allow-cli-core-change approval
DingTalk can receive interactive card callbacks, but the card UI still depends on a published template, so the adapter now supports a template-backed card path with text commands as the reliable fallback. Telegram and WeChat use the same allow-once, allow-always, and deny semantics so manual authorization behaves the same across platforms.
Constraint: DingTalk button rendering requires an operator-provided interactive card template id
Rejected: Treat the existing AI streaming card template as a permission card | it cannot guarantee visible action buttons
Confidence: high
Scope-risk: moderate
Directive: Do not remove the text approval fallback unless DingTalk card template provisioning is guaranteed
Tested: bun test adapters/common/__tests__/permission.test.ts adapters/dingtalk/__tests__/permission-card.test.ts adapters/telegram/__tests__/telegram.test.ts adapters/common/__tests__/config.test.ts src/server/__tests__/adapters.test.ts
Tested: bunx tsc -p adapters/tsconfig.json --noEmit
Tested: bun run check:adapters
Tested: cd desktop && bun run build
Tested: bun run check:server
Tested: bun run check:docs
Tested: git diff --check
Not-tested: bun run quality:pr is blocked by existing CLI core approval policy; see artifacts/quality-runs/2026-05-03T13-19-56-232Z/report.md
WeChat and DingTalk were using different pairing, attachment, and response
state paths, which made the new IM channels behave differently from Feishu
and Telegram. Align the shared pairing model, wire inbound media into the
existing attachment bridge, and map platform response capabilities to their
real APIs: WeChat block streaming plus typing, DingTalk AI Card streaming.
Constraint: WeChat iLink exposes typing and block streaming, but no editable message/card streaming API
Constraint: DingTalk streaming depends on the AI Card create/deliver/stream/finalize lifecycle
Rejected: Fake DingTalk typing with standalone markdown | it would add chat noise instead of platform state
Rejected: Auto-pair WeChat after QR login | it bypasses the shared IM pairing model
Confidence: high
Scope-risk: moderate
Directive: Keep WeChat streaming as block-send unless iLink adds editable messages; keep DingTalk streaming on AI Card APIs
Tested: bun run check:adapters; bun run check:server; cd desktop && bun run test src/stores/adapterStore.test.ts; cd desktop && bun run build; bunx tsc -p adapters/tsconfig.json --noEmit; git diff --check
Not-tested: Live WeChat and DingTalk platform smoke with real production credentials
DingTalk uses QR registration to store client credentials, then reuses the existing IM pairing and session bridge. The merged main implementation keeps the existing WeChat QR binding path intact while adding DingTalk as a peer IM platform. The default IM workdir now falls back to the local user working directory so a newly bound chat can start immediately even when recent-project history is empty.
Constraint: Local main already carries WeChat IM, so the merge keeps WeChat config, QR binding, sidecar args, and unbind behavior intact while adding DingTalk.
Rejected: Keep empty defaultProjectDir as project-picker-only | newly bound IM users can hit a dead end with no recent projects.
Confidence: high
Scope-risk: moderate
Directive: Keep IM platform unions synchronized across config, pairing, sidecar args, desktop settings, and docs.
Tested: bun test common/ dingtalk/ wechat/; bun test src/server/__tests__/adapters.test.ts; cd adapters && bunx tsc --noEmit; bun run check:policy; bun run check:server; bun run check:desktop; bun run check:adapters; bun run check:native; bun run check:docs
Not-tested: quality:pr full gate was blocked by existing local main CLI-core diff requiring allow-cli-core-change maintainer approval; live post-fix DingTalk second-message delivery was not repeated after the merge.
Bring the provider context-window, auth-secret masking, and explicit auth-strategy worktree commits onto main after local verification. The merge keeps main's notification and docs history intact while preserving the provider fixes as separate commits.
Constraint: main has diverged with local desktop notification and docs commits, so this cannot be fast-forwarded safely.
Confidence: high
Scope-risk: moderate
Directive: Keep provider auth behavior configured through preset authStrategy rather than implicit env-name guesses.
Tested: ALLOW_CLI_CORE_CHANGE=1 bun run quality:pr
Tested: bun run quality:gate --mode baseline --allow-live --provider-model minimax:main:minimax-main
# Conflicts:
# desktop/src/pages/Settings.tsx
WeChat needs a QR-paired path instead of bot-token setup, so the adapter layer now includes the iLink protocol calls, desktop pairing UI, server-side bind/unbind APIs, and shared IM command behavior. Empty project history falls back to the user's default work directory so mobile /new works without pre-opening a desktop project.
Constraint: Tencent iLink login returns a URL that the desktop UI must render as a QR image locally
Constraint: IM adapters should keep /new, /projects, status, permission, and default workdir behavior consistent across WeChat, Feishu, and Telegram
Rejected: Require users to paste absolute project paths for first WeChat sessions | mobile onboarding should work from the default user working directory
Confidence: high
Scope-risk: moderate
Directive: Do not change WeChat polling back to overlapping intervals; getupdates is a long-poll endpoint and must remain serialized
Tested: Real WeChat QR scan, inbound /status, outbound reply, and unbind E2E
Tested: bun run check:adapters
Tested: bun run quality:pr
Not-tested: Re-scan live WeChat after the default-workdir fallback tweak; covered by adapter config tests and PR gate
Provider presets now carry an explicit auth strategy so Anthropic-compatible services can choose the right Claude Code environment shape instead of relying on one global variable. The desktop editor exposes the same strategy, keeps settings.json previews in sync, and applies the strategy to connection tests as well as runtime env sync.
Constraint: Third-party Anthropic-compatible providers do not agree on ANTHROPIC_API_KEY versus ANTHROPIC_AUTH_TOKEN.
Rejected: Force all third-party providers onto one env var | breaks providers that require bearer auth, dual variables, or dummy local auth.
Confidence: high
Scope-risk: moderate
Directive: Add new provider presets with explicit authStrategy; do not infer auth behavior from model names alone.
Tested: bun test src/server/__tests__/providers.test.ts src/server/__tests__/providers-real.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run lint
Tested: ALLOW_CLI_CORE_CHANGE=1 bun run quality:pr
Tested: bun run quality:gate --mode baseline --allow-live --provider-model minimax:main:minimax-main
Tested: agent-browser provider edit flow on http://127.0.0.1:19811
Not-tested: Live successful calls for every third-party provider with real credentials.
Provider settings JSON previously masked only values that matched the current form API key. Switching providers or editing stale settings could leave ANTHROPIC_API_KEY visible while ANTHROPIC_AUTH_TOKEN was masked, even though both are sensitive auth env vars.
The settings preview now masks Anthropic API key and auth token fields by key name, restores placeholders from the previous JSON value per field, and strips provider-managed env vars before merging the active provider preview. This keeps historical provider auth values from leaking into the editable JSON surface.
Constraint: Claude Code uses ANTHROPIC_API_KEY for x-api-key auth and ANTHROPIC_AUTH_TOKEN for Bearer auth, so the fix keeps runtime semantics separate from UI redaction.
Rejected: Collapse all providers onto one env var | direct Anthropic and gateway auth paths have different documented headers.
Confidence: high
Scope-risk: narrow
Directive: Treat provider auth env vars as secrets by field name in UI surfaces, not by comparing against the current form API key.
Tested: cd desktop && bun run test -- src/lib/__tests__/providerSettingsJson.test.ts
Tested: cd desktop && bun run lint
Tested: bun run check:desktop
Tested: agent-browser opened DeepSeek provider edit modal and verified Settings JSON shows ANTHROPIC_AUTH_TOKEN as placeholder with no ANTHROPIC_API_KEY entry.
Teach provider presets and saved providers to carry per-model context windows so auto-compact uses provider-specific limits instead of a blanket fallback. The desktop provider form now keeps these limits in an advanced context section and syncs the generated settings preview with model changes.
Constraint: Third-party provider APIs do not consistently expose machine-readable model context windows.
Rejected: Keep a single 100K/200K fallback | modern provider windows vary from 128K to 1M+ and the fallback caused late or early compaction.
Confidence: high
Scope-risk: moderate
Directive: Do not change provider compact thresholds without re-running real provider long-context checks.
Tested: bun test src/services/compact/autoCompact.test.ts; bun test src/server/__tests__/provider-presets.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/providers-real.test.ts; bun run check:server; bun run check:desktop; bun run check:policy; bun run check:native; MiniMax/Kimi/DeepSeek live long-context probes; agent-browser provider form flow.
Not-tested: quality:pr policy gate still requires explicit allow-cli-core-change for CLI core edits.
Add native system notifications as a desktop-wide attention channel for permission prompts and scheduled task completion. The implementation keeps notification presentation owned by the OS, adds a user-facing enable switch with permission handling, and lets scheduled tasks choose desktop notifications without routing that channel through IM adapters.
Constraint: Notifications must use OS-native APIs without custom sound playback.
Constraint: Desktop channel is local-only and must not be sent through IM adapter delivery.
Rejected: Browser Notification API | not reliable inside the packaged Tauri desktop runtime.
Rejected: Treat desktop as an IM channel | would leak a local-only channel into server-side adapter sending.
Confidence: high
Scope-risk: moderate
Directive: Keep notification styling at the OS layer; business code should only provide title, body, dedupe, and routing decisions.
Tested: bun run check:desktop
Tested: bun run quality:pr
Tested: Computer Use macOS debug app verification for settings toggle, permission prompt, scheduled task desktop channel, and task-run polling dedupe
Not-tested: Windows and Linux native runtime smoke tests on physical hosts
This release marks the desktop app's shift from a thin CLI wrapper toward a fuller local Coding Agent workspace. It carries the reviewed v0.2.0 release notes, bumps desktop package metadata, and refreshes Cargo.lock from the pinned Tauri stack before publishing the tag-driven GitHub Actions release.
Constraint: GitHub Release body is sourced from release-notes/v0.2.0.md in the tagged commit
Constraint: Desktop releases are built remotely from the pushed v0.2.0 tag
Rejected: Publish as v0.1.10 | the release consolidates several desktop workflow and quality-gate milestones, so v0.2.0 better communicates the scope
Confidence: high
Scope-risk: narrow
Directive: Keep the v0.2.0 tag on this commit unless the release notes or version metadata are changed together
Tested: bun run scripts/release.ts 0.2.0 --dry
Tested: bun run quality:gate --mode release --allow-live --provider-model codingplan:main:codingplan-main --provider-model minimax:main:minimax-main (18 passed, 0 failed, 0 skipped)
Desktop failures were previously hard to debug from issue reports because server-side and CLI startup details were only partially visible in the UI. This adds a dedicated cc-haha diagnostics store with sanitized structured events, runtime error summaries, a Settings diagnostics view, and an exportable bundle that users can attach to reports.
Constraint: Diagnostic exports must not include chat content, file contents, full environment variables, API keys, bearer tokens, cookies, or OAuth tokens.
Rejected: Export raw server logs | easier to debug but too likely to leak secrets and private workspace data.
Rejected: Keep diagnostics only in transient UI errors | still leaves maintainers unable to diagnose later GitHub issues.
Confidence: high
Scope-risk: moderate
Directive: Do not add raw transcript, prompt, attachment, or environment dumps to diagnostics without a separate privacy review.
Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/diagnostics-service.test.ts
Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/conversation-service.test.ts
Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/conversations.test.ts
Tested: cd desktop && bun run test src/__tests__/diagnosticsSettings.test.tsx --run
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: agent-browser E2E on local server/dev UI for CLI startup failure, provider test failure, diagnostics tab, copy summary, export bundle, and tar/secret scan
Not-tested: Destructive clear-logs button in browser E2E; local deletion was intentionally not clicked.
Large desktop histories were making session discovery parse every JSONL
before pagination, while automatic title updates could still overwrite
manual names during resumed sessions. This keeps listing bounded to the
requested page, preserves custom titles, and blocks deleted placeholder
sessions from being recreated by prewarm startup.
Constraint: Desktop session storage remains JSONL-compatible with the CLI
Rejected: Virtualize the sidebar in this patch | does not fix backend JSONL parsing cost
Rejected: Disable title generation globally | would regress useful titles for unnamed sessions
Confidence: high
Scope-risk: moderate
Directive: Do not reintroduce all-file JSONL parsing on /api/sessions list paths without a heavy-session benchmark
Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/title-service.test.ts src/server/__tests__/sessions.test.ts
Tested: ANTHROPIC_API_KEY=test-key bun test src/server/__tests__/conversations.test.ts
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: heavy local preview with 240 sessions, 320 messages each, and 40 restored tabs
Not-tested: native packaged desktop runtime under Windows with the same heavy fixture
Related: https://github.com/NanmiCoder/cc-haha/issues/237
Related: https://github.com/NanmiCoder/cc-haha/issues/248
The fix was developed and UI-verified in a detached Codex worktree, then merged locally so main carries the same tested request-shape behavior.
Constraint: User requested local main integration without pushing
Confidence: high
Scope-risk: narrow
Tested: git status before merge was clean
Tested: merged commit verification retained in history
Not-tested: Post-merge full suite
DeepSeek-compatible Anthropic endpoints reject requests that combine an explicit disabled-thinking block with an effort value. The desktop UI can create that shape when users disable thinking and keep medium effort selected, so the request builder now omits effort whenever it will send explicit disabled thinking.
Constraint: DeepSeek-compatible Anthropic endpoints reject disabled thinking with effort
Rejected: Only patch Desktop provider settings | hidden title and side request paths still share the request builder
Confidence: high
Scope-risk: narrow
Directive: Keep explicit disabled-thinking providers from sending effort unless the upstream contract changes
Tested: UI repro on pre-fix DeepSeek provider path reproduced the 400
Tested: desktop/scripts/e2e-deepseek-thinking-ui-agent-browser.sh
Tested: bun test src/utils/__tests__/thinking.test.ts src/server/__tests__/provider-presets.test.ts src/server/__tests__/title-service.test.ts
Tested: git diff --check
Not-tested: Real DeepSeek API key live call
The session inspector reused a one-off light palette, so /status, /cost,
and /context could render bright panels inside the dark desktop shell.
This moves the inspector and MCP switch control onto theme-scoped tokens
while preserving the existing light-theme look.
Constraint: Desktop dark mode must keep the existing surface hierarchy and avoid changing global brand colors.
Rejected: Recolor the shared Material palette | too broad for a component-specific regression.
Confidence: high
Scope-risk: narrow
Directive: Keep slash inspector surfaces on semantic theme tokens; do not reintroduce fixed light hex classes.
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test src/__tests__/pages.test.tsx src/__tests__/mcpSettings.test.tsx
Tested: cd desktop && bun run build
Tested: agent-browser screenshots for /status, /usage, /context, and MCP settings on http://127.0.0.1:5184/