Skip to content

fix: harden updater stale pending handling#178

Merged
Astro-Han merged 3 commits into
devfrom
codex/fix-i175-updater-stale-pending
Apr 23, 2026
Merged

fix: harden updater stale pending handling#178
Astro-Han merged 3 commits into
devfrom
codex/fix-i175-updater-stale-pending

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Apr 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a semver gate in the desktop updater before download and before quitAndInstall().
  • Clear stale pending updater cache from the electron-updater pending directory and expose cache cleanup failures to the renderer.
  • Let renderer update actions call main-owned install only, without relaunching the app from renderer code.
  • Add a direct desktop semver dependency for updater version comparisons.

Why

Fixes #175. A stable build could keep a pending package for an older version and keep offering it after the running app had already advanced.

Related Issue

Fixes #175

How To Verify

cd packages/desktop-electron
bun test src/main/updater-cache.test.ts scripts/write-app-update-config.test.ts src/main/updater.test.ts src/main/index-updater-source.test.ts
bun run typecheck

cd ../app
bun test --preload ./happydom.ts ./src/pages/update-install-flow-source.test.ts
bun run typecheck

cd ../..
bun turbo typecheck
bun turbo test:ci

Screenshots or Recordings

Not applicable. This changes updater control flow and source contracts, not visible UI.

Checklist

  • I linked the related issue, or stated why there is no issue
  • This PR has type, scope, and priority labels, or I requested maintainer labeling
  • I listed the relevant verification steps, including tests when behavior changed
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for desktop, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, or generated/local file changes when relevant
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

Summary by CodeRabbit

  • Bug Fixes

    • Install no longer forces or implicitly triggers an app restart.
    • Improved handling and cleanup of stale/invalid updates; installs now refuse outdated updates.
    • Auto-install-on-quit enabled only on non-macOS platforms.
  • New Features

    • Added ability to clear pending updates to improve update reliability.
    • Update failure reporting expanded to include cache-related failures.
    • Standardized updater cache path handling.
  • Tests

    • Added tests validating update/install flows, cache behavior, and semver-based version checks.
  • Chores

    • Added semver runtime dependency (with typings).

@Astro-Han Astro-Han added bug Something isn't working P1 High priority platform Electron shell, OS integration, packaging, updater, signing, paths, and permissions labels Apr 23, 2026
@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 1 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 1 minutes and 1 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 74a4f94b-cb43-417a-9d7a-0b91c296fc69

📥 Commits

Reviewing files that changed from the base of the PR and between 1c38ca7 and eab922e.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/platform.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/package.json
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.ts
📝 Walkthrough

Walkthrough

Renderer stop calling restart during install; update failures gain a "cache" reason; a platform-aware updater cache and clearPendingUpdate API were added; updater uses semver to detect/clear stale pending updates, disallows downgrades, and conditions auto-install-on-quit by platform.

Changes

Cohort / File(s) Summary
Renderer update handlers
packages/app/src/components/settings-general.tsx, packages/app/src/pages/layout.tsx, packages/app/src/pages/error.tsx
Install/update actions now require only platform.update (and platform.checkUpdate where applicable); they no longer require or call platform.restart.
Platform types
packages/app/src/context/platform.tsx
Extend UpdateFailureReason with "cache" to represent cache/cleanup failures returned to the renderer.
Renderer contract tests
packages/app/src/pages/update-install-flow-source.test.ts
New source-level tests assert renderer code does not invoke platform.restart!() and that update gating accepts "cache" failure.
Updater cache util & tests
packages/desktop-electron/src/main/updater-cache.ts, packages/desktop-electron/src/main/updater-cache.test.ts
Add UPDATER_CACHE_DIR_NAME, getAppCacheDir, and pendingUpdateCacheDir with OS/env-aware resolution; tests cover macOS/Windows/Linux behaviors and env edge cases.
Updater controller & tests
packages/desktop-electron/src/main/updater.ts, packages/desktop-electron/src/main/updater.test.ts
Use semver (parse/gt) for freshness checks; add clearPendingUpdate() dependency and invoke it to remove stale pending state; treat cache-cleanup failures as {status:"failed", reason:"cache"} and map invalid-version errors to "metadata"; skip installs for stale ready versions.
Electron main updater wiring & source tests
packages/desktop-electron/src/main/index.ts, packages/desktop-electron/src/main/index-updater-source.test.ts
Wire clearPendingUpdate into controller; set autoUpdater.allowDowngrade = false; compute/log autoInstallOnAppQuit as process.platform !== "darwin"; add source-level assertions for ordering and config.
Write config & package deps
packages/desktop-electron/scripts/write-app-update-config.ts, packages/desktop-electron/scripts/write-app-update-config.test.ts, packages/desktop-electron/package.json
Serialize updaterCacheDirName using shared UPDATER_CACHE_DIR_NAME; tests updated to derive expected value; add semver runtime dep and @types/semver dev dep.

Sequence Diagram(s)

