perf(windows): reduce first-start editor discovery time - #6124

Open
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup
Open

perf(windows): reduce first-start editor discovery time#6124
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup

Conversation

@simon-curtis

@simon-curtissimon-curtis commented Aug 11, 2026

Copy link
Copy Markdown

What Changed

On Windows, command discovery now lists each PATH directory once, matches PATHEXT candidates case-insensitively, and performs the existing executable-file check only for matching entries.

PATH order and resolveCommandCandidates remain unchanged. If a directory cannot be listed, lookup falls back to upstream's direct candidate probes.

Why

The first client configuration probes up to 22 editor commands. Upstream performs a filesystem probe for every PATH × PATHEXT candidate, so editor discovery can consume seconds on a long Windows PATH and approaches its five-second timeout.

This narrows the PR to reducing first-start/editor-discovery time without changing desktop PATH hydration or packaging behavior.

Measured Impact

Targeted Windows command-discovery benchmark on 71 PATH entries and 12 PATHEXT extensions; medians from five alternating runs:

WorkloadUpstreamThis PRSaved
5 provider commands199 ms49 ms150 ms
22 editor commands1,964 ms425 ms1,539 ms

This is a resolver benchmark rather than an end-to-end launch measurement. The exact gain depends on PATH and PATHEXT size.

Scope

No PowerShell environment probing, provider lifecycle, installer, packaging, or WSL behavior changes are included.

Verification

  • vp fmt --check packages/shared/src/shell.ts packages/shared/src/shell.test.ts
  • vp lint packages/shared/src/shell.ts packages/shared/src/shell.test.ts --report-unused-disable-directives
  • vp run --filter @t3tools/shared typecheck
  • vp test run packages/shared/src/shell.test.ts — 30 tests passed

model: gpt-5.6-sol
harness: Codex in T3 Code

Note

Reduce first-start editor discovery time on Windows with case-insensitive PATH resolution

  • On Windows, resolveCommandPathForPlatform in shell.ts now lists each PATH directory and builds a case-insensitive filename map to match command candidates, avoiding failed probes caused by case mismatches.
  • Falls back to the existing direct-probe behavior when a PATH directory cannot be listed.
  • Adds a test in shell.test.ts that creates a mixed-case executable in a temp directory and verifies case-insensitive resolution on win32 with PATHEXT set.

Macroscope summarized ae911eb.


Note

Medium Risk
Changes Windows command resolution used by editor and CLI discovery; behavior is narrowed with a listing fallback, but incorrect matching could miss or mis-resolve executables on win32.

Overview
On Windows, resolveCommandPathForPlatform no longer probes every PATHEXT candidate in each PATH directory. For each PATH entry it lists the directory (when listing succeeds), maps filenames case-insensitively, and only runs the existing executable check for names that actually match the command candidates.

If a PATH directory cannot be listed, resolution falls back to the prior behavior of probing candidates directly. PATH order and candidate generation are unchanged.

A new test creates a mixed-case .eXe under a temp PATH entry and asserts resolveCommandPath resolves provider-tool on win32.

