You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(desktop): Stable switch no longer tells policy-blocked nightly apps to reinstall by hand - #32609
A nightly desktop app under a managed update policy that rules out nightly (allowNightly: false) should offer a one-step switch to Stable. Instead the Stable switch dialog says "This app's updates are off here, so reinstall it yourself to switch.", which is false for this app: the channelBlockedByPolicy: true flag is dropped over IPC because DesktopUpdateStateSchema has no such key.
Change
Add channelBlockedByPolicy: Schema.optionalKey(Schema.Literal(true)) to DesktopUpdateStateSchema, matching the DesktopUpdateState interface field added in #27197. The existing test "holds a nightly install until its user switches to stable" in DesktopUpdates.test.ts now also encodes the held state through the schema and checks that the flag survives.
Scope
Bug fix: the dialog asks for a manual reinstall it does not need, because the schema was missing a field its interface already declares. A small fix of an obvious bug (one optional schema key), so it needs no issue. I found no issue or PR covering it; #32048, #32027, #27484 and #30804 touch the same dialog or updater for other causes.
Verification
To see it on Linux: in apps/desktop, run vp test run src/updates/DesktopUpdates.test.ts -t "holds a nightly install until its user switches to stable" with the new assertion on main. It fails (expected undefined to be true); with the fix it passes.
Re-checked against main 476fc9be66 on 2026-11-12: 32377ee merges cleanly with it (locally, not pushed), and the test file fails with that main's packages/contracts/src/ipc.ts (1 failed, 44 passed) and passes with this PR's (45 passed); output below. maria-rcks's run covers the dialog in packaged nightly AppImages; this re-check covers the test only.
The dialog itself: maria-rcks verified d30de5d8d4 against main 6c9adfb273 with real nightly AppImages and a managed policy (mode: "auto", allowNightly: false). On main the flag was lost over IPC and the dialog asked for a manual reinstall; on d30de5d IPC kept channelBlockedByPolicy: true, the dialog included this app, and confirming installed Stable. maria-rcks's captures: main, d30de5d8d4, the dialog. They predate this head: ipc.ts, DesktopUpdates.ts, UpdateTrackSwitchDialog.tsx, updateTrackSwitch.logic.ts and SettingsPanels.tsx all changed on main after 6c9adfb273.
Not checked on 32377ee: the dialog, because it only shows in a packaged nightly build with a managed policy file; and macOS and Windows.
Root cause, commands and output
IPC results are encoded through DesktopUpdateStateSchema (getUpdateState, and the state inside the check, download and install results), so the flag is dropped before it reaches the renderer. In that state no later update-state push arrives to put it back, so localUpdaterStaysPut sees enabled: false without the flag, and the Stable switch dialog prepares no local reinstall.
Compatibility: the key is optional. The IPC runs between the main and renderer processes of one app build. DesktopUpdateStatusReport reuses this schema for remote update status; older readers ignore the extra key, and newer readers accept reports without it.
cd apps/desktop && vp test run src/updates/DesktopUpdates.test.ts -t "holds a nightly install until its user switches to stable"
On main with the new assertion: fails at assert.isTrue((yield* encodeUpdateState(held)).channelBlockedByPolicy) (expected undefined to be true).
With the fix: passes.
node scripts/check-changed.ts 25bf84ab07f53f141c20239c63e383823a9d5ea0: format, lint, knip, typecheck (36 packages) and tests (2 files, 42 passed) all pass.
On 2026-11-12, on 32377ee merged locally with main 476fc9be66 (not pushed), cd apps/desktop && vp test run src/updates/DesktopUpdates.test.ts on Node 26.12.1 fails with packages/contracts/src/ipc.ts from that main and passes with this PR's version (selected lines, copied without changes):
× holds a nightly install until its user switches to stable 11ms
AssertionError: expected undefined to be true
❯ src/updates/DesktopUpdates.test.ts:1236:16
Test Files 1 failed (1)
Tests 1 failed | 44 passed (45)
ipc.ts from this PR:
Test Files 1 passed (1)
Tests 45 passed (45)
The merge of main by maria-rcks (32377ee) resolved one conflict in the test file's imports; this PR's added schema line is unchanged.
On the same merge, node scripts/check-changed.ts 476fc9be66 passes format, lint, knip, typecheck (37 packages) and tests (2 files, 47 passed). The 2026-11-12 run above replaces earlier ones on main dbfbe3993f and b68aedfdb7. For the "main" leg, packages/contracts/src/ipc.ts was checked out from 476fc9b into the merge, then restored. The lines shown are those of the full output that match the failing test, the assertion, its location and the two totals.
Generated with Claude Opus 5.5 and GPT-6.1 Sol in T3 Code Nightly
DesktopUpdateStateSchema had no channelBlockedByPolicy key, so encoding the update state for IPC stripped it and the settings dialog told policy-blocked nightly installs to reinstall by hand instead of preparing the switch to stable.
Add the optional key to the schema so the flag reaches the renderer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Macroscope's review found this PR approvable — Preserves an existing policy-blocked update flag across IPC so the Stable-switch flow can distinguish it from updates being disabled. The optional schema addition leaves update policy and product defaults unchanged.
You can add or adjust custom eligibility rules. Learn more.
Reviewing files that changed from the base of the PR and between 9b4c1c8 and b612ad4.
You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.
Use the checkbox below for a quick retry:
Trigger review
No actionable comments were generated in the recent review.
Walkthrough
The update state schema now accepts an optional channelBlockedByPolicy field with the literal value true. A test checks that encoding a held update state preserves this field.
Changes: Update state contract
Layer / File(s)
Summary
Schema field and encoding test packages/contracts/src/ipc.ts, apps/desktop/src/updates/DesktopUpdates.test.ts
DesktopUpdateStateSchema accepts the optional channelBlockedByPolicy: true field. The test encodes the held update state and checks that the field remains true.
This fix keeps the Stable-switch dialog from asking users with a policy-blocked nightly install to reinstall by hand. The change is small and additive, with a regression test, and no merge-blocking risk is evident.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title names the actual fix: the Stable switch no longer tells policy-blocked nightly apps to reinstall manually. The changeset adds the optional channelBlockedByPolicy key to DesktopUpdateStateSch…
Description check
The description follows the template well. It states the problem, the change, the scope (a bug fix with no issue needed, with reasons), and verification with concrete commands, outputs, and a manual e…
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Full details: Title check
Explanation
The title names the actual fix: the Stable switch no longer tells policy-blocked nightly apps to reinstall manually. The changeset adds the optional channelBlockedByPolicy key to DesktopUpdateStateSchema, which stops the flag from being dropped over IPC, and extends the held-state test. The title is specific and follows the conventional commit form. Its length (92 characters) is long but acceptable.
Full details: Description check
Explanation
The description follows the template well. It states the problem, the change, the scope (a bug fix with no issue needed, with reasons), and verification with concrete commands, outputs, and a manual end-to-end run. It notes what was not checked (macOS, Windows, and the dialog on the final head). The template's conventional commit title guidance is not in the description, but the PR title covers it. The description is long and includes a collapsible details block, which is more than the template asks for, but the required sections are all present and filled in.
Finishing Touches
Generate unit tests (beta)
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Reviewing files that changed from the base of the PR and between 32377ee and 9b4c1c8.
Files selected for processing: 2
apps/desktop/src/updates/DesktopUpdates.test.ts
packages/contracts/src/ipc.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.
merged current main into the head with a merge commit (32377ee91f). one conflict in apps/desktop/src/updates/DesktopUpdates.test.ts imports: kept this PR's DesktopUpdateStateSchema encoder and main's new release-feed helpers. no other changes. vp run check:changed passed on blacksmith (format, lint, knip, typecheck of 37 packages, 2 test files); DesktopUpdates.test.ts passed 43/43. no review-bot findings to address.
Verified in a real dev environment
verified as-is at d30de5d8d4f148078d4edf9347cbc74c63261e5b on linux.
reproduced on github/main at 6c9adfb2739205244b84e607736f6fd8b4611e69 in a separate worktree. built real nightly appimages from both sources with the production github update feed and installed an actual /etc/t3code/policy.json with mode: "auto", allowNightly: false.
drove the running apps with repo playwright dependencies. on main, initial renderer ipc dropped the flag and settings > about > update track > stable falsely requested manual reinstall. on this head, ipc retained channelBlockedByPolicy: true; the dialog included this app and omitted that message.
confirmed the switch. the real updater downloaded stable 0.1.8, invoked quitAndInstall, and replaced the appimage. the installed stable binary subsequently reported enabled: true, channel: "latest", currentVersion: "0.1.8"; the existing codex thread survived.
cancel preserved nightly. with a real mode: "off" policy, ipc omitted the channel-blocked flag and the managed-policy ui blocked update controls.
created projects and threads through the app. real codex gpt-6.1-sol turns completed on main desktop, this head's dev web client in /usr/bin/google-chrome, and this head's packaged desktop client. no mocks, fixtures, or replay provider.
blacksmith ran the existing desktop updater, remote updater, and track-switch logic suites: 3 files, 70 tests passed. no source changes or pushes were needed.
automatic reopening past this vm's first-run linux keyring prompt. i left the keyring untouched and restarted the downloaded stable binary with --password-store=basic, also used for both verification builds. macos, windows, native mobile, and remote update-report transport were not exercised here.
Thank you, Maria, for testing d30de5d against main with real nightly AppImages and a managed policy that allows updates but blocks nightly. Your run showed the fix end to end: on main the flag was lost over IPC and the Stable switch asked for a manual reinstall, while on this head IPC kept channelBlockedByPolicy: true, the dialog included this app, and confirming the switch let the real updater install stable 0.1.8 with the existing thread intact. It also confirmed that with updates fully off the flag stays absent and the update controls stay blocked, and that the 70 updater and track-switch tests pass.
Noted on what stays open: reopening after the update wasn't checked past the first-run keyring prompt, and macOS, Windows and the remote update report weren't exercised. The change only adds an optional field to the serialized update state, so those paths read it the same way, and we're glad to follow up if anyone sees different behavior there.
Adapt only the schema field and managed-policy encode regression from Victor Vogel's source. The desktop producer already emits the flag; the codec discarded it.
Source-PR: pingdotgg#32609
Source-Commit: d30de5d
Source-Head: 32377ee
Source-Author: Victor Vogel (only21mil)
Adapted-by: Codex (gpt-6.1-sol, xhigh) through the Codex harness
Extend the original producer case through direct IPC encoding/decoding and a JSON round-trip of DesktopUpdateStatusReport. The regression loses the actual true-only flag on all six paths at e66d7fc; the schema adaptation preserves it.
Related-Source-PR: pingdotgg#32609
Fork-Adaptation: transport coverage, retaining existing auto/off policy, install admission and prepared retry behavior
Model: gpt-6.1-sol (xhigh)
Harness: Codex
Evidence: Linux x64 packaged Electron apps, light theme, 1280×900, fresh synthetic state. Applied the reported managed policy (updates.mode=auto, allowNightly=false), opened Settings → About, selected Stable, and confirmed on both commits. Both used the same local registry/feed and an actual Stable 0.1.9 AppImage supporting migration 94; public Stable metadata supports only migration 81 and would introduce an unrelated database compatibility block.
Base only saved the track; head downloaded and installed Stable. The installed artifact's SHA-256 matched the served fixture. Automatic relaunch reached the splash screen; the installed artifact was then reopened separately to verify its version. Videos redact an unrelated CLI-path description and end with labelled result stills.
Automatic relaunch completing beyond the splash screen was not verified under the debugger/Xvfb session. Installation completed, and the resulting installed artifact was reopened separately to verify Stable 0.1.9.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
size:XS0-9 changed lines (additions + deletions).vouch:unvouchedAuthor is not yet trusted in the VOUCHED list.
4 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A nightly desktop app under a managed update policy that rules out nightly (
allowNightly: false) should offer a one-step switch to Stable. Instead the Stable switch dialog says "This app's updates are off here, so reinstall it yourself to switch.", which is false for this app: thechannelBlockedByPolicy: trueflag is dropped over IPC becauseDesktopUpdateStateSchemahas no such key.Change
Add
channelBlockedByPolicy: Schema.optionalKey(Schema.Literal(true))toDesktopUpdateStateSchema, matching theDesktopUpdateStateinterface field added in #27197. The existing test "holds a nightly install until its user switches to stable" inDesktopUpdates.test.tsnow also encodes the held state through the schema and checks that the flag survives.Scope
Bug fix: the dialog asks for a manual reinstall it does not need, because the schema was missing a field its interface already declares. A small fix of an obvious bug (one optional schema key), so it needs no issue. I found no issue or PR covering it; #32048, #32027, #27484 and #30804 touch the same dialog or updater for other causes.
Verification
To see it on Linux: in
apps/desktop, runvp test run src/updates/DesktopUpdates.test.ts -t "holds a nightly install until its user switches to stable"with the new assertion onmain. It fails (expectedundefinedto be true); with the fix it passes.Re-checked against main
476fc9be66on 2026-11-12: 32377ee merges cleanly with it (locally, not pushed), and the test file fails with that main'spackages/contracts/src/ipc.ts(1 failed, 44 passed) and passes with this PR's (45 passed); output below. maria-rcks's run covers the dialog in packaged nightly AppImages; this re-check covers the test only.The dialog itself: maria-rcks verified d30de5d8d4 against main
6c9adfb273with real nightly AppImages and a managed policy (mode: "auto", allowNightly: false). On main the flag was lost over IPC and the dialog asked for a manual reinstall; on d30de5d IPC keptchannelBlockedByPolicy: true, the dialog included this app, and confirming installed Stable. maria-rcks's captures: main, d30de5d8d4, the dialog. They predate this head:ipc.ts,DesktopUpdates.ts,UpdateTrackSwitchDialog.tsx,updateTrackSwitch.logic.tsandSettingsPanels.tsxall changed on main after6c9adfb273.Not checked on 32377ee: the dialog, because it only shows in a packaged nightly build with a managed policy file; and macOS and Windows.
Root cause, commands and output
IPC results are encoded through
DesktopUpdateStateSchema(getUpdateState, and the state inside the check, download and install results), so the flag is dropped before it reaches the renderer. In that state no later update-state push arrives to put it back, solocalUpdaterStaysPutseesenabled: falsewithout the flag, and the Stable switch dialog prepares no local reinstall.Compatibility: the key is optional. The IPC runs between the main and renderer processes of one app build.
DesktopUpdateStatusReportreuses this schema for remote update status; older readers ignore the extra key, and newer readers accept reports without it.cd apps/desktop && vp test run src/updates/DesktopUpdates.test.ts -t "holds a nightly install until its user switches to stable"mainwith the new assertion: fails atassert.isTrue((yield* encodeUpdateState(held)).channelBlockedByPolicy)(expectedundefinedto be true).node scripts/check-changed.ts 25bf84ab07f53f141c20239c63e383823a9d5ea0: format, lint, knip, typecheck (36 packages) and tests (2 files, 42 passed) all pass.On 2026-11-12, on 32377ee merged locally with main
476fc9be66(not pushed),cd apps/desktop && vp test run src/updates/DesktopUpdates.test.tson Node 26.12.1 fails withpackages/contracts/src/ipc.tsfrom that main and passes with this PR's version (selected lines, copied without changes):ipc.ts from main 476fc9b:
ipc.ts from this PR:
The merge of main by maria-rcks (32377ee) resolved one conflict in the test file's imports; this PR's added schema line is unchanged.
On the same merge,
node scripts/check-changed.ts 476fc9be66passes format, lint, knip, typecheck (37 packages) and tests (2 files, 47 passed). The 2026-11-12 run above replaces earlier ones on maindbfbe3993fandb68aedfdb7. For the "main" leg,packages/contracts/src/ipc.tswas checked out from 476fc9b into the merge, then restored. The lines shown are those of the full output that match the failing test, the assertion, its location and the two totals.Generated with Claude Opus 5.5 and GPT-6.1 Sol in T3 Code Nightly