sequenceDiagram
    participant Renderer as Renderer
    participant Updater as Updater Controller
    participant Semver as Semver
    participant Cache as Pending Cache
    participant FS as File System

    Renderer->>Updater: request install/update
    Updater->>Updater: check for ready update
    alt ready update found
        Updater->>Semver: compare readyVersion vs currentVersion
        Semver-->>Updater: result (newer / not newer)
        alt not newer (stale)
            Updater->>Cache: pendingUpdateCacheDir()
            Updater->>FS: rm(pendingUpdateCacheDir(), {recursive:true, force:true})
            alt rm fails
                FS-->>Updater: error
                Updater-->>Renderer: return {status:"failed", reason:"cache"}
            else rm succeeds
                FS-->>Updater: success
                Updater->>Updater: re-check updates
            end
        else newer
            Updater-->>Renderer: proceed with install (renderer does not call restart)
        end
    else no ready update
        Updater-->>Renderer: no-op / continue polling
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

app

Poem

🐇 I nibbled stale zips beside the cache,
Semver sorted versions in a flash.
Cleared the pending, kept downgrades away,
No restart hops from the renderer today.
Hooray — the updater's tidy, come what may!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: harden updater stale pending handling' clearly and concisely describes the main change in the PR: improving the updater's handling of stale pending updates. It follows Conventional Commits and accurately reflects the primary objective from issue #175.
Description check ✅ Passed The PR description follows the template with all major sections completed: Summary, Why, Related Issue, How To Verify, Screenshots/Recordings status, and Checklist. All checklist items are marked complete and verification steps are provided.
Linked Issues check ✅ Passed The code changes comprehensively address all primary objectives from issue #175: (1) semver comparison gates prevent installing stale pending updates [updater.ts], (2) stale pending cache is cleared when readyVersion <= currentVersion [updater-cache.ts, updater.ts], (3) renderer no longer calls platform.restart directly [settings-general.tsx, error.tsx, layout.tsx], and (4) semver dependency added [package.json].
Out of Scope Changes check ✅ Passed All changes are tightly scoped to the objectives. File modifications address updater stale pending handling (updater.ts, updater-cache.ts), cache configuration (write-app-update-config.ts), renderer update flow (component files), and necessary tests. No unrelated changes are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-i175-updater-stale-pending

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the application update process by removing the explicit restart requirement from the renderer and centralizing updater cache management. Key changes include the introduction of a cross-platform cache path utility, semver-based version validation to prevent downgrades, and logic to clear stale updates in the main process. Review feedback recommends using regular expressions in source-code inspection tests to improve robustness against formatting changes and suggests handling case-insensitive environment variables on Windows for more reliable path resolution.