Reviewed by Cursor Bugbot for commit ae911eb. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4eaab204-5559-42cc-a1b9-f24199c759a8

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

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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 11, 2026
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
@@ -177,17 +177,20 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {

const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
maintenanceCapabilities,
maintenanceCapabilities: maintenanceCapabilities.get,

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.

🟠 HighDrivers/ClaudeDriver.ts:180

maintenanceCapabilities.get() returns the npm updater immediately after ClaudeDriver.create(), even when claude on PATH actually resolves to a native or Homebrew install. Before the deferred maintenanceCapabilities.refresh completes, ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance can therefore return the wrong update action, so an update requested during startup runs npm instead of claude update/Homebrew. makeProviderMaintenanceCapabilitiesSource initializes its current value via resolver.resolve(options) before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare claude command. Consider initializing current with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/ClaudeDriver.ts around line 180:
`maintenanceCapabilities.get()` returns the npm updater immediately after `ClaudeDriver.create()`, even when `claude` on PATH actually resolves to a native or Homebrew install. Before the deferred `maintenanceCapabilities.refresh` completes, `ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance` can therefore return the wrong update action, so an update requested during startup runs `npm` instead of `claude update`/Homebrew. `makeProviderMaintenanceCapabilitiesSource` initializes its `current` value via `resolver.resolve(options)` before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare `claude` command. Consider initializing `current` with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

Comment threadscripts/build-desktop-artifact.ts
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
Comment threadpackages/shared/src/shell.ts Outdated
@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify long phasesperf(windows): reduce startup work and clarify installer phasesAug 11, 2026
Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found.

You can customize Macroscope's approvability policy. Learn more.

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fef06d741f8c710f0957139af036297f3f3aad90. Configure here.

Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@simon-curtis
simon-curtis marked this pull request as draft August 13, 2026 11:14

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

Read the Windows shell half against today's main. CONFLICTING.

Desktop: drops the -NoProfile PATH probe entirely and keeps only the profile probe, merging process PATH + profile PATH + knownWindowsCliDirs. That is the ~2s startup win (each PowerShell spawn was ~2s). It also deletes the no-profile source #6356 is trying to make read Machine+User, so a CLI installed after launch still will not show up until the next process start unless the profile sees it.

Shared: directory listing + case-insensitive name map instead of probing every PATHEXT candidate, with a fallback when readDirectory fails (ACL / unreadable share). That is the right Windows lookup. Returning [command] when the input already has a PATHEXT suffix is also right — the old code generated three casings of the same extension.

Do not land this and #6356/#4896/#6301 independently; they all rewrite installWindowsEnvironment.

@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify installer phasesperf(windows): speed up shell command discoveryAug 17, 2026
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from 324294b to 96a86d6CompareAugust 17, 2026 16:42
@simon-curtissimon-curtis changed the title perf(windows): speed up shell command discoveryperf(windows): reduce first-start editor discovery timeAug 17, 2026
@simon-curtis

Copy link
Copy Markdown
Author

Scope update: I have reframed this PR around reducing first-start/editor-discovery time on Windows.

After comparing the original branch with current main and the overlapping Windows environment work, the earlier ~2-second startup result was attributable to removing a PowerShell PATH probe. That change affects PATH refresh semantics and conflicts with the other installWindowsEnvironment work, so it no longer belongs in this PR. The installer, packaging, provider-lifecycle, desktop environment, and WSL changes have also been removed.

What remains is the independently useful shared lookup optimization: list each Windows PATH directory once, match PATHEXT candidates case-insensitively, preserve PATH order, and fall back to the existing direct probes when listing fails. I also restored resolveCommandCandidates exactly to main, so this PR no longer changes candidate casing behavior.

A targeted five-run benchmark on this machine's 71-entry PATH and 12 PATHEXT values measured provider discovery at 199 ms → 49 ms and first editor discovery at 1,964 ms → 425 ms. This is a resolver benchmark, not an end-to-end startup claim.

@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 17, 2026
Comment threadpackages/shared/src/shell.ts
@simon-curtis
simon-curtis marked this pull request as ready for review August 17, 2026 17:25
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 7 times, most recently from 99f0376 to d5a0e7aCompareAugust 26, 2026 08:27
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from cfb0d3e to 9ecfbc4CompareSeptember 1, 2026 08:32
Simon Curtisand others added 2 commits September 2, 2026 11:03
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This note is part of an automated cleanup pass.

Preserving these details from items reviewed in the cleanup pass.

Carryover from #6221 at 59849f94e2: retain the shell.test.ts listing-failure cases. PermissionDenied, Busy, and Unknown must fall back to direct executable probes, while NotFound and BadResource can skip the directory. Keep mixed-case filenames and PATH order intact. The cross-command directory reuse may be useful if Windows measurements still show repeated listings, but the two PRs' benchmarks use different machines and cannot establish which is faster.

Carry over the slow-discovery evidence from #5050 at 61278bee47. On the reported Windows host with 46 PATH entries and 12 PATHEXT entries, getConfig timed out near 5,000 ms on every call. Concurrent discovery finished in 4,787 ms in that report. Its focused test interrupts one caller during discovery, then requires a later caller to obtain editors. Check cold discovery and caller interruption while retaining bounded expiry and current Linux/WSL file-manager checks. These are reported measurements and a test case to retain, not results rerun here.

Keep the trace evidence from #4778 at dc7f1727a6 in the Windows resolver review. Its author reported 28,577 shell.isExecutableFile spans from 22 command lookups in 13.7 seconds, plus trace files rotating at 10 MB about every 40 seconds. The distinct code change makes isExecutableFile untraced. Main still traces that helper, although its command cache and VCS limits are now bounded. Check first-scan and miss-heavy trace cost with this PR. Do not copy the older unbounded cache or treat the reported timings as rerun results.

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

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simon-curtis@t3dotgg@CDVolvik
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

perf(windows): reduce first-start editor discovery time - #6124

Open
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup
Open

perf(windows): reduce first-start editor discovery time#6124
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup

Conversation

@simon-curtis

@simon-curtissimon-curtis commented Aug 11, 2026

Copy link
Copy Markdown

What Changed

On Windows, command discovery now lists each PATH directory once, matches PATHEXT candidates case-insensitively, and performs the existing executable-file check only for matching entries.

PATH order and resolveCommandCandidates remain unchanged. If a directory cannot be listed, lookup falls back to upstream's direct candidate probes.

Why

The first client configuration probes up to 22 editor commands. Upstream performs a filesystem probe for every PATH × PATHEXT candidate, so editor discovery can consume seconds on a long Windows PATH and approaches its five-second timeout.

This narrows the PR to reducing first-start/editor-discovery time without changing desktop PATH hydration or packaging behavior.

Measured Impact

Targeted Windows command-discovery benchmark on 71 PATH entries and 12 PATHEXT extensions; medians from five alternating runs:

WorkloadUpstreamThis PRSaved
5 provider commands199 ms49 ms150 ms
22 editor commands1,964 ms425 ms1,539 ms

This is a resolver benchmark rather than an end-to-end launch measurement. The exact gain depends on PATH and PATHEXT size.

Scope

No PowerShell environment probing, provider lifecycle, installer, packaging, or WSL behavior changes are included.

Verification

  • vp fmt --check packages/shared/src/shell.ts packages/shared/src/shell.test.ts
  • vp lint packages/shared/src/shell.ts packages/shared/src/shell.test.ts --report-unused-disable-directives
  • vp run --filter @t3tools/shared typecheck
  • vp test run packages/shared/src/shell.test.ts — 30 tests passed

model: gpt-5.6-sol
harness: Codex in T3 Code

Note

Reduce first-start editor discovery time on Windows with case-insensitive PATH resolution

  • On Windows, resolveCommandPathForPlatform in shell.ts now lists each PATH directory and builds a case-insensitive filename map to match command candidates, avoiding failed probes caused by case mismatches.
  • Falls back to the existing direct-probe behavior when a PATH directory cannot be listed.
  • Adds a test in shell.test.ts that creates a mixed-case executable in a temp directory and verifies case-insensitive resolution on win32 with PATHEXT set.

Macroscope summarized ae911eb.


Note

Medium Risk
Changes Windows command resolution used by editor and CLI discovery; behavior is narrowed with a listing fallback, but incorrect matching could miss or mis-resolve executables on win32.

Overview
On Windows, resolveCommandPathForPlatform no longer probes every PATHEXT candidate in each PATH directory. For each PATH entry it lists the directory (when listing succeeds), maps filenames case-insensitively, and only runs the existing executable check for names that actually match the command candidates.

If a PATH directory cannot be listed, resolution falls back to the prior behavior of probing candidates directly. PATH order and candidate generation are unchanged.

A new test creates a mixed-case .eXe under a temp PATH entry and asserts resolveCommandPath resolves provider-tool on win32.

Reviewed by Cursor Bugbot for commit ae911eb. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4eaab204-5559-42cc-a1b9-f24199c759a8

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

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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 11, 2026
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
@@ -177,17 +177,20 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {

const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
maintenanceCapabilities,
maintenanceCapabilities: maintenanceCapabilities.get,

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.

🟠 HighDrivers/ClaudeDriver.ts:180

maintenanceCapabilities.get() returns the npm updater immediately after ClaudeDriver.create(), even when claude on PATH actually resolves to a native or Homebrew install. Before the deferred maintenanceCapabilities.refresh completes, ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance can therefore return the wrong update action, so an update requested during startup runs npm instead of claude update/Homebrew. makeProviderMaintenanceCapabilitiesSource initializes its current value via resolver.resolve(options) before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare claude command. Consider initializing current with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/ClaudeDriver.ts around line 180:
`maintenanceCapabilities.get()` returns the npm updater immediately after `ClaudeDriver.create()`, even when `claude` on PATH actually resolves to a native or Homebrew install. Before the deferred `maintenanceCapabilities.refresh` completes, `ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance` can therefore return the wrong update action, so an update requested during startup runs `npm` instead of `claude update`/Homebrew. `makeProviderMaintenanceCapabilitiesSource` initializes its `current` value via `resolver.resolve(options)` before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare `claude` command. Consider initializing `current` with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

Comment threadscripts/build-desktop-artifact.ts
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
Comment threadpackages/shared/src/shell.ts Outdated
@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify long phasesperf(windows): reduce startup work and clarify installer phasesAug 11, 2026
Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found.

You can customize Macroscope's approvability policy. Learn more.

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fef06d741f8c710f0957139af036297f3f3aad90. Configure here.

Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@simon-curtis
simon-curtis marked this pull request as draft August 13, 2026 11:14

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

Read the Windows shell half against today's main. CONFLICTING.

Desktop: drops the -NoProfile PATH probe entirely and keeps only the profile probe, merging process PATH + profile PATH + knownWindowsCliDirs. That is the ~2s startup win (each PowerShell spawn was ~2s). It also deletes the no-profile source #6356 is trying to make read Machine+User, so a CLI installed after launch still will not show up until the next process start unless the profile sees it.

Shared: directory listing + case-insensitive name map instead of probing every PATHEXT candidate, with a fallback when readDirectory fails (ACL / unreadable share). That is the right Windows lookup. Returning [command] when the input already has a PATHEXT suffix is also right — the old code generated three casings of the same extension.

Do not land this and #6356/#4896/#6301 independently; they all rewrite installWindowsEnvironment.

@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify installer phasesperf(windows): speed up shell command discoveryAug 17, 2026
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from 324294b to 96a86d6CompareAugust 17, 2026 16:42
@simon-curtissimon-curtis changed the title perf(windows): speed up shell command discoveryperf(windows): reduce first-start editor discovery timeAug 17, 2026
@simon-curtis

Copy link
Copy Markdown
Author

Scope update: I have reframed this PR around reducing first-start/editor-discovery time on Windows.

After comparing the original branch with current main and the overlapping Windows environment work, the earlier ~2-second startup result was attributable to removing a PowerShell PATH probe. That change affects PATH refresh semantics and conflicts with the other installWindowsEnvironment work, so it no longer belongs in this PR. The installer, packaging, provider-lifecycle, desktop environment, and WSL changes have also been removed.

What remains is the independently useful shared lookup optimization: list each Windows PATH directory once, match PATHEXT candidates case-insensitively, preserve PATH order, and fall back to the existing direct probes when listing fails. I also restored resolveCommandCandidates exactly to main, so this PR no longer changes candidate casing behavior.

A targeted five-run benchmark on this machine's 71-entry PATH and 12 PATHEXT values measured provider discovery at 199 ms → 49 ms and first editor discovery at 1,964 ms → 425 ms. This is a resolver benchmark, not an end-to-end startup claim.

@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 17, 2026
Comment threadpackages/shared/src/shell.ts
@simon-curtis
simon-curtis marked this pull request as ready for review August 17, 2026 17:25
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 7 times, most recently from 99f0376 to d5a0e7aCompareAugust 26, 2026 08:27
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from cfb0d3e to 9ecfbc4CompareSeptember 1, 2026 08:32
Simon Curtisand others added 2 commits September 2, 2026 11:03
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This note is part of an automated cleanup pass.

Preserving these details from items reviewed in the cleanup pass.

Carryover from #6221 at 59849f94e2: retain the shell.test.ts listing-failure cases. PermissionDenied, Busy, and Unknown must fall back to direct executable probes, while NotFound and BadResource can skip the directory. Keep mixed-case filenames and PATH order intact. The cross-command directory reuse may be useful if Windows measurements still show repeated listings, but the two PRs' benchmarks use different machines and cannot establish which is faster.

Carry over the slow-discovery evidence from #5050 at 61278bee47. On the reported Windows host with 46 PATH entries and 12 PATHEXT entries, getConfig timed out near 5,000 ms on every call. Concurrent discovery finished in 4,787 ms in that report. Its focused test interrupts one caller during discovery, then requires a later caller to obtain editors. Check cold discovery and caller interruption while retaining bounded expiry and current Linux/WSL file-manager checks. These are reported measurements and a test case to retain, not results rerun here.

Keep the trace evidence from #4778 at dc7f1727a6 in the Windows resolver review. Its author reported 28,577 shell.isExecutableFile spans from 22 command lookups in 13.7 seconds, plus trace files rotating at 10 MB about every 40 seconds. The distinct code change makes isExecutableFile untraced. Main still traces that helper, although its command cache and VCS limits are now bounded. Check first-scan and miss-heavy trace cost with this PR. Do not copy the older unbounded cache or treat the reported timings as rerun results.

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

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simon-curtis@t3dotgg@CDVolvik
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(windows): reduce first-start editor discovery time - #6124

Open
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup
Open

perf(windows): reduce first-start editor discovery time#6124
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup

Conversation

@simon-curtis

@simon-curtissimon-curtis commented Aug 11, 2026

Copy link
Copy Markdown

What Changed

On Windows, command discovery now lists each PATH directory once, matches PATHEXT candidates case-insensitively, and performs the existing executable-file check only for matching entries.

PATH order and resolveCommandCandidates remain unchanged. If a directory cannot be listed, lookup falls back to upstream's direct candidate probes.

Why

The first client configuration probes up to 22 editor commands. Upstream performs a filesystem probe for every PATH × PATHEXT candidate, so editor discovery can consume seconds on a long Windows PATH and approaches its five-second timeout.

This narrows the PR to reducing first-start/editor-discovery time without changing desktop PATH hydration or packaging behavior.

Measured Impact

Targeted Windows command-discovery benchmark on 71 PATH entries and 12 PATHEXT extensions; medians from five alternating runs:

WorkloadUpstreamThis PRSaved
5 provider commands199 ms49 ms150 ms
22 editor commands1,964 ms425 ms1,539 ms

This is a resolver benchmark rather than an end-to-end launch measurement. The exact gain depends on PATH and PATHEXT size.

Scope

No PowerShell environment probing, provider lifecycle, installer, packaging, or WSL behavior changes are included.

Verification

  • vp fmt --check packages/shared/src/shell.ts packages/shared/src/shell.test.ts
  • vp lint packages/shared/src/shell.ts packages/shared/src/shell.test.ts --report-unused-disable-directives
  • vp run --filter @t3tools/shared typecheck
  • vp test run packages/shared/src/shell.test.ts — 30 tests passed

model: gpt-5.6-sol
harness: Codex in T3 Code

Note

Reduce first-start editor discovery time on Windows with case-insensitive PATH resolution

  • On Windows, resolveCommandPathForPlatform in shell.ts now lists each PATH directory and builds a case-insensitive filename map to match command candidates, avoiding failed probes caused by case mismatches.
  • Falls back to the existing direct-probe behavior when a PATH directory cannot be listed.
  • Adds a test in shell.test.ts that creates a mixed-case executable in a temp directory and verifies case-insensitive resolution on win32 with PATHEXT set.

Macroscope summarized ae911eb.


Note

Medium Risk
Changes Windows command resolution used by editor and CLI discovery; behavior is narrowed with a listing fallback, but incorrect matching could miss or mis-resolve executables on win32.

Overview
On Windows, resolveCommandPathForPlatform no longer probes every PATHEXT candidate in each PATH directory. For each PATH entry it lists the directory (when listing succeeds), maps filenames case-insensitively, and only runs the existing executable check for names that actually match the command candidates.

If a PATH directory cannot be listed, resolution falls back to the prior behavior of probing candidates directly. PATH order and candidate generation are unchanged.

A new test creates a mixed-case .eXe under a temp PATH entry and asserts resolveCommandPath resolves provider-tool on win32.

Reviewed by Cursor Bugbot for commit ae911eb. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4eaab204-5559-42cc-a1b9-f24199c759a8

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

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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 11, 2026
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
@@ -177,17 +177,20 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {

const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
maintenanceCapabilities,
maintenanceCapabilities: maintenanceCapabilities.get,

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.

🟠 HighDrivers/ClaudeDriver.ts:180

maintenanceCapabilities.get() returns the npm updater immediately after ClaudeDriver.create(), even when claude on PATH actually resolves to a native or Homebrew install. Before the deferred maintenanceCapabilities.refresh completes, ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance can therefore return the wrong update action, so an update requested during startup runs npm instead of claude update/Homebrew. makeProviderMaintenanceCapabilitiesSource initializes its current value via resolver.resolve(options) before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare claude command. Consider initializing current with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/ClaudeDriver.ts around line 180:
`maintenanceCapabilities.get()` returns the npm updater immediately after `ClaudeDriver.create()`, even when `claude` on PATH actually resolves to a native or Homebrew install. Before the deferred `maintenanceCapabilities.refresh` completes, `ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance` can therefore return the wrong update action, so an update requested during startup runs `npm` instead of `claude update`/Homebrew. `makeProviderMaintenanceCapabilitiesSource` initializes its `current` value via `resolver.resolve(options)` before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare `claude` command. Consider initializing `current` with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

Comment threadscripts/build-desktop-artifact.ts
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
Comment threadpackages/shared/src/shell.ts Outdated
@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify long phasesperf(windows): reduce startup work and clarify installer phasesAug 11, 2026
Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found.

You can customize Macroscope's approvability policy. Learn more.

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fef06d741f8c710f0957139af036297f3f3aad90. Configure here.

Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@simon-curtis
simon-curtis marked this pull request as draft August 13, 2026 11:14

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

Read the Windows shell half against today's main. CONFLICTING.

Desktop: drops the -NoProfile PATH probe entirely and keeps only the profile probe, merging process PATH + profile PATH + knownWindowsCliDirs. That is the ~2s startup win (each PowerShell spawn was ~2s). It also deletes the no-profile source #6356 is trying to make read Machine+User, so a CLI installed after launch still will not show up until the next process start unless the profile sees it.

Shared: directory listing + case-insensitive name map instead of probing every PATHEXT candidate, with a fallback when readDirectory fails (ACL / unreadable share). That is the right Windows lookup. Returning [command] when the input already has a PATHEXT suffix is also right — the old code generated three casings of the same extension.

Do not land this and #6356/#4896/#6301 independently; they all rewrite installWindowsEnvironment.

@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify installer phasesperf(windows): speed up shell command discoveryAug 17, 2026
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from 324294b to 96a86d6CompareAugust 17, 2026 16:42
@simon-curtissimon-curtis changed the title perf(windows): speed up shell command discoveryperf(windows): reduce first-start editor discovery timeAug 17, 2026
@simon-curtis

Copy link
Copy Markdown
Author

Scope update: I have reframed this PR around reducing first-start/editor-discovery time on Windows.

After comparing the original branch with current main and the overlapping Windows environment work, the earlier ~2-second startup result was attributable to removing a PowerShell PATH probe. That change affects PATH refresh semantics and conflicts with the other installWindowsEnvironment work, so it no longer belongs in this PR. The installer, packaging, provider-lifecycle, desktop environment, and WSL changes have also been removed.

What remains is the independently useful shared lookup optimization: list each Windows PATH directory once, match PATHEXT candidates case-insensitively, preserve PATH order, and fall back to the existing direct probes when listing fails. I also restored resolveCommandCandidates exactly to main, so this PR no longer changes candidate casing behavior.

A targeted five-run benchmark on this machine's 71-entry PATH and 12 PATHEXT values measured provider discovery at 199 ms → 49 ms and first editor discovery at 1,964 ms → 425 ms. This is a resolver benchmark, not an end-to-end startup claim.

@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 17, 2026
Comment threadpackages/shared/src/shell.ts
@simon-curtis
simon-curtis marked this pull request as ready for review August 17, 2026 17:25
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 7 times, most recently from 99f0376 to d5a0e7aCompareAugust 26, 2026 08:27
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from cfb0d3e to 9ecfbc4CompareSeptember 1, 2026 08:32
Simon Curtisand others added 2 commits September 2, 2026 11:03
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This note is part of an automated cleanup pass.

Preserving these details from items reviewed in the cleanup pass.

Carryover from #6221 at 59849f94e2: retain the shell.test.ts listing-failure cases. PermissionDenied, Busy, and Unknown must fall back to direct executable probes, while NotFound and BadResource can skip the directory. Keep mixed-case filenames and PATH order intact. The cross-command directory reuse may be useful if Windows measurements still show repeated listings, but the two PRs' benchmarks use different machines and cannot establish which is faster.

Carry over the slow-discovery evidence from #5050 at 61278bee47. On the reported Windows host with 46 PATH entries and 12 PATHEXT entries, getConfig timed out near 5,000 ms on every call. Concurrent discovery finished in 4,787 ms in that report. Its focused test interrupts one caller during discovery, then requires a later caller to obtain editors. Check cold discovery and caller interruption while retaining bounded expiry and current Linux/WSL file-manager checks. These are reported measurements and a test case to retain, not results rerun here.

Keep the trace evidence from #4778 at dc7f1727a6 in the Windows resolver review. Its author reported 28,577 shell.isExecutableFile spans from 22 command lookups in 13.7 seconds, plus trace files rotating at 10 MB about every 40 seconds. The distinct code change makes isExecutableFile untraced. Main still traces that helper, although its command cache and VCS limits are now bounded. Check first-scan and miss-heavy trace cost with this PR. Do not copy the older unbounded cache or treat the reported timings as rerun results.

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

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simon-curtis@t3dotgg@CDVolvik
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(windows): reduce first-start editor discovery time - #6124

Open
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup
Open

perf(windows): reduce first-start editor discovery time#6124
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup

Conversation

@simon-curtis

@simon-curtissimon-curtis commented Aug 11, 2026

Copy link
Copy Markdown

What Changed

On Windows, command discovery now lists each PATH directory once, matches PATHEXT candidates case-insensitively, and performs the existing executable-file check only for matching entries.

PATH order and resolveCommandCandidates remain unchanged. If a directory cannot be listed, lookup falls back to upstream's direct candidate probes.

Why

The first client configuration probes up to 22 editor commands. Upstream performs a filesystem probe for every PATH × PATHEXT candidate, so editor discovery can consume seconds on a long Windows PATH and approaches its five-second timeout.

This narrows the PR to reducing first-start/editor-discovery time without changing desktop PATH hydration or packaging behavior.

Measured Impact

Targeted Windows command-discovery benchmark on 71 PATH entries and 12 PATHEXT extensions; medians from five alternating runs:

WorkloadUpstreamThis PRSaved
5 provider commands199 ms49 ms150 ms
22 editor commands1,964 ms425 ms1,539 ms

This is a resolver benchmark rather than an end-to-end launch measurement. The exact gain depends on PATH and PATHEXT size.

Scope

No PowerShell environment probing, provider lifecycle, installer, packaging, or WSL behavior changes are included.

Verification

  • vp fmt --check packages/shared/src/shell.ts packages/shared/src/shell.test.ts
  • vp lint packages/shared/src/shell.ts packages/shared/src/shell.test.ts --report-unused-disable-directives
  • vp run --filter @t3tools/shared typecheck
  • vp test run packages/shared/src/shell.test.ts — 30 tests passed

model: gpt-5.6-sol
harness: Codex in T3 Code

Note

Reduce first-start editor discovery time on Windows with case-insensitive PATH resolution

  • On Windows, resolveCommandPathForPlatform in shell.ts now lists each PATH directory and builds a case-insensitive filename map to match command candidates, avoiding failed probes caused by case mismatches.
  • Falls back to the existing direct-probe behavior when a PATH directory cannot be listed.
  • Adds a test in shell.test.ts that creates a mixed-case executable in a temp directory and verifies case-insensitive resolution on win32 with PATHEXT set.

Macroscope summarized ae911eb.


Note

Medium Risk
Changes Windows command resolution used by editor and CLI discovery; behavior is narrowed with a listing fallback, but incorrect matching could miss or mis-resolve executables on win32.

Overview
On Windows, resolveCommandPathForPlatform no longer probes every PATHEXT candidate in each PATH directory. For each PATH entry it lists the directory (when listing succeeds), maps filenames case-insensitively, and only runs the existing executable check for names that actually match the command candidates.

If a PATH directory cannot be listed, resolution falls back to the prior behavior of probing candidates directly. PATH order and candidate generation are unchanged.

A new test creates a mixed-case .eXe under a temp PATH entry and asserts resolveCommandPath resolves provider-tool on win32.

Reviewed by Cursor Bugbot for commit ae911eb. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4eaab204-5559-42cc-a1b9-f24199c759a8

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

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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 11, 2026
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
@@ -177,17 +177,20 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {

const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
maintenanceCapabilities,
maintenanceCapabilities: maintenanceCapabilities.get,

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.

🟠 HighDrivers/ClaudeDriver.ts:180

maintenanceCapabilities.get() returns the npm updater immediately after ClaudeDriver.create(), even when claude on PATH actually resolves to a native or Homebrew install. Before the deferred maintenanceCapabilities.refresh completes, ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance can therefore return the wrong update action, so an update requested during startup runs npm instead of claude update/Homebrew. makeProviderMaintenanceCapabilitiesSource initializes its current value via resolver.resolve(options) before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare claude command. Consider initializing current with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/ClaudeDriver.ts around line 180:
`maintenanceCapabilities.get()` returns the npm updater immediately after `ClaudeDriver.create()`, even when `claude` on PATH actually resolves to a native or Homebrew install. Before the deferred `maintenanceCapabilities.refresh` completes, `ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance` can therefore return the wrong update action, so an update requested during startup runs `npm` instead of `claude update`/Homebrew. `makeProviderMaintenanceCapabilitiesSource` initializes its `current` value via `resolver.resolve(options)` before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare `claude` command. Consider initializing `current` with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

Comment threadscripts/build-desktop-artifact.ts
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
Comment threadpackages/shared/src/shell.ts Outdated
@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify long phasesperf(windows): reduce startup work and clarify installer phasesAug 11, 2026
Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found.

You can customize Macroscope's approvability policy. Learn more.

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fef06d741f8c710f0957139af036297f3f3aad90. Configure here.

Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@simon-curtis
simon-curtis marked this pull request as draft August 13, 2026 11:14

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

Read the Windows shell half against today's main. CONFLICTING.

Desktop: drops the -NoProfile PATH probe entirely and keeps only the profile probe, merging process PATH + profile PATH + knownWindowsCliDirs. That is the ~2s startup win (each PowerShell spawn was ~2s). It also deletes the no-profile source #6356 is trying to make read Machine+User, so a CLI installed after launch still will not show up until the next process start unless the profile sees it.

Shared: directory listing + case-insensitive name map instead of probing every PATHEXT candidate, with a fallback when readDirectory fails (ACL / unreadable share). That is the right Windows lookup. Returning [command] when the input already has a PATHEXT suffix is also right — the old code generated three casings of the same extension.

Do not land this and #6356/#4896/#6301 independently; they all rewrite installWindowsEnvironment.

@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify installer phasesperf(windows): speed up shell command discoveryAug 17, 2026
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from 324294b to 96a86d6CompareAugust 17, 2026 16:42
@simon-curtissimon-curtis changed the title perf(windows): speed up shell command discoveryperf(windows): reduce first-start editor discovery timeAug 17, 2026
@simon-curtis

Copy link
Copy Markdown
Author

Scope update: I have reframed this PR around reducing first-start/editor-discovery time on Windows.

After comparing the original branch with current main and the overlapping Windows environment work, the earlier ~2-second startup result was attributable to removing a PowerShell PATH probe. That change affects PATH refresh semantics and conflicts with the other installWindowsEnvironment work, so it no longer belongs in this PR. The installer, packaging, provider-lifecycle, desktop environment, and WSL changes have also been removed.

What remains is the independently useful shared lookup optimization: list each Windows PATH directory once, match PATHEXT candidates case-insensitively, preserve PATH order, and fall back to the existing direct probes when listing fails. I also restored resolveCommandCandidates exactly to main, so this PR no longer changes candidate casing behavior.

A targeted five-run benchmark on this machine's 71-entry PATH and 12 PATHEXT values measured provider discovery at 199 ms → 49 ms and first editor discovery at 1,964 ms → 425 ms. This is a resolver benchmark, not an end-to-end startup claim.

@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 17, 2026
Comment threadpackages/shared/src/shell.ts
@simon-curtis
simon-curtis marked this pull request as ready for review August 17, 2026 17:25
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 7 times, most recently from 99f0376 to d5a0e7aCompareAugust 26, 2026 08:27
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from cfb0d3e to 9ecfbc4CompareSeptember 1, 2026 08:32
Simon Curtisand others added 2 commits September 2, 2026 11:03
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This note is part of an automated cleanup pass.

Preserving these details from items reviewed in the cleanup pass.

Carryover from #6221 at 59849f94e2: retain the shell.test.ts listing-failure cases. PermissionDenied, Busy, and Unknown must fall back to direct executable probes, while NotFound and BadResource can skip the directory. Keep mixed-case filenames and PATH order intact. The cross-command directory reuse may be useful if Windows measurements still show repeated listings, but the two PRs' benchmarks use different machines and cannot establish which is faster.

Carry over the slow-discovery evidence from #5050 at 61278bee47. On the reported Windows host with 46 PATH entries and 12 PATHEXT entries, getConfig timed out near 5,000 ms on every call. Concurrent discovery finished in 4,787 ms in that report. Its focused test interrupts one caller during discovery, then requires a later caller to obtain editors. Check cold discovery and caller interruption while retaining bounded expiry and current Linux/WSL file-manager checks. These are reported measurements and a test case to retain, not results rerun here.

Keep the trace evidence from #4778 at dc7f1727a6 in the Windows resolver review. Its author reported 28,577 shell.isExecutableFile spans from 22 command lookups in 13.7 seconds, plus trace files rotating at 10 MB about every 40 seconds. The distinct code change makes isExecutableFile untraced. Main still traces that helper, although its command cache and VCS limits are now bounded. Check first-scan and miss-heavy trace cost with this PR. Do not copy the older unbounded cache or treat the reported timings as rerun results.

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

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simon-curtis@t3dotgg@CDVolvik
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

perf(windows): reduce first-start editor discovery time - #6124

Open
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup
Open

perf(windows): reduce first-start editor discovery time#6124
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup

Conversation

@simon-curtis

@simon-curtissimon-curtis commented Aug 11, 2026

Copy link
Copy Markdown

What Changed

On Windows, command discovery now lists each PATH directory once, matches PATHEXT candidates case-insensitively, and performs the existing executable-file check only for matching entries.

PATH order and resolveCommandCandidates remain unchanged. If a directory cannot be listed, lookup falls back to upstream's direct candidate probes.

Why

The first client configuration probes up to 22 editor commands. Upstream performs a filesystem probe for every PATH × PATHEXT candidate, so editor discovery can consume seconds on a long Windows PATH and approaches its five-second timeout.

This narrows the PR to reducing first-start/editor-discovery time without changing desktop PATH hydration or packaging behavior.

Measured Impact

Targeted Windows command-discovery benchmark on 71 PATH entries and 12 PATHEXT extensions; medians from five alternating runs:

WorkloadUpstreamThis PRSaved
5 provider commands199 ms49 ms150 ms
22 editor commands1,964 ms425 ms1,539 ms

This is a resolver benchmark rather than an end-to-end launch measurement. The exact gain depends on PATH and PATHEXT size.

Scope

No PowerShell environment probing, provider lifecycle, installer, packaging, or WSL behavior changes are included.

Verification

  • vp fmt --check packages/shared/src/shell.ts packages/shared/src/shell.test.ts
  • vp lint packages/shared/src/shell.ts packages/shared/src/shell.test.ts --report-unused-disable-directives
  • vp run --filter @t3tools/shared typecheck
  • vp test run packages/shared/src/shell.test.ts — 30 tests passed

model: gpt-5.6-sol
harness: Codex in T3 Code

Note

Reduce first-start editor discovery time on Windows with case-insensitive PATH resolution

  • On Windows, resolveCommandPathForPlatform in shell.ts now lists each PATH directory and builds a case-insensitive filename map to match command candidates, avoiding failed probes caused by case mismatches.
  • Falls back to the existing direct-probe behavior when a PATH directory cannot be listed.
  • Adds a test in shell.test.ts that creates a mixed-case executable in a temp directory and verifies case-insensitive resolution on win32 with PATHEXT set.

Macroscope summarized ae911eb.


Note

Medium Risk
Changes Windows command resolution used by editor and CLI discovery; behavior is narrowed with a listing fallback, but incorrect matching could miss or mis-resolve executables on win32.

Overview
On Windows, resolveCommandPathForPlatform no longer probes every PATHEXT candidate in each PATH directory. For each PATH entry it lists the directory (when listing succeeds), maps filenames case-insensitively, and only runs the existing executable check for names that actually match the command candidates.

If a PATH directory cannot be listed, resolution falls back to the prior behavior of probing candidates directly. PATH order and candidate generation are unchanged.

A new test creates a mixed-case .eXe under a temp PATH entry and asserts resolveCommandPath resolves provider-tool on win32.

Reviewed by Cursor Bugbot for commit ae911eb. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4eaab204-5559-42cc-a1b9-f24199c759a8

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

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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 11, 2026
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
@@ -177,17 +177,20 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {

const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
maintenanceCapabilities,
maintenanceCapabilities: maintenanceCapabilities.get,

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.

🟠 HighDrivers/ClaudeDriver.ts:180

maintenanceCapabilities.get() returns the npm updater immediately after ClaudeDriver.create(), even when claude on PATH actually resolves to a native or Homebrew install. Before the deferred maintenanceCapabilities.refresh completes, ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance can therefore return the wrong update action, so an update requested during startup runs npm instead of claude update/Homebrew. makeProviderMaintenanceCapabilitiesSource initializes its current value via resolver.resolve(options) before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare claude command. Consider initializing current with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/ClaudeDriver.ts around line 180:
`maintenanceCapabilities.get()` returns the npm updater immediately after `ClaudeDriver.create()`, even when `claude` on PATH actually resolves to a native or Homebrew install. Before the deferred `maintenanceCapabilities.refresh` completes, `ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance` can therefore return the wrong update action, so an update requested during startup runs `npm` instead of `claude update`/Homebrew. `makeProviderMaintenanceCapabilitiesSource` initializes its `current` value via `resolver.resolve(options)` before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare `claude` command. Consider initializing `current` with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

Comment threadscripts/build-desktop-artifact.ts
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
Comment threadpackages/shared/src/shell.ts Outdated
@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify long phasesperf(windows): reduce startup work and clarify installer phasesAug 11, 2026
Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found.

You can customize Macroscope's approvability policy. Learn more.

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fef06d741f8c710f0957139af036297f3f3aad90. Configure here.

Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@simon-curtis
simon-curtis marked this pull request as draft August 13, 2026 11:14

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

Read the Windows shell half against today's main. CONFLICTING.

Desktop: drops the -NoProfile PATH probe entirely and keeps only the profile probe, merging process PATH + profile PATH + knownWindowsCliDirs. That is the ~2s startup win (each PowerShell spawn was ~2s). It also deletes the no-profile source #6356 is trying to make read Machine+User, so a CLI installed after launch still will not show up until the next process start unless the profile sees it.

Shared: directory listing + case-insensitive name map instead of probing every PATHEXT candidate, with a fallback when readDirectory fails (ACL / unreadable share). That is the right Windows lookup. Returning [command] when the input already has a PATHEXT suffix is also right — the old code generated three casings of the same extension.

Do not land this and #6356/#4896/#6301 independently; they all rewrite installWindowsEnvironment.

@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify installer phasesperf(windows): speed up shell command discoveryAug 17, 2026
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from 324294b to 96a86d6CompareAugust 17, 2026 16:42
@simon-curtissimon-curtis changed the title perf(windows): speed up shell command discoveryperf(windows): reduce first-start editor discovery timeAug 17, 2026
@simon-curtis

Copy link
Copy Markdown
Author

Scope update: I have reframed this PR around reducing first-start/editor-discovery time on Windows.

After comparing the original branch with current main and the overlapping Windows environment work, the earlier ~2-second startup result was attributable to removing a PowerShell PATH probe. That change affects PATH refresh semantics and conflicts with the other installWindowsEnvironment work, so it no longer belongs in this PR. The installer, packaging, provider-lifecycle, desktop environment, and WSL changes have also been removed.

What remains is the independently useful shared lookup optimization: list each Windows PATH directory once, match PATHEXT candidates case-insensitively, preserve PATH order, and fall back to the existing direct probes when listing fails. I also restored resolveCommandCandidates exactly to main, so this PR no longer changes candidate casing behavior.

A targeted five-run benchmark on this machine's 71-entry PATH and 12 PATHEXT values measured provider discovery at 199 ms → 49 ms and first editor discovery at 1,964 ms → 425 ms. This is a resolver benchmark, not an end-to-end startup claim.

@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 17, 2026
Comment threadpackages/shared/src/shell.ts
@simon-curtis
simon-curtis marked this pull request as ready for review August 17, 2026 17:25
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 7 times, most recently from 99f0376 to d5a0e7aCompareAugust 26, 2026 08:27
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from cfb0d3e to 9ecfbc4CompareSeptember 1, 2026 08:32
Simon Curtisand others added 2 commits September 2, 2026 11:03
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This note is part of an automated cleanup pass.

Preserving these details from items reviewed in the cleanup pass.

Carryover from #6221 at 59849f94e2: retain the shell.test.ts listing-failure cases. PermissionDenied, Busy, and Unknown must fall back to direct executable probes, while NotFound and BadResource can skip the directory. Keep mixed-case filenames and PATH order intact. The cross-command directory reuse may be useful if Windows measurements still show repeated listings, but the two PRs' benchmarks use different machines and cannot establish which is faster.

Carry over the slow-discovery evidence from #5050 at 61278bee47. On the reported Windows host with 46 PATH entries and 12 PATHEXT entries, getConfig timed out near 5,000 ms on every call. Concurrent discovery finished in 4,787 ms in that report. Its focused test interrupts one caller during discovery, then requires a later caller to obtain editors. Check cold discovery and caller interruption while retaining bounded expiry and current Linux/WSL file-manager checks. These are reported measurements and a test case to retain, not results rerun here.

Keep the trace evidence from #4778 at dc7f1727a6 in the Windows resolver review. Its author reported 28,577 shell.isExecutableFile spans from 22 command lookups in 13.7 seconds, plus trace files rotating at 10 MB about every 40 seconds. The distinct code change makes isExecutableFile untraced. Main still traces that helper, although its command cache and VCS limits are now bounded. Check first-scan and miss-heavy trace cost with this PR. Do not copy the older unbounded cache or treat the reported timings as rerun results.

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

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simon-curtis@t3dotgg@CDVolvik
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(windows): reduce first-start editor discovery time - #6124

Open
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup
Open

perf(windows): reduce first-start editor discovery time#6124
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup

Conversation

@simon-curtis

@simon-curtissimon-curtis commented Aug 11, 2026

Copy link
Copy Markdown

What Changed

On Windows, command discovery now lists each PATH directory once, matches PATHEXT candidates case-insensitively, and performs the existing executable-file check only for matching entries.

PATH order and resolveCommandCandidates remain unchanged. If a directory cannot be listed, lookup falls back to upstream's direct candidate probes.

Why

The first client configuration probes up to 22 editor commands. Upstream performs a filesystem probe for every PATH × PATHEXT candidate, so editor discovery can consume seconds on a long Windows PATH and approaches its five-second timeout.

This narrows the PR to reducing first-start/editor-discovery time without changing desktop PATH hydration or packaging behavior.

Measured Impact

Targeted Windows command-discovery benchmark on 71 PATH entries and 12 PATHEXT extensions; medians from five alternating runs:

WorkloadUpstreamThis PRSaved
5 provider commands199 ms49 ms150 ms
22 editor commands1,964 ms425 ms1,539 ms

This is a resolver benchmark rather than an end-to-end launch measurement. The exact gain depends on PATH and PATHEXT size.

Scope

No PowerShell environment probing, provider lifecycle, installer, packaging, or WSL behavior changes are included.

Verification

  • vp fmt --check packages/shared/src/shell.ts packages/shared/src/shell.test.ts
  • vp lint packages/shared/src/shell.ts packages/shared/src/shell.test.ts --report-unused-disable-directives
  • vp run --filter @t3tools/shared typecheck
  • vp test run packages/shared/src/shell.test.ts — 30 tests passed

model: gpt-5.6-sol
harness: Codex in T3 Code

Note

Reduce first-start editor discovery time on Windows with case-insensitive PATH resolution

  • On Windows, resolveCommandPathForPlatform in shell.ts now lists each PATH directory and builds a case-insensitive filename map to match command candidates, avoiding failed probes caused by case mismatches.
  • Falls back to the existing direct-probe behavior when a PATH directory cannot be listed.
  • Adds a test in shell.test.ts that creates a mixed-case executable in a temp directory and verifies case-insensitive resolution on win32 with PATHEXT set.

Macroscope summarized ae911eb.


Note

Medium Risk
Changes Windows command resolution used by editor and CLI discovery; behavior is narrowed with a listing fallback, but incorrect matching could miss or mis-resolve executables on win32.

Overview
On Windows, resolveCommandPathForPlatform no longer probes every PATHEXT candidate in each PATH directory. For each PATH entry it lists the directory (when listing succeeds), maps filenames case-insensitively, and only runs the existing executable check for names that actually match the command candidates.

If a PATH directory cannot be listed, resolution falls back to the prior behavior of probing candidates directly. PATH order and candidate generation are unchanged.

A new test creates a mixed-case .eXe under a temp PATH entry and asserts resolveCommandPath resolves provider-tool on win32.

Reviewed by Cursor Bugbot for commit ae911eb. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4eaab204-5559-42cc-a1b9-f24199c759a8

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

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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 11, 2026
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
@@ -177,17 +177,20 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {

const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
maintenanceCapabilities,
maintenanceCapabilities: maintenanceCapabilities.get,

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.

🟠 HighDrivers/ClaudeDriver.ts:180

maintenanceCapabilities.get() returns the npm updater immediately after ClaudeDriver.create(), even when claude on PATH actually resolves to a native or Homebrew install. Before the deferred maintenanceCapabilities.refresh completes, ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance can therefore return the wrong update action, so an update requested during startup runs npm instead of claude update/Homebrew. makeProviderMaintenanceCapabilitiesSource initializes its current value via resolver.resolve(options) before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare claude command. Consider initializing current with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/ClaudeDriver.ts around line 180:
`maintenanceCapabilities.get()` returns the npm updater immediately after `ClaudeDriver.create()`, even when `claude` on PATH actually resolves to a native or Homebrew install. Before the deferred `maintenanceCapabilities.refresh` completes, `ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance` can therefore return the wrong update action, so an update requested during startup runs `npm` instead of `claude update`/Homebrew. `makeProviderMaintenanceCapabilitiesSource` initializes its `current` value via `resolver.resolve(options)` before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare `claude` command. Consider initializing `current` with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

Comment threadscripts/build-desktop-artifact.ts
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
Comment threadpackages/shared/src/shell.ts Outdated
@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify long phasesperf(windows): reduce startup work and clarify installer phasesAug 11, 2026
Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found.

You can customize Macroscope's approvability policy. Learn more.

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fef06d741f8c710f0957139af036297f3f3aad90. Configure here.

Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@simon-curtis
simon-curtis marked this pull request as draft August 13, 2026 11:14

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

Read the Windows shell half against today's main. CONFLICTING.

Desktop: drops the -NoProfile PATH probe entirely and keeps only the profile probe, merging process PATH + profile PATH + knownWindowsCliDirs. That is the ~2s startup win (each PowerShell spawn was ~2s). It also deletes the no-profile source #6356 is trying to make read Machine+User, so a CLI installed after launch still will not show up until the next process start unless the profile sees it.

Shared: directory listing + case-insensitive name map instead of probing every PATHEXT candidate, with a fallback when readDirectory fails (ACL / unreadable share). That is the right Windows lookup. Returning [command] when the input already has a PATHEXT suffix is also right — the old code generated three casings of the same extension.

Do not land this and #6356/#4896/#6301 independently; they all rewrite installWindowsEnvironment.

@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify installer phasesperf(windows): speed up shell command discoveryAug 17, 2026
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from 324294b to 96a86d6CompareAugust 17, 2026 16:42
@simon-curtissimon-curtis changed the title perf(windows): speed up shell command discoveryperf(windows): reduce first-start editor discovery timeAug 17, 2026
@simon-curtis

Copy link
Copy Markdown
Author

Scope update: I have reframed this PR around reducing first-start/editor-discovery time on Windows.

After comparing the original branch with current main and the overlapping Windows environment work, the earlier ~2-second startup result was attributable to removing a PowerShell PATH probe. That change affects PATH refresh semantics and conflicts with the other installWindowsEnvironment work, so it no longer belongs in this PR. The installer, packaging, provider-lifecycle, desktop environment, and WSL changes have also been removed.

What remains is the independently useful shared lookup optimization: list each Windows PATH directory once, match PATHEXT candidates case-insensitively, preserve PATH order, and fall back to the existing direct probes when listing fails. I also restored resolveCommandCandidates exactly to main, so this PR no longer changes candidate casing behavior.

A targeted five-run benchmark on this machine's 71-entry PATH and 12 PATHEXT values measured provider discovery at 199 ms → 49 ms and first editor discovery at 1,964 ms → 425 ms. This is a resolver benchmark, not an end-to-end startup claim.

@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 17, 2026
Comment threadpackages/shared/src/shell.ts
@simon-curtis
simon-curtis marked this pull request as ready for review August 17, 2026 17:25
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 7 times, most recently from 99f0376 to d5a0e7aCompareAugust 26, 2026 08:27
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from cfb0d3e to 9ecfbc4CompareSeptember 1, 2026 08:32
Simon Curtisand others added 2 commits September 2, 2026 11:03
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This note is part of an automated cleanup pass.

Preserving these details from items reviewed in the cleanup pass.

Carryover from #6221 at 59849f94e2: retain the shell.test.ts listing-failure cases. PermissionDenied, Busy, and Unknown must fall back to direct executable probes, while NotFound and BadResource can skip the directory. Keep mixed-case filenames and PATH order intact. The cross-command directory reuse may be useful if Windows measurements still show repeated listings, but the two PRs' benchmarks use different machines and cannot establish which is faster.

Carry over the slow-discovery evidence from #5050 at 61278bee47. On the reported Windows host with 46 PATH entries and 12 PATHEXT entries, getConfig timed out near 5,000 ms on every call. Concurrent discovery finished in 4,787 ms in that report. Its focused test interrupts one caller during discovery, then requires a later caller to obtain editors. Check cold discovery and caller interruption while retaining bounded expiry and current Linux/WSL file-manager checks. These are reported measurements and a test case to retain, not results rerun here.

Keep the trace evidence from #4778 at dc7f1727a6 in the Windows resolver review. Its author reported 28,577 shell.isExecutableFile spans from 22 command lookups in 13.7 seconds, plus trace files rotating at 10 MB about every 40 seconds. The distinct code change makes isExecutableFile untraced. Main still traces that helper, although its command cache and VCS limits are now bounded. Check first-scan and miss-heavy trace cost with this PR. Do not copy the older unbounded cache or treat the reported timings as rerun results.

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

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simon-curtis@t3dotgg@CDVolvik
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(windows): reduce first-start editor discovery time - #6124

Open
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup
Open

perf(windows): reduce first-start editor discovery time#6124
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup

Conversation

@simon-curtis

@simon-curtissimon-curtis commented Aug 11, 2026

Copy link
Copy Markdown

What Changed

On Windows, command discovery now lists each PATH directory once, matches PATHEXT candidates case-insensitively, and performs the existing executable-file check only for matching entries.

PATH order and resolveCommandCandidates remain unchanged. If a directory cannot be listed, lookup falls back to upstream's direct candidate probes.

Why

The first client configuration probes up to 22 editor commands. Upstream performs a filesystem probe for every PATH × PATHEXT candidate, so editor discovery can consume seconds on a long Windows PATH and approaches its five-second timeout.

This narrows the PR to reducing first-start/editor-discovery time without changing desktop PATH hydration or packaging behavior.

Measured Impact

Targeted Windows command-discovery benchmark on 71 PATH entries and 12 PATHEXT extensions; medians from five alternating runs:

WorkloadUpstreamThis PRSaved
5 provider commands199 ms49 ms150 ms
22 editor commands1,964 ms425 ms1,539 ms

This is a resolver benchmark rather than an end-to-end launch measurement. The exact gain depends on PATH and PATHEXT size.

Scope

No PowerShell environment probing, provider lifecycle, installer, packaging, or WSL behavior changes are included.

Verification

  • vp fmt --check packages/shared/src/shell.ts packages/shared/src/shell.test.ts
  • vp lint packages/shared/src/shell.ts packages/shared/src/shell.test.ts --report-unused-disable-directives
  • vp run --filter @t3tools/shared typecheck
  • vp test run packages/shared/src/shell.test.ts — 30 tests passed

model: gpt-5.6-sol
harness: Codex in T3 Code

Note

Reduce first-start editor discovery time on Windows with case-insensitive PATH resolution

  • On Windows, resolveCommandPathForPlatform in shell.ts now lists each PATH directory and builds a case-insensitive filename map to match command candidates, avoiding failed probes caused by case mismatches.
  • Falls back to the existing direct-probe behavior when a PATH directory cannot be listed.
  • Adds a test in shell.test.ts that creates a mixed-case executable in a temp directory and verifies case-insensitive resolution on win32 with PATHEXT set.

Macroscope summarized ae911eb.


Note

Medium Risk
Changes Windows command resolution used by editor and CLI discovery; behavior is narrowed with a listing fallback, but incorrect matching could miss or mis-resolve executables on win32.

Overview
On Windows, resolveCommandPathForPlatform no longer probes every PATHEXT candidate in each PATH directory. For each PATH entry it lists the directory (when listing succeeds), maps filenames case-insensitively, and only runs the existing executable check for names that actually match the command candidates.

If a PATH directory cannot be listed, resolution falls back to the prior behavior of probing candidates directly. PATH order and candidate generation are unchanged.

A new test creates a mixed-case .eXe under a temp PATH entry and asserts resolveCommandPath resolves provider-tool on win32.

Reviewed by Cursor Bugbot for commit ae911eb. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4eaab204-5559-42cc-a1b9-f24199c759a8

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

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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 11, 2026
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
@@ -177,17 +177,20 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {

const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
maintenanceCapabilities,
maintenanceCapabilities: maintenanceCapabilities.get,

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.

🟠 HighDrivers/ClaudeDriver.ts:180

maintenanceCapabilities.get() returns the npm updater immediately after ClaudeDriver.create(), even when claude on PATH actually resolves to a native or Homebrew install. Before the deferred maintenanceCapabilities.refresh completes, ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance can therefore return the wrong update action, so an update requested during startup runs npm instead of claude update/Homebrew. makeProviderMaintenanceCapabilitiesSource initializes its current value via resolver.resolve(options) before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare claude command. Consider initializing current with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/ClaudeDriver.ts around line 180:
`maintenanceCapabilities.get()` returns the npm updater immediately after `ClaudeDriver.create()`, even when `claude` on PATH actually resolves to a native or Homebrew install. Before the deferred `maintenanceCapabilities.refresh` completes, `ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance` can therefore return the wrong update action, so an update requested during startup runs `npm` instead of `claude update`/Homebrew. `makeProviderMaintenanceCapabilitiesSource` initializes its `current` value via `resolver.resolve(options)` before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare `claude` command. Consider initializing `current` with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

Comment threadscripts/build-desktop-artifact.ts
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
Comment threadpackages/shared/src/shell.ts Outdated
@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify long phasesperf(windows): reduce startup work and clarify installer phasesAug 11, 2026
Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found.

You can customize Macroscope's approvability policy. Learn more.

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fef06d741f8c710f0957139af036297f3f3aad90. Configure here.

Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@simon-curtis
simon-curtis marked this pull request as draft August 13, 2026 11:14

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

Read the Windows shell half against today's main. CONFLICTING.

Desktop: drops the -NoProfile PATH probe entirely and keeps only the profile probe, merging process PATH + profile PATH + knownWindowsCliDirs. That is the ~2s startup win (each PowerShell spawn was ~2s). It also deletes the no-profile source #6356 is trying to make read Machine+User, so a CLI installed after launch still will not show up until the next process start unless the profile sees it.

Shared: directory listing + case-insensitive name map instead of probing every PATHEXT candidate, with a fallback when readDirectory fails (ACL / unreadable share). That is the right Windows lookup. Returning [command] when the input already has a PATHEXT suffix is also right — the old code generated three casings of the same extension.

Do not land this and #6356/#4896/#6301 independently; they all rewrite installWindowsEnvironment.

@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify installer phasesperf(windows): speed up shell command discoveryAug 17, 2026
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from 324294b to 96a86d6CompareAugust 17, 2026 16:42
@simon-curtissimon-curtis changed the title perf(windows): speed up shell command discoveryperf(windows): reduce first-start editor discovery timeAug 17, 2026
@simon-curtis

Copy link
Copy Markdown
Author

Scope update: I have reframed this PR around reducing first-start/editor-discovery time on Windows.

After comparing the original branch with current main and the overlapping Windows environment work, the earlier ~2-second startup result was attributable to removing a PowerShell PATH probe. That change affects PATH refresh semantics and conflicts with the other installWindowsEnvironment work, so it no longer belongs in this PR. The installer, packaging, provider-lifecycle, desktop environment, and WSL changes have also been removed.

What remains is the independently useful shared lookup optimization: list each Windows PATH directory once, match PATHEXT candidates case-insensitively, preserve PATH order, and fall back to the existing direct probes when listing fails. I also restored resolveCommandCandidates exactly to main, so this PR no longer changes candidate casing behavior.

A targeted five-run benchmark on this machine's 71-entry PATH and 12 PATHEXT values measured provider discovery at 199 ms → 49 ms and first editor discovery at 1,964 ms → 425 ms. This is a resolver benchmark, not an end-to-end startup claim.

@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 17, 2026
Comment threadpackages/shared/src/shell.ts
@simon-curtis
simon-curtis marked this pull request as ready for review August 17, 2026 17:25
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 7 times, most recently from 99f0376 to d5a0e7aCompareAugust 26, 2026 08:27
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from cfb0d3e to 9ecfbc4CompareSeptember 1, 2026 08:32
Simon Curtisand others added 2 commits September 2, 2026 11:03
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This note is part of an automated cleanup pass.

Preserving these details from items reviewed in the cleanup pass.

Carryover from #6221 at 59849f94e2: retain the shell.test.ts listing-failure cases. PermissionDenied, Busy, and Unknown must fall back to direct executable probes, while NotFound and BadResource can skip the directory. Keep mixed-case filenames and PATH order intact. The cross-command directory reuse may be useful if Windows measurements still show repeated listings, but the two PRs' benchmarks use different machines and cannot establish which is faster.

Carry over the slow-discovery evidence from #5050 at 61278bee47. On the reported Windows host with 46 PATH entries and 12 PATHEXT entries, getConfig timed out near 5,000 ms on every call. Concurrent discovery finished in 4,787 ms in that report. Its focused test interrupts one caller during discovery, then requires a later caller to obtain editors. Check cold discovery and caller interruption while retaining bounded expiry and current Linux/WSL file-manager checks. These are reported measurements and a test case to retain, not results rerun here.

Keep the trace evidence from #4778 at dc7f1727a6 in the Windows resolver review. Its author reported 28,577 shell.isExecutableFile spans from 22 command lookups in 13.7 seconds, plus trace files rotating at 10 MB about every 40 seconds. The distinct code change makes isExecutableFile untraced. Main still traces that helper, although its command cache and VCS limits are now bounded. Check first-scan and miss-heavy trace cost with this PR. Do not copy the older unbounded cache or treat the reported timings as rerun results.

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

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simon-curtis@t3dotgg@CDVolvik
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

perf(windows): reduce first-start editor discovery time - #6124

Open
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup
Open

perf(windows): reduce first-start editor discovery time#6124
simon-curtis wants to merge 2 commits into
pingdotgg:mainfrom
simon-curtis:perf/windows-startup

Conversation

@simon-curtis

@simon-curtissimon-curtis commented Aug 11, 2026

Copy link
Copy Markdown

What Changed

On Windows, command discovery now lists each PATH directory once, matches PATHEXT candidates case-insensitively, and performs the existing executable-file check only for matching entries.

PATH order and resolveCommandCandidates remain unchanged. If a directory cannot be listed, lookup falls back to upstream's direct candidate probes.

Why

The first client configuration probes up to 22 editor commands. Upstream performs a filesystem probe for every PATH × PATHEXT candidate, so editor discovery can consume seconds on a long Windows PATH and approaches its five-second timeout.

This narrows the PR to reducing first-start/editor-discovery time without changing desktop PATH hydration or packaging behavior.

Measured Impact

Targeted Windows command-discovery benchmark on 71 PATH entries and 12 PATHEXT extensions; medians from five alternating runs:

WorkloadUpstreamThis PRSaved
5 provider commands199 ms49 ms150 ms
22 editor commands1,964 ms425 ms1,539 ms

This is a resolver benchmark rather than an end-to-end launch measurement. The exact gain depends on PATH and PATHEXT size.

Scope

No PowerShell environment probing, provider lifecycle, installer, packaging, or WSL behavior changes are included.

Verification

  • vp fmt --check packages/shared/src/shell.ts packages/shared/src/shell.test.ts
  • vp lint packages/shared/src/shell.ts packages/shared/src/shell.test.ts --report-unused-disable-directives
  • vp run --filter @t3tools/shared typecheck
  • vp test run packages/shared/src/shell.test.ts — 30 tests passed

model: gpt-5.6-sol
harness: Codex in T3 Code

Note

Reduce first-start editor discovery time on Windows with case-insensitive PATH resolution

  • On Windows, resolveCommandPathForPlatform in shell.ts now lists each PATH directory and builds a case-insensitive filename map to match command candidates, avoiding failed probes caused by case mismatches.
  • Falls back to the existing direct-probe behavior when a PATH directory cannot be listed.
  • Adds a test in shell.test.ts that creates a mixed-case executable in a temp directory and verifies case-insensitive resolution on win32 with PATHEXT set.

Macroscope summarized ae911eb.


Note

Medium Risk
Changes Windows command resolution used by editor and CLI discovery; behavior is narrowed with a listing fallback, but incorrect matching could miss or mis-resolve executables on win32.

Overview
On Windows, resolveCommandPathForPlatform no longer probes every PATHEXT candidate in each PATH directory. For each PATH entry it lists the directory (when listing succeeds), maps filenames case-insensitively, and only runs the existing executable check for names that actually match the command candidates.

If a PATH directory cannot be listed, resolution falls back to the prior behavior of probing candidates directly. PATH order and candidate generation are unchanged.

A new test creates a mixed-case .eXe under a temp PATH entry and asserts resolveCommandPath resolves provider-tool on win32.

Reviewed by Cursor Bugbot for commit ae911eb. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4eaab204-5559-42cc-a1b9-f24199c759a8

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

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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 11, 2026
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
@@ -177,17 +177,20 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {

const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
maintenanceCapabilities,
maintenanceCapabilities: maintenanceCapabilities.get,

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.

🟠 HighDrivers/ClaudeDriver.ts:180

maintenanceCapabilities.get() returns the npm updater immediately after ClaudeDriver.create(), even when claude on PATH actually resolves to a native or Homebrew install. Before the deferred maintenanceCapabilities.refresh completes, ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance can therefore return the wrong update action, so an update requested during startup runs npm instead of claude update/Homebrew. makeProviderMaintenanceCapabilitiesSource initializes its current value via resolver.resolve(options) before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare claude command. Consider initializing current with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/ClaudeDriver.ts around line 180:
`maintenanceCapabilities.get()` returns the npm updater immediately after `ClaudeDriver.create()`, even when `claude` on PATH actually resolves to a native or Homebrew install. Before the deferred `maintenanceCapabilities.refresh` completes, `ProviderRegistry.getProviderMaintenanceCapabilitiesForInstance` can therefore return the wrong update action, so an update requested during startup runs `npm` instead of `claude update`/Homebrew. `makeProviderMaintenanceCapabilitiesSource` initializes its `current` value via `resolver.resolve(options)` before command-path/realpath discovery, and that synchronous resolve falls back to the npm updater for a bare `claude` command. Consider initializing `current` with a non-actionable value (or gating maintenance access) until the deferred resolution completes.

Comment threadscripts/build-desktop-artifact.ts
Comment threadapps/desktop/src/shell/DesktopShellEnvironment.ts Outdated
Comment threadpackages/shared/src/shell.ts Outdated
@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify long phasesperf(windows): reduce startup work and clarify installer phasesAug 11, 2026
Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found.

You can customize Macroscope's approvability policy. Learn more.

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fef06d741f8c710f0957139af036297f3f3aad90. Configure here.

Comment threadapps/server/src/provider/providerMaintenance.ts Outdated
@simon-curtis
simon-curtis marked this pull request as draft August 13, 2026 11:14

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

Read the Windows shell half against today's main. CONFLICTING.

Desktop: drops the -NoProfile PATH probe entirely and keeps only the profile probe, merging process PATH + profile PATH + knownWindowsCliDirs. That is the ~2s startup win (each PowerShell spawn was ~2s). It also deletes the no-profile source #6356 is trying to make read Machine+User, so a CLI installed after launch still will not show up until the next process start unless the profile sees it.

Shared: directory listing + case-insensitive name map instead of probing every PATHEXT candidate, with a fallback when readDirectory fails (ACL / unreadable share). That is the right Windows lookup. Returning [command] when the input already has a PATHEXT suffix is also right — the old code generated three casings of the same extension.

Do not land this and #6356/#4896/#6301 independently; they all rewrite installWindowsEnvironment.

@simon-curtissimon-curtis changed the title perf(windows): reduce startup work and clarify installer phasesperf(windows): speed up shell command discoveryAug 17, 2026
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from 324294b to 96a86d6CompareAugust 17, 2026 16:42
@simon-curtissimon-curtis changed the title perf(windows): speed up shell command discoveryperf(windows): reduce first-start editor discovery timeAug 17, 2026
@simon-curtis

Copy link
Copy Markdown
Author

Scope update: I have reframed this PR around reducing first-start/editor-discovery time on Windows.

After comparing the original branch with current main and the overlapping Windows environment work, the earlier ~2-second startup result was attributable to removing a PowerShell PATH probe. That change affects PATH refresh semantics and conflicts with the other installWindowsEnvironment work, so it no longer belongs in this PR. The installer, packaging, provider-lifecycle, desktop environment, and WSL changes have also been removed.

What remains is the independently useful shared lookup optimization: list each Windows PATH directory once, match PATHEXT candidates case-insensitively, preserve PATH order, and fall back to the existing direct probes when listing fails. I also restored resolveCommandCandidates exactly to main, so this PR no longer changes candidate casing behavior.

A targeted five-run benchmark on this machine's 71-entry PATH and 12 PATHEXT values measured provider discovery at 199 ms → 49 ms and first editor discovery at 1,964 ms → 425 ms. This is a resolver benchmark, not an end-to-end startup claim.

@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 17, 2026
Comment threadpackages/shared/src/shell.ts
@simon-curtis
simon-curtis marked this pull request as ready for review August 17, 2026 17:25
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 7 times, most recently from 99f0376 to d5a0e7aCompareAugust 26, 2026 08:27
@simon-curtis
simon-curtisforce-pushed the perf/windows-startup branch 2 times, most recently from cfb0d3e to 9ecfbc4CompareSeptember 1, 2026 08:32
Simon Curtisand others added 2 commits September 2, 2026 11:03
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This note is part of an automated cleanup pass.

Preserving these details from items reviewed in the cleanup pass.

Carryover from #6221 at 59849f94e2: retain the shell.test.ts listing-failure cases. PermissionDenied, Busy, and Unknown must fall back to direct executable probes, while NotFound and BadResource can skip the directory. Keep mixed-case filenames and PATH order intact. The cross-command directory reuse may be useful if Windows measurements still show repeated listings, but the two PRs' benchmarks use different machines and cannot establish which is faster.

Carry over the slow-discovery evidence from #5050 at 61278bee47. On the reported Windows host with 46 PATH entries and 12 PATHEXT entries, getConfig timed out near 5,000 ms on every call. Concurrent discovery finished in 4,787 ms in that report. Its focused test interrupts one caller during discovery, then requires a later caller to obtain editors. Check cold discovery and caller interruption while retaining bounded expiry and current Linux/WSL file-manager checks. These are reported measurements and a test case to retain, not results rerun here.

Keep the trace evidence from #4778 at dc7f1727a6 in the Windows resolver review. Its author reported 28,577 shell.isExecutableFile spans from 22 command lookups in 13.7 seconds, plus trace files rotating at 10 MB about every 40 seconds. The distinct code change makes isExecutableFile untraced. Main still traces that helper, although its command cache and VCS limits are now bounded. Check first-scan and miss-heavy trace cost with this PR. Do not copy the older unbounded cache or treat the reported timings as rerun results.

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

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simon-curtis@t3dotgg@CDVolvik