Skip to content

fix(server): refresh Windows PATH before provider checks - #8465

Open
SkyVence wants to merge 6 commits into
pingdotgg:mainfrom
SkyVence:fix/windows-path-refresh
Open

fix(server): refresh Windows PATH before provider checks#8465
SkyVence wants to merge 6 commits into
pingdotgg:mainfrom
SkyVence:fix/windows-path-refresh

Conversation

@SkyVence

@SkyVence SkyVence commented Aug 28, 2026

Copy link
Copy Markdown

Problem

Installing a provider CLI on Windows updates the User PATH in the registry, but an already-running T3 server keeps its inherited PATH. Provider instances with custom environment variables also retained a copied startup environment, so Refresh could continue reporting the new CLI as unavailable until T3 restarted.

Verification of the refreshed-PATH lifecycle also found three follow-on failure modes:

  • Claude could report as available after Refresh while an existing session adapter still passed a stale PATH when a custom Claude home was configured.
  • Claude resolved its SDK executable only when the provider instance was created, so a CLI installed later could pass health checks but still fail to start a session.
  • Package-managed update detection for Claude, Codex, and OpenCode was fixed at instance creation, so Refresh could find a newly available CLI while one-click update still targeted the old installer.

Codex, Cursor, Grok, and OpenCode session and text-generation runtimes were audited. They resolve or materialize their executable environment at the session or operation boundary and do not share the Claude adapter failure.

Fixes #6352.

This also reinforces the Windows PATH hydration work discussed in #7360 and #7406.

Solution

  • Read no-profile Windows PATH values from the current User and Machine registry scopes instead of a stale child-process environment.
  • Rehydrate PATH before all-provider, provider-kind, and provider-instance refreshes.
  • Serialize concurrent Windows PATH hydration while preserving concurrent provider checks.
  • Keep inherited values in custom provider environments live for existing instances while preserving explicit overrides.
  • Normalize Windows environment override names case-insensitively.
  • Preserve the live environment when adding CLAUDE_CONFIG_DIR for configured Claude homes.
  • Resolve the Windows Claude SDK executable at each session start instead of caching it at provider creation.
  • Resolve maintenance capabilities lazily for snapshot enrichment and update execution so installer selection follows the current PATH.
  • Inject the host platform and environment into all five provider drivers so environment merging follows Windows and POSIX naming semantics correctly.
  • Cover registry refresh behavior, concurrent refreshes, mixed-case overrides, environment enumeration, Claude session startup, configured Claude homes, lazy maintenance capabilities, and a real Node child process.

Validation

  • vp test run packages/shared/src/shell.test.ts apps/server/src/provider/ProviderInstanceEnvironment.test.ts
    • 32 tests passed
  • vp test run apps/server/src/provider/Drivers/ClaudeHome.test.ts apps/server/src/provider/Layers/ClaudeAdapter.test.ts apps/server/src/provider/makeManagedServerProvider.test.ts apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts
    • 93 tests passed
  • vp test run apps/server/src/provider/Layers/ProviderRegistry.test.ts -t "rehydrates Windows PATH before manual provider refreshes"
    • Focused regression passed
  • vp test run apps/server/src/textGeneration/ClaudeTextGeneration.test.ts -t "runs Claude text generation with the configured CLAUDE_CONFIG_DIR"
    • Focused regression passed
  • vp test run apps/server/src/provider/providerMaintenance.test.ts -t "switches package-managed providers to bun updates"
    • Focused regression passed
  • vp run --filter t3 typecheck
  • Targeted formatting and type-aware lint
    • Formatting passed and lint reported 0 errors
    • One pre-existing no-useless-spread warning remains in an unchanged ClaudeAdapter hunk
  • git diff --check
  • Fresh read-only review found no actionable issues

The complete ProviderRegistry test file still has unrelated Windows-sensitive failures involving POSIX path assumptions and existing cache or scheduling assertions. The complete providerMaintenance test file also has two existing Windows failures because this environment cannot create test symlinks without elevated privileges; the focused maintenance regression passes.

Implemented with GPT-5.6 Sol via the Zed coding agent.

Note

Refresh Windows PATH before provider checks and make provider environments live and platform-aware

  • On Windows, ProviderRegistryLive rehydrates PATH via fixPath (single-permit semaphore) before refreshAll, refresh, and refreshInstance
  • mergeProviderInstanceEnvironment now returns a Proxy that overlays instance overrides on top of the live host environment, with Windows case-insensitive variable normalization
  • ServerProviderShape replaces the static maintenanceCapabilities field with an async getMaintenanceCapabilities Effect; makeManagedServerProvider and ProviderRegistry resolve capabilities on demand per snapshot enrichment
  • All provider drivers (ClaudeDriver, CodexDriver, CursorDriver, GrokDriver, OpenCodeDriver) inject HostProcessPlatform and HostProcessEnvironment and pass current maintenance capabilities to enrichSnapshot
  • ClaudeAdapter defers SDK executable resolution to session start, using platform-aware resolution and file checks against the current environment
  • readEnvironmentFromWindowsShell captures PATH from merged Machine and User scopes when profile loading is disabled
  • Risk: mergeProviderInstanceEnvironment signature changed to require platform and baseEnv; all in-tree callers are updated, but out-of-tree callers of this utility or of ServerProviderShape.maintenanceCapabilities will break