Comment thread packages/app/src/pages/update-install-flow-source.test.ts Outdated
Comment thread packages/desktop-electron/src/main/index-updater-source.test.ts Outdated
Comment thread packages/desktop-electron/src/main/updater-cache.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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/scripts/write-app-update-config.test.ts`:
- Around line 36-45: Replace the hardcoded macOS assertion string with the
shared constant so tests don't drift: in the test suite that calls
serializeAppUpdateConfig (the test "serializes updater cache dir from the shared
constant" and the macOS path assertion), replace the literal
"updaterCacheDirName: pawwork-updater" with a template using
UPDATER_CACHE_DIR_NAME (the same constant used earlier) so the macOS write-path
assertion references UPDATER_CACHE_DIR_NAME instead of a hardcoded value.

In `@packages/desktop-electron/src/main/updater-cache.test.ts`:
- Around line 16-30: The tests currently only cover branches where env vars
exist; add explicit tests for pendingUpdateCacheDir to assert the fallback
behavior when those env vars are missing: for platform "win32" call
pendingUpdateCacheDir with env: {} and homedir "C:\\Users\\demo" and assert it
returns "C:\\Users\\demo\\AppData\\Local\\pawwork-updater\\pending" (or the
intended fallback path used in the implementation), and for platform "linux"
call pendingUpdateCacheDir with env: {} and homedir "/home/demo" and assert it
returns "/home/demo/.cache/pawwork-updater/pending" (matching the function's
fallback). Reference the pendingUpdateCacheDir function in tests so both
env-present and env-absent branches are covered.

In `@packages/desktop-electron/src/main/updater.ts`:
- Around line 91-95: The branch that returns {status: "none"} after calling
newerThanCurrent(info.version, currentVersion) must clear any stale pending
cache and trigger one re-check; call clearPendingUpdate() before returning in
the case where comparison is false (version <= currentVersion), then set a
one-shot guard (e.g., a local boolean like haveClearedPendingOnce) so you re-run
the update check exactly once after cleanup to fetch fresh metadata and avoid
repeated reuse of a stale pending package; ensure you reference newerThanCurrent
and clearPendingUpdate() when locating where to insert the cleanup and the
guarded re-check.
🪄 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: 714940fb-1d77-46b0-abd3-cd6569ac41f5

📥 Commits

Reviewing files that changed from the base of the PR and between 4d934c8 and 6c90c1a.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/platform.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/package.json
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.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). (11)
  • GitHub Check: smoke-macos-arm64
  • GitHub Check: unit-windows-opencode-session
  • GitHub Check: unit-windows-desktop
  • GitHub Check: unit-windows-opencode-config-project
  • GitHub Check: unit-windows-app
  • GitHub Check: unit-windows-opencode-server-tools
  • GitHub Check: unit-desktop
  • GitHub Check: unit-opencode
  • GitHub Check: unit-app
  • GitHub Check: e2e-artifacts
  • GitHub Check: analyze-js-ts
🧰 Additional context used
📓 Path-based instructions (2)
packages/app/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/app/AGENTS.md)

Always prefer createStore over multiple createSignal calls in SolidJS

Files:

  • packages/app/src/components/settings-general.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/app/src/context/platform.tsx
packages/desktop-electron/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/desktop-electron/AGENTS.md)

Renderer process should only call window.api from src/preload

Files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.ts
🧠 Learnings (20)
📚 Learning: 2026-04-22T08:49:44.563Z
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:44.563Z
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/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/packages/app/src/testing/**/*.ts : Test-only hooks must be inert unless explicitly enabled and should not add normal-runtime listeners, reactive subscriptions, or per-update allocations

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-22T09:32:52.806Z
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:52.806Z
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/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Test one feature per test file

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Import test utilities from `../fixtures` instead of `playwright/test`

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `it.effect(...)` when the test should run with `TestClock` and `TestConsole`. Use `it.live(...)` when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:08.745Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/desktop-electron/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:08.745Z
Learning: Applies to packages/desktop-electron/src/**/*.{ts,tsx,js,jsx} : Renderer process should only call `window.api` from `src/preload`

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:08.745Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/desktop-electron/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:08.745Z
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-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use SCREAMING_SNAKE_CASE for constants in tests

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `tmpdir` function from `fixture/fixture.ts` to create temporary directories for tests with automatic cleanup. Use `await using` syntax to ensure automatic cleanup when the variable goes out of scope.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use camelCase for variable names in tests

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `init` option in `tmpdir` to define custom setup functions that can return extra data accessible via `tmp.extra`, and use the `dispose` option for custom cleanup logic.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When using the `tmpdir` function with git repository support, pass the `git: true` option to initialize a git repo with a root commit.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : When validating routing, assert against canonical or resolved workspace slugs using shared helpers from `../actions` to account for Windows canonicalization

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When a test needs instance-local state, prefer `provideTmpdirInstance(...)` or `provideInstance(...)` over manual `Instance.provide(...)` inside Promise-style tests.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
🔇 Additional comments (7)
packages/desktop-electron/package.json (1)

37-38: Semver dependency wiring is consistent and appropriate.

Adding semver at runtime with matching type support is a clean fit for updater version gating logic.

Also applies to: 49-50

packages/desktop-electron/src/main/updater-cache.ts (1)

1-30: Cache path utility is well-structured and test-friendly.

The platform-branching and environment fallbacks are clear, and the injectable inputs make this easy to validate in tests.

packages/app/src/pages/layout.tsx (1)

370-370: Update polling gate aligns with main-owned install flow.

Requiring checkUpdate + update and removing renderer restart coupling is the right contract boundary.

packages/app/src/components/settings-general.tsx (1)

151-170: Install action contract is correctly simplified.

Using platform.update as the sole requirement here keeps renderer behavior consistent with main-owned installation/relaunch.

packages/desktop-electron/scripts/write-app-update-config.ts (1)

4-5: Good deduplication of updater cache directory naming.

Importing the shared constant avoids future config/runtime divergence.

packages/app/src/context/platform.tsx (1)

10-10: Platform update failure typing is correctly extended.

Adding "cache" keeps renderer type safety aligned with updater failure reporting.

packages/app/src/pages/error.tsx (1)

267-267: Error-page install flow now correctly depends only on platform.update.

This keeps renderer-side behavior consistent with main-controlled update installation.

Comment thread packages/desktop-electron/scripts/write-app-update-config.test.ts
Comment thread packages/desktop-electron/src/main/updater-cache.test.ts
Comment thread packages/desktop-electron/src/main/updater.ts Outdated
@Astro-Han Astro-Han force-pushed the codex/fix-i175-updater-stale-pending branch from 6c90c1a to 037e0c9 Compare April 23, 2026 03:19
@Astro-Han

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Astro-Han

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/app/src/pages/update-install-flow-source.test.ts`:
- Around line 10-14: The test "renderer install actions do not relaunch after
platform update" currently ensures layout and settings don't contain "await
platform.restart!()" but only blocks the ".then(() => platform.restart!())" form
on errorPage; update the errorPage assertion to also reject async/await by
adding a similar regex check (e.g.,
expect(errorPage).not.toMatch(/await\s+platform\.restart!\(\)/)) so both await
and .then patterns for platform.restart!() are covered alongside existing checks
for layout and settings.

In `@packages/desktop-electron/src/main/updater-cache.ts`:
- Around line 16-25: getAppCacheDir may return environment cache paths verbatim
even when relative; change it to only use env.LOCALAPPDATA / env.localappdata
(on win32) and env.XDG_CACHE_HOME (on other platforms) if the value is an
absolute path (use path.isAbsolute) otherwise fall back to the platform default
path computed by pathForPlatform(current platform) joined with homedir; update
the win32 branch (function getAppCacheDir) to test absolute-ness before
returning env value and do the same for the XDG_CACHE_HOME branch so
pendingUpdateCacheDir() never becomes cwd-relative.
🪄 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: e2af0adc-fd55-4b17-8b15-896ff7709de9

📥 Commits

Reviewing files that changed from the base of the PR and between 6c90c1a and 037e0c9.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/platform.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/package.json
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
packages/app/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/app/AGENTS.md)

Always prefer createStore over multiple createSignal calls in SolidJS

Files:

  • packages/app/src/components/settings-general.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/context/platform.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/update-install-flow-source.test.ts
packages/desktop-electron/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/desktop-electron/AGENTS.md)

Renderer process should only call window.api from src/preload

Files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/updater.ts
  • packages/desktop-electron/src/main/updater.test.ts
