Skip to content

sync: port upstream web files, desktop bounds, defaults (#3996–#4244)#182

Merged
cursor[bot] merged 14 commits into
mainfrom
codex/sync-20260725-l2-web-defaults-daf2
Jul 25, 2026
Merged

sync: port upstream web files, desktop bounds, defaults (#3996–#4244)#182
cursor[bot] merged 14 commits into
mainfrom
codex/sync-20260725-l2-web-defaults-daf2

Conversation

@aaditagrawal

@aaditagrawal aaditagrawal commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

Ports upstream web/desktop/defaults work through pingdotgg#4244 onto fork main (after #181).

Includes fork adaptations required for the pingdotgg#4177 orchestration resume path:

  • Keep both subscribeDomainEvents (fork live-buffer race fix) and latestSequence (upstream resume gap check)
  • Mark Claude Sonnet 5 as the provider default model so isDefault resolution matches contracts
  • Update model-selection tests for the new isDefault fallback path
  • Stub openPath on desktop ElectronShell fixtures

Merge order

#181 (merged) → #182#184#183#185#186#187

Test plan

  • vp run typecheck
  • vp check on touched files
  • CI Check + Test green on this PR
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Desktop windows now restore and persist size, position, and maximized state.
    • Drag files from the workspace tree into the composer to insert mentions.
    • Workspace image previews now show loading and error states.
    • Sidebar width is remembered and constrained for usable layouts.
    • Added environment indicators and improved default branch/model selection.
    • Added stable and nightly release channel options.
  • Improvements

    • Improved shell stream resume, synchronization, and update coalescing.
    • Updated default models to GPT-5.6 variants and expanded Claude context-window choices.
    • Improved changed-files controls with clearer tooltips and accessibility labels.
  • Bug Fixes

    • Improved handling of provider messages, remote default branches, and release asset publishing.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@cursor[bot], you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ec6f111-b476-4333-b7d1-1e6639c9c082

📥 Commits

Reviewing files that changed from the base of the PR and between 81550dd and 23a0d8f.

📒 Files selected for processing (1)
  • apps/server/src/provider/Layers/ClaudeAdapter.ts
📝 Walkthrough

Walkthrough

The PR updates release automation, persists and restores desktop window state, adds bounded orchestration shell streaming, refines provider model defaults and Claude handling, improves web workspace interactions, and prioritizes remote default Git refs.

Changes

Release automation

Layer / File(s) Summary
Release workflow inputs and publication
.github/workflows/release.yml
Stable/nightly release inputs, preflight metadata, platform artifacts, CLI publishing, web deployment, stable finalization, and Discord announcements are updated.

Desktop window state

Layer / File(s) Summary
Persisted bounds contract
apps/desktop/src/settings/DesktopAppSettings.ts, apps/desktop/src/settings/DesktopAppSettings.test.ts
Window bounds and maximized state are validated, normalized, serialized, persisted, and tested.
Window restoration and flushing
apps/desktop/src/window/DesktopWindow.ts, apps/desktop/src/app/DesktopLifecycle.ts
The Electron window restores display-valid bounds, debounces updates, flushes pending writes, and flushes before shutdown.
Window behavior tests and mocks
apps/desktop/src/window/DesktopWindow.test.ts, apps/desktop/src/backend/*.test.ts, apps/desktop/src/updates/DesktopUpdates.test.ts
Mocks and tests cover restoration, maximization, debounce, invalid bounds, display failures, and close-time persistence.

Orchestration and providers

Layer / File(s) Summary
Shell resume and coalescing
apps/server/src/orchestration/..., apps/server/src/ws.ts, apps/server/src/server.test.ts
The engine exposes its latest sequence, while shell streams bound replay gaps, coalesce updates, retry projection reads, and fall back to snapshots.
Model defaults and Claude handling
packages/contracts/src/model.ts, apps/server/src/provider/Layers/*, apps/web/src/modelSelection.ts, apps/mobile/src/lib/modelOptions.ts
Explicit model defaults flow through provider contracts and selection; Claude context windows and SDK system subtypes are handled explicitly, and Codex preferred defaults are applied.

Web workspace interactions

Layer / File(s) Summary
Workspace navigation and files
apps/web/src/components/AppSidebarLayout.tsx, apps/web/src/components/threadSidebarWidth.ts, apps/web/src/components/BranchToolbar*.tsx, apps/web/src/components/files/*
Sidebar sizing, environment indicators, default branch selection, workspace image previews, and file-tree drag behavior are updated.
Composer and changed-file UI
apps/web/src/components/chat/*
Composer mention drops, unavailable-environment handling, sticky changed-file headers, tooltip actions, and related tests are added or updated.

Git refs

Layer / File(s) Summary
Default remote refs
apps/server/src/vcs/GitVcsDriverCore.ts, apps/server/src/vcs/GitVcsDriverCore.test.ts
Remote refs matching the repository default branch are marked default and prioritized in listing results.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main themes of the PR: upstream web porting, desktop window bounds, and default/model updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/sync-20260725-l2-web-defaults-daf2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). labels Jul 25, 2026
@cursor
cursor Bot force-pushed the codex/sync-20260725-l2-web-defaults-daf2 branch from 1ea182a to 71eedba Compare July 25, 2026 08:52

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ea182a18f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pin release writes to the fork

When this workflow is run from any repository context other than the fork—for example after an accidental upstream sync—using github.repository_owner here, implicit repository targeting in action-gh-release, and the later git push origin directs privileged writes to that repository. Restore the GITHUB_REPOSITORY == aaditagrawal/t3code preflight guard and explicitly target the fork before minting or using the release token.

AGENTS.md reference: AGENTS.md:L5-L10

Useful? React with 👍 / 👎.

# Node ABI, not libc ABI. Use a standard GitHub-hosted runner + Node bullseye
# container instead of Blacksmith Ubuntu 2004 labels, which can queue forever.
runs-on: ubuntu-24.04
runs-on: blacksmith-8vcpu-ubuntu-2404

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Build the WSL addon against an older libc

Windows releases now compile the bundled WSL pty.node directly on Ubuntu 24.04, so the native addon may require newer glibc/libstdc++ symbols and fail to load on common Ubuntu 20.04 or 22.04 WSL distributions. N-API only stabilizes the Node ABI, not these system-library ABIs; retain the previous Debian Bullseye container (or another oldest-supported sysroot) for this build.

Useful? React with 👍 / 👎.

Comment on lines 251 to 253
resolveSelectableModel(entry.driverKind, selectedModel, options) ??
resolveSelectableModel(entry.driverKind, defaultModel, options) ??
options.find((option) => option.isDefault)?.slug ??
options[0]?.slug ??

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve configured defaults when snapshots omit isDefault

For providers that do not emit model-level isDefault metadata, an absent, hidden, or unavailable selection now falls straight to catalog order. Claude's catalog has no model-level default and starts with claude-fable-5, so this bypasses the declared claude-sonnet-5 chat default; the same helper also makes text generation choose Fable instead of the declared Haiku default. Keep the DEFAULT_MODEL_BY_PROVIDER and DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER fallbacks when no live default is present.

AGENTS.md reference: AGENTS.md:L22-L24

Useful? React with 👍 / 👎.

Comment on lines +311 to +312
needs: [preflight, relay_public_config, build_wsl_node_pty]
if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore the Android APK release job

The release graph now proceeds directly from preflight to the desktop matrix after deleting build_android_apk; a repo-wide search finds no remaining assembleRelease, android-apk, or APK artifact producer. Consequently every release silently stops publishing the signed Android APK, preventing Android users from installing or upgrading from these releases.

Useful? React with 👍 / 👎.

Comment on lines +662 to +663
- name: Publish CLI package
run: node apps/server/scripts/cli.ts publish --tag "${{ needs.preflight.outputs.cli_dist_tag }}" --app-version "${{ needs.preflight.outputs.version }}" --verbose

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep fork releases independent of the upstream npm package

Every fork release now publishes the globally named t3 package before creating the GitHub release, and the latter explicitly depends on this job. In aaditagrawal/t3code, the OIDC identity will not match the official package's upstream trusted-publisher configuration, so publishing fails and blocks all desktop releases; if credentials do authorize it, this instead publishes the fork build as the official upstream CLI. Disable this job for the fork or publish to a fork-owned package without gating desktop releases on it.

Useful? React with 👍 / 👎.

Comment on lines 3743 to 3744
});
context.currentApiModelId = apiModelId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Update Claude context limits when switching models

When sendTurn changes the effective Claude model or its 200k/1M option, this path now updates only currentApiModelId and leaves lastKnownContextWindow from the previous selection. Subsequent stream and task-progress usage events therefore report the previous model's maximum—or omit maxTokens when the session started without a model—until a later result or context-usage query arrives, making the context banner incorrect throughout the active turn. Set the resolved context window after setModel succeeds.

AGENTS.md reference: AGENTS.md:L22-L24

Useful? React with 👍 / 👎.

Comment on lines 704 to 709
for x64_manifest in release-assets/*-mac-x64.yml; do
arm64_manifest="${x64_manifest/-x64.yml/.yml}"
if [[ ! -f "$arm64_manifest" ]]; then
echo "Missing matching arm64 macOS manifest for $x64_manifest" >&2
exit 1
arm64_manifest="${x64_manifest%-x64.yml}.yml"
if [[ -f "$arm64_manifest" ]]; then
node scripts/merge-update-manifests.ts --platform mac "$arm64_manifest" "$x64_manifest"
rm -f "$x64_manifest"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fail releases when a macOS updater manifest is missing

If either macOS architecture stops producing its updater manifest while still producing a DMG, this loop now silently skips the merge and the release continues. A missing arm64 manifest leaves only the suffixed x64 file, while a missing x64 manifest leaves an unmerged arm64-only canonical manifest; either case publishes a release whose auto-update metadata is absent or incomplete. Restore the previous checks that require both manifests and fail when no matching pair is found.

Useful? React with 👍 / 👎.

Comment on lines +103 to +104
const [sidebarWidth, setSidebarWidth] = useState(readInitialThreadSidebarWidth);
const sidebarMaximumWidth = resolveThreadSidebarMaximumWidth(window.innerWidth);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recompute the sidebar maximum after viewport resizing

The new maxWidth is derived from window.innerWidth only when this component renders, with no resize subscription. If the user enlarges a desktop window without crossing the mobile breakpoint, the sidebar rail retains the old smaller maximum—for example, opening at 1000px and then maximizing still prevents resizing beyond roughly 360px—even though shouldAcceptWidth previously used the wrapper's live width. Track viewport changes or rely on the existing dynamic acceptance predicate.

Useful? React with 👍 / 👎.

Comment on lines +124 to +127
const assetUrl = useAssetUrlState(props.environmentId, {
_tag: "workspace-file",
threadId: props.threadRef.threadId,
path: props.absolutePath,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Support image previews before a draft thread is persisted

On a new unsent draft, threadRef.threadId exists only in the client draft store; the server's assetsCreateUrl handler resolves workspace-file resources through getThreadShellById, so it returns AssetWorkspaceContextNotFoundError until the first turn creates the thread. Because image paths now disable the normal project-file query and always use this asset URL, opening any image from the file panel in a fresh draft shows “Unable to load workspace image.” Use the known project/cwd context for drafts or retain a file-query/blob fallback.

Useful? React with 👍 / 👎.

@cursor
cursor Bot force-pushed the codex/sync-20260725-l2-web-defaults-daf2 branch 5 times, most recently from cf43415 to 21b9602 Compare July 25, 2026 09:42
@cursor
cursor Bot changed the base branch from codex/sync-20260725-l1-foundation-daf2 to main July 25, 2026 09:45
Rhiz3K and others added 10 commits July 25, 2026 09:46
Co-authored-by: Rhiz3K <rhiz3k@protonmail.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
(cherry picked from commit 0936fd2)
…otgg#4140)

Co-authored-by: Julius Marminge <julius0216@outlook.com>
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
(cherry picked from commit 8ca4eec)
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
(cherry picked from commit 2c199aa)
…up (pingdotgg#4177)

Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
(cherry picked from commit db4b2d8)
…g#4113)

Co-authored-by: Julius Marminge <julius0216@outlook.com>
(cherry picked from commit c8a04bd)
Co-authored-by: codex <codex@users.noreply.github.com>
(cherry picked from commit 5d34f9f)
…#4217)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit c0bb237)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 23c18fd)
…om origin main (pingdotgg#4240)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 62cf461)
…arning rows (pingdotgg#4244)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 6f34ad3)
@cursor
cursor Bot force-pushed the codex/sync-20260725-l2-web-defaults-daf2 branch from 21b9602 to 4bd87dd Compare July 25, 2026 09:48
cursoragent and others added 4 commits July 25, 2026 09:57
Upstream pingdotgg#4177 added latestSequence and dropped subscribeDomainEvents; the
fork needs both so shell/thread live buffers do not race PubSub subscribe.
Also mark Claude Sonnet 5 as the default model, update model-selection
tests for isDefault fallbacks, and stub openPath in desktop fixtures.

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
Local oxfmt rewrites the Nayuki for-loop spacing differently than CI.
Keep the upstream/main form and ignore the vendored file in fmt.

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
Upstream pingdotgg#4240 made Sonnet 5 expose a contextWindow selector (200k
default, 1M opt-in). Update fork tests that still assumed native 1M
with no selector, and wire subscribeDomainEvents into the live-burst
shell coalesce fixture.

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
Model selection on sendTurn already calls setModel for gateway ids like
sonnet[1m], but lastKnownContextWindow was left stale after pingdotgg#4240, so
token-usage maxTokens ignored mid-session context switches.

Co-authored-by: aaditagrawal <aaditagrawal@users.noreply.github.com>
@cursor
cursor Bot merged commit f5dc5df into main Jul 25, 2026
11 of 12 checks passed

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.github/workflows/release.yml (1)

570-580: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

release/*.yml also sweeps in electron-builder debug output.

electron-builder writes builder-debug.yml (and builder-effective-config.yaml) into the output dir, so this glob publishes them as release assets, and because every platform emits the same builder-debug.yml the merge-multiple: true download in release will collide on it. Narrowing the glob avoids both.

🔧 Narrow the manifest glob
-            "release/*.yml"; do
+            "release/latest*.yml" \
+            "release/nightly*.yml"; do
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 570 - 580, Update the asset-copy
loop in the release workflow to replace the broad release/*.yml glob with a
narrowly scoped manifest pattern that matches only the intended release manifest
files, excluding electron-builder debug and effective-config outputs such as
builder-debug.yml and builder-effective-config.yaml.
apps/server/src/provider/Layers/ClaudeAdapter.ts (1)

3737-3750: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Stale lastKnownContextWindow after mid-turn model switch.

sendTurn updates context.currentApiModelId/context.session.model on a model switch but no longer refreshes context.lastKnownContextWindow, unlike startSession which seeds it via selectedClaudeContextWindow(modelSelection). Until the turn completes (via completeTurn/queryCurrentContextUsage), every message_delta-driven thread.token-usage.updated event will report the previous model's maxTokens, misrepresenting context usage for the whole turn.

🐛 Proposed fix
     if (modelSelection?.model) {
       const apiModelId = resolveClaudeApiModelId(modelSelection);
       if (context.currentApiModelId !== apiModelId) {
         yield* Effect.tryPromise({
           try: () => context.query.setModel(apiModelId),
           catch: (cause) => toRequestError(input.threadId, "turn/setModel", cause),
         });
         context.currentApiModelId = apiModelId;
+        context.lastKnownContextWindow =
+          selectedClaudeContextWindow(modelSelection) ?? context.lastKnownContextWindow;
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/ClaudeAdapter.ts` around lines 3737 - 3750,
Update the model-switch branch in sendTurn to refresh
context.lastKnownContextWindow using selectedClaudeContextWindow(modelSelection)
after successfully switching models, matching startSession behavior. Keep the
existing currentApiModelId and session.model updates unchanged so token-usage
events immediately use the new model’s context window.
🧹 Nitpick comments (10)
apps/server/src/vcs/GitVcsDriverCore.test.ts (1)

599-617: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert first-page ordering, not only the flags.

This test can pass even if origin/<initialBranch> is returned after other refs. Call listRefs with limit: 1 and assert that the first ref is the remote default, reusing the existing feature/only-local branch as the competing ref.

As per coding guidelines, backend changes must include and run focused tests for the changed behavior.

Suggested test adjustment
-        const refs = yield* driver.listRefs({ cwd });
+        const refs = yield* driver.listRefs({ cwd, limit: 1 });
+        assert.equal(refs.refs[0]?.name, `origin/${initialBranch}`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/vcs/GitVcsDriverCore.test.ts` around lines 599 - 617, The
test case around GitVcsDriver.listRefs should verify ordering as well as default
flags. Call listRefs with limit: 1, assert the returned first ref is
origin/${initialBranch} and is marked remote/default, using the existing
feature/only-local branch as the competing ref; add or update the focused test
coverage for this behavior.

Source: Coding guidelines

.github/workflows/release.yml (4)

610-616: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document why this job stays on the GitHub-hosted runner.

The inline # blacksmith-8vcpu-ubuntu-2404 reads as an accidental leftover. If the reason is npm trusted publishing needing a GitHub-hosted OIDC issuer, say so explicitly so a future cleanup doesn't switch it back and break id-token: write.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 610 - 616, Update the inline
runner comment on the release job’s runs-on setting to explicitly document that
it must remain GitHub-hosted because npm trusted publishing requires the
GitHub-hosted OIDC issuer. Remove or replace the ambiguous Blacksmith reference
while preserving the existing runner and id-token: write permission.

363-375: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Same tracing-config load block is copy-pasted into three jobs. Identical download + sed + mask + >> $GITHUB_ENV logic appears here, in publish_cli (Lines 639-651) and in deploy_web (Lines 816-828). A local composite action (.github/actions/load-relay-tracing) would keep the masking rule in one place — if the mask line ever drifts in one copy, the token leaks in that job's logs.

Also note cat "$config_path" >> "$GITHUB_ENV" assumes single-line values; a multi-line value in the generated file would corrupt the env file. A while read loop with heredoc delimiters is safer if the generator can ever emit those.

As per coding guidelines: "extract shared logic instead of duplicating local implementations."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 363 - 375, The relay tracing
configuration download and loading logic is duplicated across three workflow
jobs and writes values unsafely to GITHUB_ENV. Extract the shared download,
token masking, and environment-loading steps into the local composite action
.github/actions/load-relay-tracing, update the affected jobs to invoke it, and
use delimiter-based environment-file writes to preserve multiline values.

Source: Coding guidelines


662-663: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

npm publish now precedes the GitHub Release, and it isn't reversible.

release runs after publish_cli, so a failure in manifest merging or asset upload leaves an npm version live that points at desktop artifacts and release notes that don't exist. Consider publishing to npm last, or publishing the CLI under a throwaway dist-tag and promoting the tag (npm dist-tag add) only after release succeeds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 662 - 663, Update the release
workflow around the “Publish CLI package” step so the CLI is not permanently
published under its final npm dist-tag until the GitHub Release completes
successfully. Either move final npm publication after the release job or publish
under a temporary tag and promote it with npm dist-tag add only after release
succeeds, while preserving the existing version and package inputs.

741-780: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Collapse the duplicate release publish steps

Both Publish release and Publish first release call softprops/action-gh-release@v2 with the same payload except previous_tag; omitting it has the same effect as an empty comparison base, so keep one step and pass ${{ needs.preflight.outputs.previous_tag }}.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 741 - 780, Collapse the duplicate
“Publish release” and “Publish first release” steps into one
softprops/action-gh-release@v2 step, using the shared payload and retaining the
release condition. Include previous_tag as needs.preflight.outputs.previous_tag;
its empty value must preserve first-release behavior.
apps/server/src/provider/Layers/ClaudeProvider.ts (1)

142-145: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate contextWindow option descriptors instead of reusing a shared factory.

claude-sonnet-5 (246-254) and claude-sonnet-4-6 (273-277) manually re-declare the exact same 200k-default options already encapsulated in CLAUDE_CONTEXT_WINDOW_OPTIONS/buildClaudeContextWindowDescriptor() (113-124), and claude-fable-5 (142-145)/claude-opus-4-6 (207-210) duplicate an equivalent 1m-default variant with no shared helper at all. A future change to the option set risks silently diverging across these four sites.

Consider extracting a small factory, e.g. buildClaudeContextWindowDescriptor({ default: "200k" | "1m" }), and reusing it at all four call sites (and for withClaudeSonnet5ContextWindowSelector).

Also applies to: 207-210, 246-254, 273-277

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/ClaudeProvider.ts` around lines 142 - 145,
Extract or extend a shared context-window descriptor factory near
CLAUDE_CONTEXT_WINDOW_OPTIONS, such as buildClaudeContextWindowDescriptor, to
support both "200k" and "1m" defaults. Replace the duplicated option arrays in
withClaudeFable5ContextWindowSelector, withClaudeOpus46ContextWindowSelector,
withClaudeSonnet5ContextWindowSelector, and
withClaudeSonnet46ContextWindowSelector, preserving each model’s current
default.
apps/desktop/src/settings/DesktopAppSettings.ts (1)

53-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Export MIN_MAIN_WINDOW_SIZE so the window creation options stop duplicating 840/620.

DesktopWindow.ts hardcodes minWidth: 840 / minHeight: 620, which must stay in lockstep with this schema check — otherwise Electron can produce a window whose bounds silently fail DesktopWindowBoundsSchema and never persist.

As per coding guidelines, "extract shared logic instead of duplicating local implementations".

♻️ Proposed change
-const MIN_MAIN_WINDOW_SIZE = {
+export const MIN_MAIN_WINDOW_SIZE = {
   width: 840,
   height: 620,
 } as const;

Then in apps/desktop/src/window/DesktopWindow.ts:

-      minWidth: 840,
-      minHeight: 620,
+      minWidth: DesktopAppSettings.MIN_MAIN_WINDOW_SIZE.width,
+      minHeight: DesktopAppSettings.MIN_MAIN_WINDOW_SIZE.height,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/settings/DesktopAppSettings.ts` around lines 53 - 62, Export
MIN_MAIN_WINDOW_SIZE from DesktopAppSettings.ts, then update the DesktopWindow
creation options to reuse its width and height values for minWidth and minHeight
instead of hardcoding 840 and 620. Preserve the existing
DesktopWindowBoundsSchema validation and ensure both window constraints remain
synchronized.

Source: Coding guidelines

apps/desktop/src/window/DesktopWindow.ts (1)

108-118: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Full containment in a single display discards valid multi-monitor layouts.

A window straddling two adjacent displays fits neither one, so it is reset to the default centered size on every launch even though it was fully visible. Consider accepting bounds whose visible intersection with some display is substantial (e.g. title bar / a minimum overlap area) instead of requiring total containment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/window/DesktopWindow.ts` around lines 108 - 118, Update
windowFitsWithinDisplay to accept bounds that meaningfully overlap a display,
rather than requiring complete containment within one display. Preserve
rejection for windows with only negligible visibility by validating a
substantial intersection, such as a visible title bar or minimum overlap area,
so valid windows spanning adjacent monitors are retained.
apps/web/src/components/AppSidebarLayout.tsx (1)

161-169: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Memoize the sidebar resizable config to stop redundant effect reruns. The resizable object passed to <Sidebar> is rebuilt with a new identity on every AppSidebarLayout render (inline shouldAcceptWidth arrow), and sidebar.tsx's persisted-width hydration effect keys off that same object's identity, so it fires far more often than needed.

  • apps/web/src/components/AppSidebarLayout.tsx#L161-L169: wrap the resizable object (and ideally sidebarProviderStyle) in useMemo keyed on sidebarMaximumWidth, THREAD_SIDEBAR_MIN_WIDTH, THREAD_MAIN_CONTENT_MIN_WIDTH, THREAD_SIDEBAR_WIDTH_STORAGE_KEY, and setSidebarWidth so its identity is stable across unrelated re-renders.
  • apps/web/src/components/ui/sidebar.tsx#L565-L585: no change needed here once the caller's resizable reference is stable — this effect's [resolvedResizable] dependency will then only fire on genuine config changes instead of every parent re-render.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/AppSidebarLayout.tsx` around lines 161 - 169, Memoize
the resizable configuration in AppSidebarLayout using useMemo with dependencies
on sidebarMaximumWidth, the referenced width constants/storage key, and
setSidebarWidth; also memoize sidebarProviderStyle if needed for stable
identity. In apps/web/src/components/AppSidebarLayout.tsx:161-169, update the
resizable construction while preserving its existing behavior. In
apps/web/src/components/ui/sidebar.tsx:565-585, make no direct changes because
stabilizing the caller’s resizable reference fixes the resolvedResizable effect
reruns.

Source: Coding guidelines

apps/web/src/components/chat/ChangedFilesTree.tsx (1)

44-112: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated changed-files header into a shared component. ChangedFilesCard and AssistantChangedFilesSectionInner render the same header (container classes, sticky header, pluralized file-count label, DiffStatLabel, tooltip-wrapped expand/collapse and view-diff icon buttons) with near-identical class strings, introduced in the same PR — a clear candidate for a single shared subcomponent instead of two copies that must be kept in sync manually.

  • apps/web/src/components/chat/ChangedFilesTree.tsx#L44-L112: extract this header (props: file count, summary stat, allDirectoriesExpanded, onToggleAllDirectories, onOpenTurnDiff) into a shared ChangedFilesCardHeader component.
  • apps/web/src/components/chat/MessagesTimeline.tsx#L1281-L1341: replace this header block with the same shared ChangedFilesCardHeader component, passing turnSummary/checkpointFiles-derived props.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/chat/ChangedFilesTree.tsx` around lines 44 - 112, The
duplicated changed-files header should be centralized in a shared
ChangedFilesCardHeader component. In
apps/web/src/components/chat/ChangedFilesTree.tsx lines 44-112, extract the
container, file-count/stat display, and tooltip-wrapped expand/collapse and
view-diff controls into that component with props for file count, summary stat,
allDirectoriesExpanded, onToggleAllDirectories, and onOpenTurnDiff. In
apps/web/src/components/chat/MessagesTimeline.tsx lines 1281-1341, replace the
existing header block with ChangedFilesCardHeader, passing the corresponding
turnSummary/checkpointFiles-derived values; do not change the surrounding tree
or section behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 215-221: Remove the “Upload relay client tracing config” artifact
step and stop transferring relay-client-tracing.env between jobs. Update each
consuming job to receive only non-secret tracing fields through job outputs and
source T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN directly from the configured
secrets or relay state, preserving the existing tracing configuration behavior
without exposing the token as an artifact.
- Around line 285-293: The WSL `pty.node` build in the release workflow now
links against the Ubuntu 24.04 runner’s newer glibc, breaking older supported
distributions. Update the build block around `pty_pkg`, `pty_dir`, and `node-gyp
rebuild` to compile in an older-glibc environment such as the existing Bullseye
container or an equivalent `zig cc`/sysroot setup, while preserving the
generated `wsl-prebuild/pty.node` artifact flow.
- Around line 701-710: Update the “Merge macOS updater manifests” loop in the
release workflow so a missing $arm64_manifest causes the step to fail
immediately instead of silently skipping. Preserve the existing merge and
x64-manifest removal behavior when both manifests exist, and emit a clear error
before exiting for the missing-manifest case.
- Around line 672-678: Update the “Mint release app token” step using
actions/create-github-app-token@v2 to include a repositories input limited to
the current repository, using the repository name derived from
github.repository. Preserve the existing app ID, private key, and owner
configuration.

In `@apps/desktop/src/app/DesktopLifecycle.ts`:
- Around line 81-85: Update the shutdown flow in handleBeforeQuit so
DesktopWindow.flushMainWindowBounds is best-effort and bounded, ensuring a
stalled settings write cannot delay shutdown.request or shutdown.awaitComplete.
Add the established timeout/fallback behavior around the flush, then always
continue to the shutdown steps after success, failure, or timeout.

---

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 570-580: Update the asset-copy loop in the release workflow to
replace the broad release/*.yml glob with a narrowly scoped manifest pattern
that matches only the intended release manifest files, excluding
electron-builder debug and effective-config outputs such as builder-debug.yml
and builder-effective-config.yaml.

In `@apps/server/src/provider/Layers/ClaudeAdapter.ts`:
- Around line 3737-3750: Update the model-switch branch in sendTurn to refresh
context.lastKnownContextWindow using selectedClaudeContextWindow(modelSelection)
after successfully switching models, matching startSession behavior. Keep the
existing currentApiModelId and session.model updates unchanged so token-usage
events immediately use the new model’s context window.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 610-616: Update the inline runner comment on the release job’s
runs-on setting to explicitly document that it must remain GitHub-hosted because
npm trusted publishing requires the GitHub-hosted OIDC issuer. Remove or replace
the ambiguous Blacksmith reference while preserving the existing runner and
id-token: write permission.
- Around line 363-375: The relay tracing configuration download and loading
logic is duplicated across three workflow jobs and writes values unsafely to
GITHUB_ENV. Extract the shared download, token masking, and environment-loading
steps into the local composite action .github/actions/load-relay-tracing, update
the affected jobs to invoke it, and use delimiter-based environment-file writes
to preserve multiline values.
- Around line 662-663: Update the release workflow around the “Publish CLI
package” step so the CLI is not permanently published under its final npm
dist-tag until the GitHub Release completes successfully. Either move final npm
publication after the release job or publish under a temporary tag and promote
it with npm dist-tag add only after release succeeds, while preserving the
existing version and package inputs.
- Around line 741-780: Collapse the duplicate “Publish release” and “Publish
first release” steps into one softprops/action-gh-release@v2 step, using the
shared payload and retaining the release condition. Include previous_tag as
needs.preflight.outputs.previous_tag; its empty value must preserve
first-release behavior.

In `@apps/desktop/src/settings/DesktopAppSettings.ts`:
- Around line 53-62: Export MIN_MAIN_WINDOW_SIZE from DesktopAppSettings.ts,
then update the DesktopWindow creation options to reuse its width and height
values for minWidth and minHeight instead of hardcoding 840 and 620. Preserve
the existing DesktopWindowBoundsSchema validation and ensure both window
constraints remain synchronized.

In `@apps/desktop/src/window/DesktopWindow.ts`:
- Around line 108-118: Update windowFitsWithinDisplay to accept bounds that
meaningfully overlap a display, rather than requiring complete containment
within one display. Preserve rejection for windows with only negligible
visibility by validating a substantial intersection, such as a visible title bar
or minimum overlap area, so valid windows spanning adjacent monitors are
retained.

In `@apps/server/src/provider/Layers/ClaudeProvider.ts`:
- Around line 142-145: Extract or extend a shared context-window descriptor
factory near CLAUDE_CONTEXT_WINDOW_OPTIONS, such as
buildClaudeContextWindowDescriptor, to support both "200k" and "1m" defaults.
Replace the duplicated option arrays in withClaudeFable5ContextWindowSelector,
withClaudeOpus46ContextWindowSelector, withClaudeSonnet5ContextWindowSelector,
and withClaudeSonnet46ContextWindowSelector, preserving each model’s current
default.

In `@apps/server/src/vcs/GitVcsDriverCore.test.ts`:
- Around line 599-617: The test case around GitVcsDriver.listRefs should verify
ordering as well as default flags. Call listRefs with limit: 1, assert the
returned first ref is origin/${initialBranch} and is marked remote/default,
using the existing feature/only-local branch as the competing ref; add or update
the focused test coverage for this behavior.

In `@apps/web/src/components/AppSidebarLayout.tsx`:
- Around line 161-169: Memoize the resizable configuration in AppSidebarLayout
using useMemo with dependencies on sidebarMaximumWidth, the referenced width
constants/storage key, and setSidebarWidth; also memoize sidebarProviderStyle if
needed for stable identity. In
apps/web/src/components/AppSidebarLayout.tsx:161-169, update the resizable
construction while preserving its existing behavior. In
apps/web/src/components/ui/sidebar.tsx:565-585, make no direct changes because
stabilizing the caller’s resizable reference fixes the resolvedResizable effect
reruns.

In `@apps/web/src/components/chat/ChangedFilesTree.tsx`:
- Around line 44-112: The duplicated changed-files header should be centralized
in a shared ChangedFilesCardHeader component. In
apps/web/src/components/chat/ChangedFilesTree.tsx lines 44-112, extract the
container, file-count/stat display, and tooltip-wrapped expand/collapse and
view-diff controls into that component with props for file count, summary stat,
allDirectoriesExpanded, onToggleAllDirectories, and onOpenTurnDiff. In
apps/web/src/components/chat/MessagesTimeline.tsx lines 1281-1341, replace the
existing header block with ChangedFilesCardHeader, passing the corresponding
turnSummary/checkpointFiles-derived values; do not change the surrounding tree
or section behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e9216c5f-ce6b-40d5-adff-8bfa208ade07

📥 Commits

Reviewing files that changed from the base of the PR and between 6935c15 and 81550dd.

📒 Files selected for processing (59)
  • .github/workflows/release.yml
  • apps/desktop/src/app/DesktopLifecycle.ts
  • apps/desktop/src/backend/DesktopBackendPool.test.ts
  • apps/desktop/src/backend/DesktopServerExposure.test.ts
  • apps/desktop/src/settings/DesktopAppSettings.test.ts
  • apps/desktop/src/settings/DesktopAppSettings.ts
  • apps/desktop/src/updates/DesktopUpdates.test.ts
  • apps/desktop/src/window/DesktopApplicationMenu.test.ts
  • apps/desktop/src/window/DesktopWindow.test.ts
  • apps/desktop/src/window/DesktopWindow.ts
  • apps/mobile/src/features/threads/new-task-flow-provider.tsx
  • apps/mobile/src/lib/modelOptions.ts
  • apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts
  • apps/server/src/orchestration/Layers/OrchestrationEngine.ts
  • apps/server/src/orchestration/Services/OrchestrationEngine.ts
  • apps/server/src/provider/Layers/ClaudeAdapter.test.ts
  • apps/server/src/provider/Layers/ClaudeAdapter.ts
  • apps/server/src/provider/Layers/ClaudeProvider.test.ts
  • apps/server/src/provider/Layers/ClaudeProvider.ts
  • apps/server/src/provider/Layers/CodexProvider.test.ts
  • apps/server/src/provider/Layers/CodexProvider.ts
  • apps/server/src/provider/Layers/ProviderRegistry.test.ts
  • apps/server/src/relay/AgentAwarenessRelay.test.ts
  • apps/server/src/server.test.ts
  • apps/server/src/serverRuntimeStartup.test.ts
  • apps/server/src/vcs/GitVcsDriverCore.test.ts
  • apps/server/src/vcs/GitVcsDriverCore.ts
  • apps/server/src/ws.ts
  • apps/web/src/assets/assetUrls.ts
  • apps/web/src/components/AppSidebarLayout.tsx
  • apps/web/src/components/BranchToolbar.logic.test.ts
  • apps/web/src/components/BranchToolbar.logic.ts
  • apps/web/src/components/BranchToolbar.tsx
  • apps/web/src/components/BranchToolbarBranchSelector.tsx
  • apps/web/src/components/BranchToolbarEnvironmentSelector.tsx
  • apps/web/src/components/chat/ChangedFilesTree.test.tsx
  • apps/web/src/components/chat/ChangedFilesTree.tsx
  • apps/web/src/components/chat/ChatComposer.tsx
  • apps/web/src/components/chat/MessagesTimeline.test.tsx
  • apps/web/src/components/chat/MessagesTimeline.tsx
  • apps/web/src/components/chat/composerMentionDrag.test.ts
  • apps/web/src/components/chat/composerMentionDrag.ts
  • apps/web/src/components/files/FileBrowserPanel.tsx
  • apps/web/src/components/files/FilePreviewPanel.tsx
  • apps/web/src/components/files/fileTreeDragMention.test.ts
  • apps/web/src/components/files/fileTreeDragMention.ts
  • apps/web/src/components/files/projectFilesQueryState.ts
  • apps/web/src/components/threadSidebarWidth.test.ts
  • apps/web/src/components/threadSidebarWidth.ts
  • apps/web/src/components/ui/sidebar.tsx
  • apps/web/src/modelSelection.test.ts
  • apps/web/src/modelSelection.ts
  • apps/web/src/providerModels.ts
  • packages/client-runtime/src/operations/projects.test.ts
  • packages/contracts/src/model.ts
  • packages/contracts/src/server.ts
  • packages/contracts/src/settings.test.ts
  • packages/contracts/src/settings.ts
  • vite.config.ts

Comment on lines +215 to +221
- name: Upload relay client tracing config
uses: actions/upload-artifact@v7
with:
name: relay-client-tracing-config
path: ${{ runner.temp }}/relay-client-tracing.env
if-no-files-found: error
retention-days: 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Tracing token is persisted to a downloadable workflow artifact.

relay-client-tracing.env contains T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN. Artifacts are not encrypted and are downloadable by anyone with repo read access (and via the API) for the retention window, so this leaks the OTLP ingest credential outside the masked-secret boundary. Consider passing the non-secret tracing fields via job outputs and sourcing the token directly from secrets/the relay state in each consuming job instead of shipping it as a file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 215 - 221, Remove the “Upload
relay client tracing config” artifact step and stop transferring
relay-client-tracing.env between jobs. Update each consuming job to receive only
non-secret tracing fields through job outputs and source
T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN directly from the configured secrets or
relay state, preserving the existing tracing configuration behavior without
exposing the token as an artifact.

Comment on lines 285 to 293
# Resolve node-pty from apps/server (where it's a dependency) and build
# its native binary from source for Linux against an older glibc. node-
# addon-api resolves from node-pty's own dependency tree inside the
# mounted workspace, so node-gyp has everything it needs.
# its native binary from source for Linux. node-addon-api resolves from
# node-pty's own dependency tree, so node-gyp has everything it needs.
pty_pkg="$(node -e "console.log(require.resolve('node-pty/package.json', { paths: ['$GITHUB_WORKSPACE/apps/server'] }))")"
pty_dir="$(dirname "$pty_pkg")"
docker run --rm \
-v "$GITHUB_WORKSPACE:$GITHUB_WORKSPACE" \
-w "$pty_dir" \
node:24.13.1-bullseye \
bash -lc 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq build-essential python3 >/dev/null && npx --yes node-gyp rebuild'
( cd "$pty_dir" && npx --yes node-gyp rebuild )
mkdir -p wsl-prebuild
cp "$pty_dir/build/Release/pty.node" wsl-prebuild/pty.node
file wsl-prebuild/pty.node

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Dropping the bullseye container raises the glibc floor for the shipped WSL binary.

Building pty.node directly on the Ubuntu 24.04 runner links it against that runner's glibc (2.39). N-API makes it Node-version-agnostic, but not glibc-agnostic — users whose WSL distro is older (Ubuntu 22.04 → 2.35, Debian 12 → 2.36) will get GLIBC_2.3x not found at load time, which is precisely the first-launch failure this prebuild exists to avoid. Worth keeping the older-glibc build environment (container or zig cc/sysroot) unless the supported WSL distro floor is explicitly 24.04+.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 285 - 293, The WSL `pty.node`
build in the release workflow now links against the Ubuntu 24.04 runner’s newer
glibc, breaking older supported distributions. Update the build block around
`pty_pkg`, `pty_dir`, and `node-gyp rebuild` to compile in an older-glibc
environment such as the existing Bullseye container or an equivalent `zig
cc`/sysroot setup, while preserving the generated `wsl-prebuild/pty.node`
artifact flow.

Comment on lines +672 to +678
- id: app_token
name: Mint release app token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Scope the app token to this repository.

Without a repositories: input the minted token carries the installation's permissions for every repository the app is installed on, so any step in this job (or a compromised action in it) gets far more than release-publishing rights.

🔒 Restrict installation scope
           app-id: ${{ secrets.RELEASE_APP_ID }}
           private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
           owner: ${{ github.repository_owner }}
+          repositories: ${{ github.event.repository.name }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- id: app_token
name: Mint release app token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
- id: app_token
name: Mint release app token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: ${{ github.event.repository.name }}
🧰 Tools
🪛 zizmor (1.26.1)

[error] 678-678: dangerous use of GitHub App tokens (github-app): token granted access to all repositories for this owner's app installation

(github-app)


[error] 674-674: dangerous use of GitHub App tokens (github-app): app token inherits blanket installation permissions

(github-app)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 672 - 678, Update the “Mint
release app token” step using actions/create-github-app-token@v2 to include a
repositories input limited to the current repository, using the repository name
derived from github.repository. Preserve the existing app ID, private key, and
owner configuration.

Source: Linters/SAST tools

Comment on lines 701 to 710
- name: Merge macOS updater manifests
run: |
set -euo pipefail
shopt -s nullglob

found_mac_manifest=false
for x64_manifest in release-assets/*-mac-x64.yml; do
arm64_manifest="${x64_manifest/-x64.yml/.yml}"
if [[ ! -f "$arm64_manifest" ]]; then
echo "Missing matching arm64 macOS manifest for $x64_manifest" >&2
exit 1
arm64_manifest="${x64_manifest%-x64.yml}.yml"
if [[ -f "$arm64_manifest" ]]; then
node scripts/merge-update-manifests.ts --platform mac "$arm64_manifest" "$x64_manifest"
rm -f "$x64_manifest"
fi

found_mac_manifest=true
node scripts/merge-update-manifests.ts --platform mac \
"$arm64_manifest" \
"$x64_manifest"
rm -f "$x64_manifest"
done

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

A missing arm64 manifest silently ships a mis-named mac updater manifest.

If $arm64_manifest isn't present the loop just skips, leaving latest-mac-x64.yml in release-assets — which then gets published while latest-mac.yml is absent, so macOS auto-update silently 404s for the whole release. The (commented-out) Windows variant correctly hard-failed in this case; the mac path should too.

🐛 Fail loudly instead of skipping
           for x64_manifest in release-assets/*-mac-x64.yml; do
             arm64_manifest="${x64_manifest%-x64.yml}.yml"
-            if [[ -f "$arm64_manifest" ]]; then
-              node scripts/merge-update-manifests.ts --platform mac "$arm64_manifest" "$x64_manifest"
-              rm -f "$x64_manifest"
-            fi
+            if [[ ! -f "$arm64_manifest" ]]; then
+              echo "Missing matching arm64 mac manifest for $x64_manifest" >&2
+              exit 1
+            fi
+            node scripts/merge-update-manifests.ts --platform mac "$arm64_manifest" "$x64_manifest"
+            rm -f "$x64_manifest"
           done
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Merge macOS updater manifests
run: |
set -euo pipefail
shopt -s nullglob
found_mac_manifest=false
for x64_manifest in release-assets/*-mac-x64.yml; do
arm64_manifest="${x64_manifest/-x64.yml/.yml}"
if [[ ! -f "$arm64_manifest" ]]; then
echo "Missing matching arm64 macOS manifest for $x64_manifest" >&2
exit 1
arm64_manifest="${x64_manifest%-x64.yml}.yml"
if [[ -f "$arm64_manifest" ]]; then
node scripts/merge-update-manifests.ts --platform mac "$arm64_manifest" "$x64_manifest"
rm -f "$x64_manifest"
fi
found_mac_manifest=true
node scripts/merge-update-manifests.ts --platform mac \
"$arm64_manifest" \
"$x64_manifest"
rm -f "$x64_manifest"
done
- name: Merge macOS updater manifests
run: |
shopt -s nullglob
for x64_manifest in release-assets/*-mac-x64.yml; do
arm64_manifest="${x64_manifest%-x64.yml}.yml"
if [[ ! -f "$arm64_manifest" ]]; then
echo "Missing matching arm64 mac manifest for $x64_manifest" >&2
exit 1
fi
node scripts/merge-update-manifests.ts --platform mac "$arm64_manifest" "$x64_manifest"
rm -f "$x64_manifest"
done
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 701 - 710, Update the “Merge
macOS updater manifests” loop in the release workflow so a missing
$arm64_manifest causes the step to fail immediately instead of silently
skipping. Preserve the existing merge and x64-manifest removal behavior when
both manifests exist, and emit a clear error before exiting for the
missing-manifest case.

Comment on lines 81 to 85
const shutdown = yield* DesktopShutdown.DesktopShutdown;
const desktopWindow = yield* DesktopWindow.DesktopWindow;
yield* desktopWindow.flushMainWindowBounds;
yield* shutdown.request;
yield* shutdown.awaitComplete;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Bound the bounds flush so a stalled settings write cannot block quit/relaunch.

flushMainWindowBounds joins an in-flight settings file write. handleBeforeQuit already called event.preventDefault(), so if that write stalls the app never reaches shutdown.request and never quits. Persisting window geometry is best-effort and should not gate shutdown.

As per coding guidelines, prioritize "reliability, predictable behavior under load and failures".

🛡️ Proposed fix
     const shutdown = yield* DesktopShutdown.DesktopShutdown;
     const desktopWindow = yield* DesktopWindow.DesktopWindow;
-    yield* desktopWindow.flushMainWindowBounds;
+    yield* desktopWindow.flushMainWindowBounds.pipe(
+      Effect.timeout(MAIN_WINDOW_BOUNDS_FLUSH_TIMEOUT_MS),
+      Effect.ignore,
+    );
     yield* shutdown.request;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const shutdown = yield* DesktopShutdown.DesktopShutdown;
const desktopWindow = yield* DesktopWindow.DesktopWindow;
yield* desktopWindow.flushMainWindowBounds;
yield* shutdown.request;
yield* shutdown.awaitComplete;
const shutdown = yield* DesktopShutdown.DesktopShutdown;
const desktopWindow = yield* DesktopWindow.DesktopWindow;
yield* desktopWindow.flushMainWindowBounds.pipe(
Effect.timeout(MAIN_WINDOW_BOUNDS_FLUSH_TIMEOUT_MS),
Effect.ignore,
);
yield* shutdown.request;
yield* shutdown.awaitComplete;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/app/DesktopLifecycle.ts` around lines 81 - 85, Update the
shutdown flow in handleBeforeQuit so DesktopWindow.flushMainWindowBounds is
best-effort and bounded, ensuring a stalled settings write cannot delay
shutdown.request or shutdown.awaitComplete. Add the established timeout/fallback
behavior around the flush, then always continue to the shutdown steps after
success, failure, or timeout.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants