feat(desktop): updater progress indicator and reworked failure dialog - #222
Conversation
… download fallbacks
📝 WalkthroughWalkthroughAdds download-progress tracking (percent→currentProgress), broadcasts progress to all BrowserWindows via setProgressBar and reapplies on ready-to-show, clears progress on terminal/error events, replaces generic alerts with localized, reason-specific failure dialogs with action buttons (retry, open download page, later), and expands tests/labels accordingly. Changes
Sequence Diagram(s)sequenceDiagram
participant Updater as Updater
participant Main as Main Process
participant Window as BrowserWindow
participant User as User
Updater->>Main: emit "download-progress" (info.percent)
Main->>Main: currentProgress = info.percent / 100
Main->>Window: broadcast setProgressBar(currentProgress)
Window->>User: OS/taskbar progress reflects download
Updater->>Main: emit "update-downloaded" / "update-not-available" / "update-cancelled"
Main->>Main: clear currentProgress
Main->>Window: broadcast setProgressBar(-1)
Updater->>Main: emit "error" (reason)
Main->>Main: clear currentProgress
Main->>Window: broadcast setProgressBar(-1)
Main->>User: show localized failure dialog (message + detail, buttons)
User->>Main: click "retry"
Main->>Updater: call checkForUpdates(alertOnFail)
Main->>Main: log rejection on failure
User->>Main: click "open download page"
Main->>User: open URL (releases/latest)
Note over Main,Window: New windows on "ready-to-show" reapply setProgressBar(currentProgress)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request implements progress bar tracking for the auto-updater and introduces more detailed, localized error dialogs for update and installation failures. Feedback focuses on improving maintainability and robustness by centralizing the release URL as a constant, implementing a custom type guard for error handling instead of relying on instanceof Error, and using regular expressions in tests that inspect source code to ensure they are resilient to formatting changes.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/desktop-electron/src/main/index-updater-source.test.ts`:
- Around line 52-54: The test assumes the ready-to-show hook exists but slices
source using hookIndex which can be -1; add an explicit guard asserting
hookIndex !== -1 (e.g., expect(hookIndex).not.toBe(-1) or throw with a clear
message) before calling source.slice so the test fails clearly if the
win.once("ready-to-show") hook is missing; update the test around the
hookIndex/source.slice/reapplySlice logic to perform this check prior to
slicing.
In `@packages/desktop-electron/src/main/index.ts`:
- Around line 685-686: The call to shell.openExternal(LATEST_RELEASE_URL)
(invoked from checkForUpdates and the menu handler) can reject and is currently
awaited with no local error handling, causing unhandled promise rejections; wrap
the shell.openExternal(...) invocation inside a try/catch (or attach a .catch)
within checkForUpdates (and the menu handler invocation) to catch errors and log
them (e.g., via processLogger.error or console.error) and avoid propagating the
rejection, ensuring the function still returns/continues gracefully.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 65aaab6e-2f12-4ea3-913b-f93f7fa73d03
📒 Files selected for processing (2)
packages/desktop-electron/src/main/index-updater-source.test.tspackages/desktop-electron/src/main/index.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: unit-windows-opencode-server-tools
- GitHub Check: unit-windows-app
- GitHub Check: unit-windows-desktop
- GitHub Check: typecheck
- GitHub Check: unit-windows-opencode-session
- GitHub Check: unit-windows-opencode-config-project
- GitHub Check: unit-opencode
- GitHub Check: unit-app
- GitHub Check: unit-desktop
- GitHub Check: smoke-macos-arm64
- GitHub Check: analyze-js-ts
- GitHub Check: e2e-artifacts
🧰 Additional context used
📓 Path-based instructions (1)
packages/desktop-electron/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (packages/desktop-electron/AGENTS.md)
Renderer process should only call
window.apifromsrc/preload
Files:
packages/desktop-electron/src/main/index.tspackages/desktop-electron/src/main/index-updater-source.test.ts
🧠 Learnings (10)
📚 Learning: 2026-04-20T14:36:08.774Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/desktop-electron/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:08.774Z
Learning: Applies to packages/desktop-electron/src/main/ipc.ts : Main process should register IPC handlers in `src/main/ipc.ts`
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-20T14:21:56.373Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 71
File: packages/app/src/components/session/session-status-connections.tsx:146-147
Timestamp: 2026-04-20T14:21:56.373Z
Learning: In the Astro-Han/pawwork repository (SolidJS app), `sync.data.config` is always initialized to `{}` at `packages/app/src/context/global-sync.tsx` line 71 and is never `undefined` at runtime. Non-optional property access like `sync.data.config.plugin` is intentional and consistent with the pattern used in `packages/app/src/components/status-popover-body.tsx` line 243. Do not flag `sync.data.config.plugin` as needing optional chaining.
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : In `Effect.gen` / `Effect.fn`, prefer `yield* new MyError(...)` over `yield* Effect.fail(new MyError(...))` for direct early-failure branches
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-22T08:49:47.800Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/desktop-electron/src/main/index-sidecar-source.test.ts:3-11
Timestamp: 2026-04-22T08:49:47.800Z
Learning: In `packages/desktop-electron/src/main/index-sidecar-source.test.ts` (Astro-Han/pawwork), the test intentionally uses `expect(source).toContain` / `expect(source).not.toContain` string matching against the raw `index.ts` source text as a lightweight sidecar contract guard. The maintainer has explicitly chosen not to introduce an AST parser (e.g., `babel/parser` or acorn) for this purpose. Do not flag these string-based assertions as fragile or suggest converting them to AST-based matching.
Applied to files:
packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-22T09:32:54.556Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Applied to files:
packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use locator assertions like `toBeVisible()`, `toHaveCount(0)`, and `toHaveAttribute(...)` for normal UI state verification
Applied to files:
packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Prefer fluent helpers and drivers when they make intent obvious and reduce locator-heavy noise in tests
Applied to files:
packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use lowercase, descriptive test names (e.g., 'sidebar can be toggled')
Applied to files:
packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use SCREAMING_SNAKE_CASE for constants in tests
Applied to files:
packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use camelCase for variable names in tests
Applied to files:
packages/desktop-electron/src/main/index-updater-source.test.ts
🔇 Additional comments (5)
packages/desktop-electron/src/main/index.ts (3)
90-103: Nice extraction of progress-bar state management.
currentProgress,applyProgressBar, andclearProgressBarkeep the updater progress flow centralized and easy to reason about.
361-365: Good handling for windows opened mid-download.Reapplying progress on
ready-to-showensures newly opened windows stay in sync with ongoing download state.
611-621: Progress lifecycle wiring is solid.Broadcasting on
"download-progress"and clearing on terminal updater events (including"error") matches expected UX behavior across windows.packages/desktop-electron/src/main/index-updater-source.test.ts (2)
28-49: Great contract coverage for updater progress behavior.These tests lock in the core source-level guarantees (broadcasting, terminal clearing, and post-early-return listener registration) with clear intent.
58-91: Dialog-flow contract assertions are strong and well-targeted.The added checks for reason-specific copy, button ordering/defaults, retry path behavior, and install-failure button set give good regression protection for the new UX.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/desktop-electron/src/main/index.ts`:
- Around line 686-692: The current code in checkForUpdates calls itself
recursively when the dialog choice is "Retry" (response.response === 0), which
nests async frames; refactor checkForUpdates to use an explicit iterative retry
loop instead: introduce a local loop (e.g., while/retryLoop) inside the
checkForUpdates function that performs the update attempt, shows the dialog, and
on "Retry" uses continue to re-run the attempt rather than calling
checkForUpdates again, on "Cancel" or success breaks out, and preserve the
existing error handling (catch block calling logger.error("retry after update
failure failed", error)) and the alertOnFail parameter behavior. Ensure you
update the blocks that currently check response.response === 0 / === 1 to use
continue/break semantics and remove the recursive await
checkForUpdates(alertOnFail) call.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 56f3fee3-1116-43c1-9dbc-188e7c993d16
📒 Files selected for processing (1)
packages/desktop-electron/src/main/index.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: unit-windows-desktop
- GitHub Check: unit-windows-opencode-config-project
- GitHub Check: unit-windows-app
- GitHub Check: unit-windows-opencode-session
- GitHub Check: typecheck
- GitHub Check: unit-windows-opencode-server-tools
- GitHub Check: unit-app
- GitHub Check: unit-desktop
- GitHub Check: unit-opencode
- GitHub Check: smoke-macos-arm64
- GitHub Check: e2e-artifacts
- GitHub Check: analyze-js-ts
🧰 Additional context used
📓 Path-based instructions (1)
packages/desktop-electron/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (packages/desktop-electron/AGENTS.md)
Renderer process should only call
window.apifromsrc/preload
Files:
packages/desktop-electron/src/main/index.ts
🧠 Learnings (8)
📚 Learning: 2026-04-20T14:21:56.373Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 71
File: packages/app/src/components/session/session-status-connections.tsx:146-147
Timestamp: 2026-04-20T14:21:56.373Z
Learning: In the Astro-Han/pawwork repository (SolidJS app), `sync.data.config` is always initialized to `{}` at `packages/app/src/context/global-sync.tsx` line 71 and is never `undefined` at runtime. Non-optional property access like `sync.data.config.plugin` is intentional and consistent with the pattern used in `packages/app/src/components/status-popover-body.tsx` line 243. Do not flag `sync.data.config.plugin` as needing optional chaining.
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : In `Effect.gen` / `Effect.fn`, prefer `yield* new MyError(...)` over `yield* Effect.fail(new MyError(...))` for direct early-failure branches
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-22T08:49:47.800Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/desktop-electron/src/main/index-sidecar-source.test.ts:3-11
Timestamp: 2026-04-22T08:49:47.800Z
Learning: In `packages/desktop-electron/src/main/index-sidecar-source.test.ts` (Astro-Han/pawwork), the test intentionally uses `expect(source).toContain` / `expect(source).not.toContain` string matching against the raw `index.ts` source text as a lightweight sidecar contract guard. The maintainer has explicitly chosen not to introduce an AST parser (e.g., `babel/parser` or acorn) for this purpose. Do not flag these string-based assertions as fragile or suggest converting them to AST-based matching.
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-20T14:36:08.774Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/desktop-electron/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:08.774Z
Learning: Applies to packages/desktop-electron/src/main/ipc.ts : Main process should register IPC handlers in `src/main/ipc.ts`
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-20T17:03:40.214Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 73
File: packages/opencode/src/cli/cmd/tui/context/sync.tsx:486-489
Timestamp: 2026-04-20T17:03:40.214Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/cli/cmd/tui/context/sync.tsx`), `sync.ready` returning `true` when `process.env.OPENCODE_FAST_BOOT` is set is intentional. The plugin-facing data properties `state.config` (initialized to `{}`) and `state.provider` (initialized to `[]`) expose safe-empty defaults, so they are safe to access before bootstrap completes. Do not flag these as needing null-guards or conditional patterns to match `vcs` — the difference is intentional because `vcs` starts as `undefined` while the others have initialized defaults. Changing this would alter the plugin API contract without a concrete failing case.
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-24T05:39:56.086Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 208
File: packages/app/src/components/prompt-input.tsx:1569-1611
Timestamp: 2026-04-24T05:39:56.086Z
Learning: In Astro-Han/pawwork `packages/app/src/components/prompt-input.tsx`, after the composer unification in PR `#208` (fixed in commit 5d810aa):
- `SendButton.disabled` does NOT gate on `store.mode !== "normal"`. Shell mode has a fully visible, clickable orange submit button that calls `handleSubmit` directly (same path as the Enter key in `handleKeyDown`). Do NOT suggest re-adding the mode gate.
- `SendButton` does NOT use the `buttons()` spring opacity animation (`style={buttons()}`). It is always fully visible regardless of mode.
- `WorkspaceChip` is gated on `props.homeMode && store.mode === "normal"` so it hides in shell mode (preventing it from appearing isolated/bright while neighboring controls fade).
- The left-side chip group (`aria-hidden={store.mode !== "normal"}`) covers attach/model/variant/workspace controls only; `SendButton` remains in a separate right-side sibling div.
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-23T08:51:04.230Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 186
File: packages/opencode/test/plugin/workspace-adaptor.test.ts:139-144
Timestamp: 2026-04-23T08:51:04.230Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/util/filesystem.ts`), the `Filesystem` utility does NOT expose a `remove` or `unlink` helper. The established repository pattern for auth.json teardown in tests (e.g. `provider.test.ts`, `amazon-bedrock.test.ts`, `workspace-adaptor.test.ts`) is to combine `Filesystem.write` with `node:fs/promises unlink`. Do not flag this mixed usage as inconsistent — it is the correct and intentional pattern.
Applied to files:
packages/desktop-electron/src/main/index.ts
📚 Learning: 2026-04-24T00:02:53.315Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 203
File: packages/app/e2e/sidebar/sidebar-session-links.spec.ts:34-55
Timestamp: 2026-04-24T00:02:53.315Z
Learning: In Astro-Han/pawwork E2E tests (`packages/app/e2e/**/*.spec.ts`), `project.trackDirectory()` and `project.trackSession()` cannot be called before `project.open()` — the `project` fixture throws until `open()` initializes its internal state. The correct pattern is: call `project.trackSession(sessionID)` from inside the `beforeGoto` callback (where state already exists), call `project.trackDirectory(directory)` and cross-workspace `project.trackSession(id, directory)` immediately after `project.open()` returns, and rely on explicit `finally` cleanup (e.g. `cleanupSession` / `cleanupTestProject`) for any resources created before `open()` that cannot yet be tracked via the fixture.
Applied to files:
packages/desktop-electron/src/main/index.ts
🔇 Additional comments (2)
packages/desktop-electron/src/main/index.ts (2)
94-111: Good progress-bar lifecycle handling.Centralized apply/clear helpers plus
ready-to-showreplay give consistent dock/taskbar progress behavior across existing and newly opened windows.Also applies to: 369-373, 619-629
673-685: Failure dialog behavior is clear and actionable.Reason-specific copy, explicit action buttons, and the dedicated install-failure branch match the updater UX goals and reduce ambiguity for users.
Also applies to: 732-748
Summary
Implements the v5.2 design in #213:
setProgressBar(info.percent / 100)to every openBrowserWindowso macOS dock and Windows 10/11 taskbar icons show motion.ready-to-show.installFailedMessageand only Open Download Page / Later (retry does not help after install failure).No renderer changes. No new IPC. No new UI primitives.
Follow-up for network reliability (CDN / mirror) is tracked in #219.
Test plan
bun test packages/desktop-electron/src/main/updater-dialog-labels.test.tsbun test packages/desktop-electron/src/main/index-updater-source.test.tsbun --cwd packages/desktop-electron test(282 pass)bun --cwd packages/desktop-electron typecheckimport { type MenuLocale }, not in this PR's scope)UPDATER_ENABLED+ real feed; not runnable frombun dev) — gating before releaseSummary by CodeRabbit
New Features
Bug Fixes
Localization
Tests