🧠 Learnings (30)
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `tmpdir` function from `fixture/fixture.ts` to create temporary directories for tests with automatic cleanup. Use `await using` syntax to ensure automatic cleanup when the variable goes out of scope.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `init` option in `tmpdir` to define custom setup functions that can return extra data accessible via `tmp.extra`, and use the `dispose` option for custom cleanup logic.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-22T08:49:44.563Z
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:44.563Z
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/updater-cache.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/updater.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When using the `tmpdir` function with git repository support, pass the `git: true` option to initialize a git repo with a root commit.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : When validating routing, assert against canonical or resolved workspace slugs using shared helpers from `../actions` to account for Windows canonicalization

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When a test needs instance-local state, prefer `provideTmpdirInstance(...)` or `provideInstance(...)` over manual `Instance.provide(...)` inside Promise-style tests.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T17:03:37.710Z
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:37.710Z
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/updater-cache.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater.ts
📚 Learning: 2026-04-21T13:45:41.773Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 99
File: packages/desktop-electron/src/renderer/i18n/index.ts:30-35
Timestamp: 2026-04-21T13:45:41.773Z
Learning: In Astro-Han/pawwork, the locale normalization helpers in `packages/app/src/context/language.tsx` (`normalizeLocale`) and `packages/desktop-electron/src/renderer/i18n/index.ts` (`parseLocale`) are intentionally kept separate and have different fallback shapes: `normalizeLocale` always returns a concrete `Locale` (falling back to `"en"`), while `parseLocale` returns `Locale | null` so the desktop shell can decide whether to fall back to browser detection. Do not suggest extracting a shared normalization helper across these two runtimes.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use SCREAMING_SNAKE_CASE for constants in tests

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-22T09:32:52.806Z
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:52.806Z
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/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-22T09:32:52.535Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:52.535Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-20T14:21:51.047Z
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:51.047Z
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/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater.ts
📚 Learning: 2026-04-21T16:57:22.813Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/agent.ts:108-119
Timestamp: 2026-04-21T16:57:22.813Z
Learning: In `packages/opencode/src/config/agent.ts` (Astro-Han/pawwork), `ConfigPermission.Info` only accepts permission objects or the three action strings `"ask"`, `"allow"`, `"deny"`, and transforms those action strings into `{ "*": action }` before `normalize()` runs. By the time `normalize()` is reached, `configuredPermission` is always either `undefined` or a `Record<string, Rule>` — never a raw arbitrary string. The `Object.assign(permission, configuredPermission)` pattern is therefore safe. Do not flag it as corrupting string permission references.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-22T05:24:42.404Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 98
File: packages/desktop-electron/src/main/menu-labels.ts:1-2
Timestamp: 2026-04-22T05:24:42.404Z
Learning: In Astro-Han/pawwork, the app i18n layer (`packages/app/src/i18n/`) only contains `en.ts` and `zh.ts`, and `normalizeLocale` (in `packages/app/src/context/language.tsx`) only returns `"en"` or `"zh"`. The desktop `MenuLocale = "en" | "zh"` union in `packages/desktop-electron/src/main/menu-labels.ts` is intentionally limited to these two locales and is not a broader restriction — do not flag it as overly restrictive or suggest adding other locales.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/packages/app/src/testing/**/*.ts : Test-only hooks must be inert unless explicitly enabled and should not add normal-runtime listeners, reactive subscriptions, or per-update allocations

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Import test utilities from `../fixtures` instead of `playwright/test`

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Test one feature per test file

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
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/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use lowercase, descriptive test names (e.g., 'sidebar can be toggled')

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
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/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use `expect.poll(...)` for probing mock or backend state rather than transient DOM visibility

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use camelCase for variable names in tests

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Never use wall-clock waits like `page.waitForTimeout(...)` to make a test pass

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `it.effect(...)` when the test should run with `TestClock` and `TestConsole`. Use `it.live(...)` when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:08.745Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/desktop-electron/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:08.745Z
Learning: Applies to packages/desktop-electron/src/**/*.{ts,tsx,js,jsx} : Renderer process should only call `window.api` from `src/preload`

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
🔇 Additional comments (6)
packages/app/src/context/platform.tsx (1)

10-14: Type contract update is coherent for cache-related updater failures.

Adding "cache" to UpdateFailureReason cleanly extends the existing UpdateInfo failed branch without breaking the union shape.

packages/app/src/components/settings-general.tsx (1)

151-158: Install action ownership is correctly narrowed to platform.update.

This aligns the renderer flow with main-process update/install ownership and removes renderer-side relaunch coupling.

packages/app/src/pages/layout.tsx (1)

368-371: Update polling gate now matches renderer install contract.

Requiring only platform.checkUpdate + platform.update is correct after removing renderer-managed restart calls.

packages/app/src/pages/error.tsx (1)

266-270: installUpdate is correctly decoupled from renderer restart capability.

The guard at Line 267 now reflects the intended contract: install through platform.update only.

packages/desktop-electron/scripts/write-app-update-config.ts (1)

4-5: Good constant reuse to prevent config/runtime drift.

Using UPDATER_CACHE_DIR_NAME from the shared source is the right move for consistency.

packages/desktop-electron/scripts/write-app-update-config.test.ts (1)

30-45: Test updates correctly enforce shared updater-cache constant usage.

These assertions close the remaining hardcoded-path drift risk and keep serialization checks aligned with runtime config.

Also applies to: 81-81