Macroscope summarized 577b35b.

@coderabbitai

coderabbitai Bot commented Aug 28, 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: f3265c62-fb97-4608-90f9-7633251b4101

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

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 28, 2026
Comment thread packages/shared/src/shell.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One convention issue found: the driver create effects now take HostProcessPlatform from the Effect environment but still let mergeProviderInstanceEnvironment fall back to the process.env module global instead of HostProcessEnvironment. See the inline comment for the suggested fix (applies to all five touched drivers).

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/provider/Drivers/CodexDriver.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR changes existing Windows refresh, provider environment, update-detection, and Claude session-start behavior across shared infrastructure and multiple production drivers. Its broad runtime surface and server-wide PATH mutation exceed the scope of a routine isolated bug fix, despite the added regression tests.

You can add or adjust custom eligibility rules. Learn more.

Acquire the host environment through Effect when building provider instances so refreshed PATH values reach every driver without a hidden process.env dependency.
Comment thread apps/server/src/provider/Drivers/ClaudeDriver.ts
Comment thread apps/server/src/provider/Drivers/ClaudeDriver.ts

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Effect Service Conventions — 2 findings

1. Stale ServerProviderShape consumers left behind (apps/server/src/provider/Layers/ProviderRegistry.test.ts, lines 1032 and 1055)

This PR renames ServerProviderShape.maintenanceCapabilities to getMaintenanceCapabilities: Effect<...>, and every other fake instance in this file was migrated — but the two instances in the instances array (satisfies ReadonlyArray<ProviderInstance> at line 1068) still set the removed field, so they no longer satisfy the new shape.

               snapshot: {
-                maintenanceCapabilities: makeManualOnlyProviderMaintenanceCapabilities({
-                  provider: codexDriver,
-                  packageName: null,
-                }),
+                getMaintenanceCapabilities: Effect.succeed(
+                  makeManualOnlyProviderMaintenanceCapabilities({
+                    provider: codexDriver,
+                    packageName: null,
+                  }),
+                ),
                 getSnapshot: Effect.succeed(codexProvider),

(and the same change for the openCodeDriver instance at line 1055).

2. refreshWindowsPath does not pin the host-process references it was built from — see the inline comment on apps/server/src/provider/Layers/ProviderRegistry.ts.

Posted via Macroscope — Effect Service Conventions

Comment on lines +234 to +237
const refreshWindowsPath =
platform === "win32"
? windowsPathRefreshSemaphore.withPermits(1)(
fixPath().pipe(

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.

fixPath() reads both HostProcessPlatform and HostProcessEnvironment from the environment, but only FileSystem/Path are pinned to the values this layer acquired at construction. Because both are Context.References with process.* defaults, the requirement disappears from the type and the effect silently resolves them from whatever context the caller of refresh/refreshInstance runs in. If that context differs from the layer's (no reference provided → default process.env), hydration mutates a different object than the one drivers captured via HostProcessEnvironment, so the PATH refresh never reaches them — while the platform === "win32" gate above still uses the construction-time value.

Consider pinning them alongside FileSystem/Path:

     const platform = yield* HostProcessPlatform;
+    const hostEnvironment = yield* HostProcessEnvironment;
     const windowsPathRefreshSemaphore = yield* Semaphore.make(1);
     const refreshWindowsPath =
       platform === "win32"
         ? windowsPathRefreshSemaphore.withPermits(1)(
             fixPath().pipe(
               Effect.provideService(FileSystem.FileSystem, fileSystem),
               Effect.provideService(Path.Path, path),
+              Effect.provideService(HostProcessPlatform, platform),
+              Effect.provideService(HostProcessEnvironment, hostEnvironment),
             ),
           )
         : Effect.void;

This also needs HostProcessEnvironment added to the existing @t3tools/shared/hostProcess import.

Posted via Macroscope — Effect Service Conventions

@t3dotgg

t3dotgg commented Sep 4, 2026

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.

Preserve the PATH-only evidence from #7362 at head 8e7b59d6. Its desktop test covers User PATH recovery when PowerShell returns a stale process PATH. Its shell tests cover REG_EXPAND_SZ, User/Machine registry reads through reg.exe, and process/profile precedence. Compare the fallback that works without PowerShell and the local cursor-agent directory against this implementation. Keep profile-added paths and live per-instance environments. The unrelated Hornet provider is not part of this carryover. No PATH code was moved by this cleanup.

Carryover from #6356 at d00be9516f: check the explicit %LOCALAPPDATA%\Programs\OpenAI\Codex\bin fallback in knownWindowsCliDirs and its shell.test.ts case. Keep it if that standalone installation is still supported. Preserve the distinction between a fresh Machine/User PATH read and the profile probe's process PATH, including an fnm or custom directory added only by the profile. This records the case without claiming it is already included.

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

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: newly installed provider CLI stays missing until app restart

2 participants