Comment thread packages/app/src/pages/update-install-flow-source.test.ts
Comment thread packages/desktop-electron/src/main/updater-cache.ts Outdated
@Astro-Han Astro-Han force-pushed the codex/fix-i175-updater-stale-pending branch from 037e0c9 to 66ecfd6 Compare April 23, 2026 04:50
@Astro-Han

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/app/src/pages/update-install-flow-source.test.ts`:
- Around line 10-15: The test only rejects the chained restart pattern for
errorPage but not for layout or settings sources; add assertions to also
disallow the `.then(() => platform.restart!())` pattern in layout and settings
by extending the test in update-install-flow-source.test.ts to include
expect(layout).not.toMatch(/\.then\(\(\)\s*=>\s*platform\.restart!\(\)\)/) and
expect(settings).not.toMatch(/\.then\(\(\)\s*=>\s*platform\.restart!\(\)\)/), so
any `platform.update().then(() => platform.restart!())` or similar chained forms
in layout.tsx or settings-general.tsx will fail the test.

In `@packages/desktop-electron/src/main/index-updater-source.test.ts`:
- Around line 20-24: The test is too broad by banning ".catch(() =>" anywhere in
the file; narrow it to the specific cleanup call. Replace the global assertion
on source with one that targets the exact call site (e.g., assert source does
not contain the substring "rm(pendingUpdateCacheDir(), { recursive: true, force:
true }).catch(() =>" or use a regex/contains check that looks for ".catch("
immediately following the call to rm(pendingUpdateCacheDir(), { recursive: true,
force: true }) ). Keep the other assertions referencing pendingUpdateCacheDir
and rm as-is.
🪄 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: 3bfeacef-c9a2-4b5e-8cf6-c76a05af9fbf

📥 Commits

Reviewing files that changed from the base of the PR and between 037e0c9 and 66ecfd6.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/platform.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/package.json
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.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). (1)
  • GitHub Check: e2e-artifacts
🧰 Additional context used
📓 Path-based instructions (2)
packages/app/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/app/AGENTS.md)

Always prefer createStore over multiple createSignal calls in SolidJS

Files:

  • packages/app/src/components/settings-general.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/context/platform.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/update-install-flow-source.test.ts
packages/desktop-electron/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/desktop-electron/AGENTS.md)

Renderer process should only call window.api from src/preload

Files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.ts
🧠 Learnings (32)
📚 Learning: 2026-04-22T08:49:44.563Z
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:44.563Z
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/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use SCREAMING_SNAKE_CASE for constants in tests

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Import test utilities from `../fixtures` instead of `playwright/test`

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use camelCase for variable names in tests

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `tmpdir` function from `fixture/fixture.ts` to create temporary directories for tests with automatic cleanup. Use `await using` syntax to ensure automatic cleanup when the variable goes out of scope.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When a test needs instance-local state, prefer `provideTmpdirInstance(...)` or `provideInstance(...)` over manual `Instance.provide(...)` inside Promise-style tests.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-22T09:32:52.806Z
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:52.806Z
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/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : When validating routing, assert against canonical or resolved workspace slugs using shared helpers from `../actions` to account for Windows canonicalization

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-22T09:32:52.535Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:52.535Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-20T14:21:51.047Z
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:51.047Z
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/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/updater.ts
📚 Learning: 2026-04-20T17:03:37.710Z
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:37.710Z
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/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/updater.ts
📚 Learning: 2026-04-21T16:57:22.813Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/agent.ts:108-119
Timestamp: 2026-04-21T16:57:22.813Z
Learning: In `packages/opencode/src/config/agent.ts` (Astro-Han/pawwork), `ConfigPermission.Info` only accepts permission objects or the three action strings `"ask"`, `"allow"`, `"deny"`, and transforms those action strings into `{ "*": action }` before `normalize()` runs. By the time `normalize()` is reached, `configuredPermission` is always either `undefined` or a `Record<string, Rule>` — never a raw arbitrary string. The `Object.assign(permission, configuredPermission)` pattern is therefore safe. Do not flag it as corrupting string permission references.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-22T05:24:42.404Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 98
File: packages/desktop-electron/src/main/menu-labels.ts:1-2
Timestamp: 2026-04-22T05:24:42.404Z
Learning: In Astro-Han/pawwork, the app i18n layer (`packages/app/src/i18n/`) only contains `en.ts` and `zh.ts`, and `normalizeLocale` (in `packages/app/src/context/language.tsx`) only returns `"en"` or `"zh"`. The desktop `MenuLocale = "en" | "zh"` union in `packages/desktop-electron/src/main/menu-labels.ts` is intentionally limited to these two locales and is not a broader restriction — do not flag it as overly restrictive or suggest adding other locales.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `init` option in `tmpdir` to define custom setup functions that can return extra data accessible via `tmp.extra`, and use the `dispose` option for custom cleanup logic.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When using the `tmpdir` function with git repository support, pass the `git: true` option to initialize a git repo with a root commit.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-21T13:45:41.773Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 99
File: packages/desktop-electron/src/renderer/i18n/index.ts:30-35
Timestamp: 2026-04-21T13:45:41.773Z
Learning: In Astro-Han/pawwork, the locale normalization helpers in `packages/app/src/context/language.tsx` (`normalizeLocale`) and `packages/desktop-electron/src/renderer/i18n/index.ts` (`parseLocale`) are intentionally kept separate and have different fallback shapes: `normalizeLocale` always returns a concrete `Locale` (falling back to `"en"`), while `parseLocale` returns `Locale | null` so the desktop shell can decide whether to fall back to browser detection. Do not suggest extracting a shared normalization helper across these two runtimes.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/packages/app/src/testing/**/*.ts : Test-only hooks must be inert unless explicitly enabled and should not add normal-runtime listeners, reactive subscriptions, or per-update allocations

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Test one feature per test file

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
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/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use lowercase, descriptive test names (e.g., 'sidebar can be toggled')

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
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/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use `expect.poll(...)` for probing mock or backend state rather than transient DOM visibility

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Never use wall-clock waits like `page.waitForTimeout(...)` to make a test pass

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-21T12:14:29.062Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 93
File: packages/opencode/test/server/cors-middleware.test.ts:15-23
Timestamp: 2026-04-21T12:14:29.062Z
Learning: In `packages/opencode/test/server/cors-middleware.test.ts` (and similar Hono-based test files), `app.request(...)` on the Hono `Hono` app type returns `Promise<Response> | Response` — a union that includes a non-PromiseLike branch. `Effect.promise(...)` requires a `PromiseLike`, so wrapping with `Promise.resolve(app.request(...))` is necessary to normalize the union type. Removing the wrapper causes a TS2322 typecheck error in `packages/opencode`.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `it.effect(...)` when the test should run with `TestClock` and `TestConsole`. Use `it.live(...)` when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:08.745Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/desktop-electron/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:08.745Z
Learning: Applies to packages/desktop-electron/src/**/*.{ts,tsx,js,jsx} : Renderer process should only call `window.api` from `src/preload`

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:21.274Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.274Z
Learning: Applies to packages/opencode/**/*.ts : Use `InstanceState` from `src/effect/instance-state.ts` for per-directory or per-project state that needs per-instance cleanup; do work directly in the `InstanceState.make` closure where `ScopedCache` handles run-once semantics

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.ts
🔇 Additional comments (9)
packages/app/src/context/platform.tsx (1)

10-10: UpdateFailureReason correctly includes "cache".

This keeps renderer-facing update failure typing aligned with the new updater cache-cleanup failure path.

packages/desktop-electron/package.json (1)

37-39: Dependency additions are consistent with updater changes.

semver runtime + typings support the new version-gating logic cleanly.

Also applies to: 49-50

packages/desktop-electron/scripts/write-app-update-config.ts (1)

4-5: Good shared-constant migration for updater cache dir.

Using UPDATER_CACHE_DIR_NAME here prevents config drift with main updater logic.

Also applies to: 19-19

packages/app/src/pages/layout.tsx (1)

370-370: Renderer install action is correctly narrowed to platform.update.

This keeps install/restart ownership in the desktop main flow and avoids renderer-driven relaunch behavior.

Also applies to: 386-389

packages/app/src/components/settings-general.tsx (1)

151-170: Update toast action flow is correctly simplified.

Conditioning on platform.update and calling only platform.update() is consistent with the hardened install flow.

packages/app/src/pages/error.tsx (1)

267-270: installUpdate now correctly delegates to platform update only.

This matches the renderer/main separation applied across the rest of the update flow.

packages/desktop-electron/scripts/write-app-update-config.test.ts (1)

6-7: Test updates look good and close the hardcoded-literal gap.

The suite now consistently validates updaterCacheDirName via UPDATER_CACHE_DIR_NAME.

Also applies to: 30-31, 36-45, 81-81

packages/desktop-electron/src/main/index.ts (2)

65-66: Pending update cache cleanup integration is solid.

Wiring clearPendingUpdate to await rm(pendingUpdateCacheDir(), { recursive: true, force: true }) is a clean and strict implementation.

Also applies to: 111-111, 550-552


559-567: Auto-updater hardening settings look correct.

Disabling downgrades and using platform-aware autoInstallOnAppQuit addresses stale downgrade risk and platform behavior more safely.

Comment thread packages/app/src/pages/update-install-flow-source.test.ts
Comment thread packages/desktop-electron/src/main/index-updater-source.test.ts
@Astro-Han Astro-Han force-pushed the codex/fix-i175-updater-stale-pending branch from 66ecfd6 to 1c38ca7 Compare April 23, 2026 05:21

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/scripts/write-app-update-config.test.ts`:
- Around line 79-81: Read the file once into a variable and reuse it in both
assertions instead of calling readFileSync twice: create a local const (e.g.,
configContents) by calling readFileSync(configPath, "utf8") and then call
expect(configContents).toContain("repo: pawwork") and
expect(configContents).toContain(`updaterCacheDirName:
${UPDATER_CACHE_DIR_NAME}`); update the test that defines configPath to use this
cached value.
🪄 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: 5e2e78a3-8814-4b8e-b11c-37e5fa7fc455

📥 Commits

Reviewing files that changed from the base of the PR and between 66ecfd6 and 1c38ca7.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/platform.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/package.json
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.ts
  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.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). (9)
  • GitHub Check: smoke-macos-arm64
  • GitHub Check: unit-windows-app
  • GitHub Check: unit-windows-opencode-session
  • GitHub Check: unit-windows-opencode-server-tools
  • GitHub Check: unit-windows-desktop
  • GitHub Check: unit-windows-opencode-config-project
  • GitHub Check: unit-desktop
  • GitHub Check: analyze-js-ts
  • GitHub Check: e2e-artifacts
🧰 Additional context used
📓 Path-based instructions (2)
packages/app/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/app/AGENTS.md)

Always prefer createStore over multiple createSignal calls in SolidJS

Files:

  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/platform.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/update-install-flow-source.test.ts
packages/desktop-electron/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/desktop-electron/AGENTS.md)

Renderer process should only call window.api from src/preload

Files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/index.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.ts
  • packages/desktop-electron/src/main/updater-cache.ts
🧠 Learnings (32)
📚 Learning: 2026-04-22T08:49:44.563Z
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:44.563Z
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
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
  • packages/desktop-electron/src/main/updater.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/packages/app/src/testing/**/*.ts : Test-only hooks must be inert unless explicitly enabled and should not add normal-runtime listeners, reactive subscriptions, or per-update allocations

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-22T09:32:52.806Z
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:52.806Z
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
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Import test utilities from `../fixtures` instead of `playwright/test`

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `tmpdir` function from `fixture/fixture.ts` to create temporary directories for tests with automatic cleanup. Use `await using` syntax to ensure automatic cleanup when the variable goes out of scope.

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When a test needs instance-local state, prefer `provideTmpdirInstance(...)` or `provideInstance(...)` over manual `Instance.provide(...)` inside Promise-style tests.

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
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
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `it.effect(...)` when the test should run with `TestClock` and `TestConsole`. Use `it.live(...)` when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.

Applied to files:

  • packages/desktop-electron/src/main/index-updater-source.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use SCREAMING_SNAKE_CASE for constants in tests

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use camelCase for variable names in tests

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : When validating routing, assert against canonical or resolved workspace slugs using shared helpers from `../actions` to account for Windows canonicalization

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-22T09:32:52.535Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:52.535Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-20T14:21:51.047Z
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:51.047Z
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/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-20T17:03:37.710Z
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:37.710Z
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/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-21T16:57:22.813Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/agent.ts:108-119
Timestamp: 2026-04-21T16:57:22.813Z
Learning: In `packages/opencode/src/config/agent.ts` (Astro-Han/pawwork), `ConfigPermission.Info` only accepts permission objects or the three action strings `"ask"`, `"allow"`, `"deny"`, and transforms those action strings into `{ "*": action }` before `normalize()` runs. By the time `normalize()` is reached, `configuredPermission` is always either `undefined` or a `Record<string, Rule>` — never a raw arbitrary string. The `Object.assign(permission, configuredPermission)` pattern is therefore safe. Do not flag it as corrupting string permission references.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-22T05:24:42.404Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 98
File: packages/desktop-electron/src/main/menu-labels.ts:1-2
Timestamp: 2026-04-22T05:24:42.404Z
Learning: In Astro-Han/pawwork, the app i18n layer (`packages/app/src/i18n/`) only contains `en.ts` and `zh.ts`, and `normalizeLocale` (in `packages/app/src/context/language.tsx`) only returns `"en"` or `"zh"`. The desktop `MenuLocale = "en" | "zh"` union in `packages/desktop-electron/src/main/menu-labels.ts` is intentionally limited to these two locales and is not a broader restriction — do not flag it as overly restrictive or suggest adding other locales.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `init` option in `tmpdir` to define custom setup functions that can return extra data accessible via `tmp.extra`, and use the `dispose` option for custom cleanup logic.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When using the `tmpdir` function with git repository support, pass the `git: true` option to initialize a git repo with a root commit.

Applied to files:

  • packages/desktop-electron/scripts/write-app-update-config.test.ts
  • packages/desktop-electron/src/main/updater-cache.test.ts
📚 Learning: 2026-04-20T14:36:31.017Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.017Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Test one feature per test file

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
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/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use lowercase, descriptive test names (e.g., 'sidebar can be toggled')

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use `expect.poll(...)` for probing mock or backend state rather than transient DOM visibility

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:04.099Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.099Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Never use wall-clock waits like `page.waitForTimeout(...)` to make a test pass

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-21T12:14:29.062Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 93
File: packages/opencode/test/server/cors-middleware.test.ts:15-23
Timestamp: 2026-04-21T12:14:29.062Z
Learning: In `packages/opencode/test/server/cors-middleware.test.ts` (and similar Hono-based test files), `app.request(...)` on the Hono `Hono` app type returns `Promise<Response> | Response` — a union that includes a non-PromiseLike branch. `Effect.promise(...)` requires a `PromiseLike`, so wrapping with `Promise.resolve(app.request(...))` is necessary to normalize the union type. Removing the wrapper causes a TS2322 typecheck error in `packages/opencode`.

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-20T14:36:08.745Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/desktop-electron/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:08.745Z
Learning: Applies to packages/desktop-electron/src/**/*.{ts,tsx,js,jsx} : Renderer process should only call `window.api` from `src/preload`

Applied to files:

  • packages/app/src/pages/update-install-flow-source.test.ts
📚 Learning: 2026-04-21T13:45:41.773Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 99
File: packages/desktop-electron/src/renderer/i18n/index.ts:30-35
Timestamp: 2026-04-21T13:45:41.773Z
Learning: In Astro-Han/pawwork, the locale normalization helpers in `packages/app/src/context/language.tsx` (`normalizeLocale`) and `packages/desktop-electron/src/renderer/i18n/index.ts` (`parseLocale`) are intentionally kept separate and have different fallback shapes: `normalizeLocale` always returns a concrete `Locale` (falling back to `"en"`), while `parseLocale` returns `Locale | null` so the desktop shell can decide whether to fall back to browser detection. Do not suggest extracting a shared normalization helper across these two runtimes.

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.test.ts
  • packages/desktop-electron/src/main/updater-cache.ts
📚 Learning: 2026-04-20T14:36:21.274Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.274Z
Learning: Applies to packages/opencode/**/*.ts : Use `InstanceState` from `src/effect/instance-state.ts` for per-directory or per-project state that needs per-instance cleanup; do work directly in the `InstanceState.make` closure where `ScopedCache` handles run-once semantics

Applied to files:

  • packages/desktop-electron/src/main/updater-cache.ts
🔇 Additional comments (18)
packages/desktop-electron/src/main/updater-cache.ts (1)

16-44: Good hardening of updater cache path resolution.

The absolute-path gate plus platform fallback logic is solid and directly prevents cwd-relative cache resolution issues.

packages/desktop-electron/src/main/updater-cache.test.ts (1)

16-78: Coverage for cache-root edge cases is strong.

These tests correctly lock in fallback and relative-path rejection behavior across platforms.

packages/desktop-electron/scripts/write-app-update-config.ts (1)

4-20: Nice constant unification for updater cache config.

Using UPDATER_CACHE_DIR_NAME here eliminates cross-file string drift risk.

packages/desktop-electron/scripts/write-app-update-config.test.ts (1)

6-45: Shared-constant migration in tests looks correct.

The updated assertions now stay in sync with runtime config generation.

packages/app/src/pages/layout.tsx (1)

370-389: Renderer install flow ownership change looks correct.

Gating on checkUpdate + update and removing direct restart from this action matches the intended main-process install control.

packages/app/src/components/settings-general.tsx (1)

151-170: Update action wiring is consistent with the new install model.

Switching this action set to rely on platform.update only is a clean alignment with main-owned restart/install behavior.

packages/desktop-electron/package.json (1)

37-50: Dependency additions are justified and scoped correctly.

semver and its types map directly to updater version-gating usage.

packages/app/src/context/platform.tsx (1)

10-14: Type union update is correct and necessary.

Adding "cache" keeps renderer-facing update failure typing aligned with desktop updater failure modes.

packages/app/src/pages/error.tsx (1)

267-270: Renderer install flow is correctly decoupled from restart calls.

This guard now depends only on platform.update, which keeps renderer-side install behavior aligned with main-owned relaunch/install handling.

packages/desktop-electron/src/main/index.ts (2)

111-118: Pending-cache cleanup wiring is solid.

clearPendingUpdate is correctly injected into the updater controller, and cleanup is implemented as a strict awaited recursive delete without swallowing errors.

Also applies to: 550-552


559-567: Updater policy hardening looks correct.

Disabling downgrades and making autoInstallOnAppQuit platform-aware (!== "darwin") is consistent with the stale-pending/downgrade mitigation goals.

packages/desktop-electron/src/main/index-updater-source.test.ts (1)

7-13: Source-level updater contracts are well covered.

These checks correctly lock in downgrade prevention, macOS-specific auto-install behavior, and strict pending-cache cleanup semantics.

Also applies to: 15-18, 20-25

packages/app/src/pages/update-install-flow-source.test.ts (1)

10-17: Renderer update-install contract tests look complete.

The assertions capture the key guarantees: no renderer restart chaining, update gating on update capability, and renderer awareness of "cache" failure reasons.

Also applies to: 19-24, 26-28

packages/desktop-electron/src/main/updater.ts (3)

32-37: Semver gating for both ready and install paths is correctly enforced.

Using the same comparison helper across check and install paths closes stale/downgrade install gaps.

Also applies to: 48-55, 94-99, 133-138


56-64: Stale pending cache handling is robust and fail-closed.

Clearing stale cache, returning "cache" on cleanup failures, and performing a one-shot recheck provide the right recovery behavior without infinite retry loops.

Also applies to: 65-108


10-11: Failure classification expansion is coherent.

Adding "cache" and mapping invalid-version paths to metadata failures keeps update error reporting explicit and actionable.

Also applies to: 22-23, 75-77, 102-105

packages/desktop-electron/src/main/updater.test.ts (2)

5-24: Test harness updates are effective for version-sensitive flows.

The mutable currentVersion and clearPendingUpdate counters make the new stale-cache and semver paths testable with good precision.

Also applies to: 36-38


156-169: Regression coverage for stale pending/downgrade scenarios is strong.

These additions give good confidence that stale metadata cleanup, cache-failure behavior, semver ordering, and invalid-version mapping remain protected.

Also applies to: 171-209, 211-257, 259-319

Comment thread packages/desktop-electron/scripts/write-app-update-config.test.ts Outdated
@Astro-Han Astro-Han force-pushed the codex/fix-i175-updater-stale-pending branch from 1c38ca7 to 8de55ee Compare April 23, 2026 05:27
@Astro-Han Astro-Han force-pushed the codex/fix-i175-updater-stale-pending branch from 8de55ee to eab922e Compare April 23, 2026 05:39
@Astro-Han Astro-Han merged commit a96227a into dev Apr 23, 2026
23 checks passed
@Astro-Han Astro-Han deleted the codex/fix-i175-updater-stale-pending branch April 23, 2026 06:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working P1 High priority platform Electron shell, OS integration, packaging, updater, signing, paths, and permissions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Updater can offer stale pending downgrade

1 participant